pax_global_header00006660000000000000000000000064126452226600014517gustar00rootroot0000000000000052 comment=471ad3fb0f0f4b332057e3fa7ee277dc780abcc2 openmw-openmw-0.38.0/000077500000000000000000000000001264522266000144175ustar00rootroot00000000000000openmw-openmw-0.38.0/.gitignore000066400000000000000000000011211264522266000164020ustar00rootroot00000000000000## make CMakeFiles */CMakeFiles CMakeCache.txt cmake_install.cmake Makefile makefile build* prebuilt ## doxygen Doxygen ## ides/editors *~ *.kdev4 *.swp *.swo *.kate-swp .cproject .project .settings .directory ## qt-creator CMakeLists.txt.user* ## resources data resources /*.cfg /*.desktop /*.install ## binaries /esmtool /mwiniimport /omwlauncher /openmw /opencs /niftest ## generated objects apps/openmw/config.hpp docs/mainpage.hpp moc_*.cxx *.cxx_parameters *qrc_launcher.cxx *qrc_resources.cxx *__* *ui_datafilespage.h *ui_graphicspage.h *ui_mainwindow.h *ui_playpage.h *.[ao] *.so openmw-openmw-0.38.0/.travis.yml000066400000000000000000000037071264522266000165370ustar00rootroot00000000000000os: - linux # - osx language: cpp branches: only: - master - coverity_scan - /openmw-.*$/ env: global: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE=" addons: coverity_scan: project: name: "OpenMW/openmw" description: "" notification_email: scrawl@baseoftrash.de build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE" build_command: "make" branch_pattern: coverity_scan matrix: include: - os: linux env: ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 " compiler: clang allow_failures: - env: ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 " before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_install.linux.sh; fi - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_install.osx.sh; fi before_script: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_script.linux.sh; fi - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi script: - cd ./build - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j2; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi notifications: recipients: - corrmage+travis-ci@gmail.com email: on_success: change on_failure: always irc: channels: - "chat.freenode.net#openmw" on_success: change on_failure: always use_notice: true openmw-openmw-0.38.0/AUTHORS.md000066400000000000000000000122521264522266000160700ustar00rootroot00000000000000Contributors ============ The OpenMW project was started in 2008 by Nicolay Korslund. In the course of years many people have contributed to the project. If you feel your name is missing from this list, please notify a developer. Programmers ----------- Marc Zinnschlag (Zini) - Lead Programmer/Project Manager Adam Hogan (aurix) Aesylwinn Aleksandar Jovanov Alex Haddad (rainChu) Alex McKibben (WeirdSexy) Alexander Nadeau (wareya) Alexander Olofsson (Ace) Artem Kotsynyak (greye) artemutin Arthur Moore (EmperorArthur) athile Bret Curtis (psi29a) Britt Mathis (galdor557) cc9cii Chris Boyce (slothlife) Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) darkf Dieho Dmitry Shkurskiy (endorph) Douglas Diniz (Dgdiniz) Douglas Mencken (dougmencken) dreamer-dead David Teviotdale (dteviot) Edmondo Tommasina (edmondo) Eduard Cot (trombonecot) Eli2 Emanuel Guével (potatoesmaster) eroen escondida Evgeniy Mineev (sandstranger) Fil Krynicki (filkry) Gašper Sedej gugus/gus Hallfaer Tuilinn Internecine Jacob Essex (Yacoby) Jannik Heller (scrawl) Jason Hooks (jhooks) jeaye Jeffrey Haines (Jyby) Jengerer Jiří Kuneš (kunesj) Joe Wilkerson (neuralroberts) Joel Graff (graffy) John Blomberg (fstp) Jordan Ayers Jordan Milne Julien Voisin (jvoisin/ap0) Karl-Felix Glatzer (k1ll) Kevin Poitra (PuppyKevin) Koncord Lars Söderberg (Lazaroth) lazydev Leon Saunders (emoose) Lukasz Gromanowski (lgro) Manuel Edelmann (vorenon) Marc Bouvier (CramitDeFrog) Marcin Hulist (Gohan) Mark Siewert (mark76) Marco Melletti (mellotanica) Marco Schulze Mateusz Kołaczek (PL_kolek) Mateusz Malisz (malice) megaton Michael Hogan (Xethik) Michael Mc Donnell Michael Papageorgiou (werdanith) Michał Bień (Glorf) Miroslav Puda (pakanek) MiroslavR naclander Narmo Nathan Jeffords (blunted2night) NeveHanter Nikolay Kasyanov (corristo) nobrakal Nolan Poe (nopoe) Paul Cercueil (pcercuei) Paul McElroy (Greendogo) Pieter van der Kloet (pvdk) pkubik Radu-Marius Popovici (rpopovici) rdimesio riothamus Robert MacGregor (Ragora) Rohit Nirmal Roman Melnik (Kromgart) Roman Proskuryakov (humbug) Sandy Carter (bwrsandman) Scott Howard Sebastian Wick (swick) Sergey Shambir sir_herrbatka smbas Stefan Galowicz (bogglez) Stanislav Bobrov (Jiub) Sylvain Thesnieres (Garvek) terrorfisch Thomas Luppi (Digmaster) Tom Mason (wheybags) Torben Leif Carrington (TorbenC) viadanna Vincent Heuken vocollapse zelurker Manual ------ Bodillium Cramal sir_herrbatka Packagers --------- Alexander Olofsson (Ace) - Windows Bret Curtis (psi29a) - Ubuntu Linux Edmondo Tommasina (edmondo) - Gentoo Linux Julian Ospald (hasufell) - Gentoo Linux Karl-Felix Glatzer (k1ll) - Linux Binaries Kenny Armstrong (artorius) - Fedora Linux Nikolay Kasyanov (corristo) - Mac OS X Sandy Carter (bwrsandman) - Arch Linux Public Relations and Translations --------------------------------- Alex McKibben (WeirdSexy) - Podcaster Artem Kotsynyak (greye) - Russian News Writer Jim Clauwaert (Zedd) - Public Outreach Julien Voisin (jvoisin/ap0) - French News Writer Tom Koenderink (Okulo) - English News Writer Lukasz Gromanowski (lgro) - English News Writer Mickey Lyle (raevol) - Release Manager Pithorn - Chinese News Writer sir_herrbatka - Polish News Writer Dawid Lakomy (Vedyimyn) - Polish News Writer Website ------- Lukasz Gromanowski (Lgro) - Website Administrator Ryan Sardonic (Wry) - Wiki Editor sir_herrbatka - Forum Administrator Formula Research ---------------- Hrnchamd Epsilon fragonard Greendogo HiPhish modred11 Myckel natirips Sadler Artwork ------- Necrod - OpenMW Logo Mickey Lyle (raevol) - Wordpress Theme Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel - OpenMW Editor Icons Inactive Contributors --------------------- Ardekantur Armin Preiml Berulacks Carl Maxwell Diggory Hardy Dmitry Marakasov (AMDmi3) ElderTroll guidoj Jan-Peter Nilsson (peppe) Jan Borsodi Josua Grawitter juanmnzsk8 Kingpix Lordrea Michal Sciubidlo Nicolay Korslund Nekochan pchan3 penguinroad psi29a sergoz spyboot Star-Demon Thoronador Yuri Krupenin Additional Credits ------------------ In this section we would like to thank people not part of OpenMW for their work. Thanks to Maxim Nikolaev, for allowing us to use his excellent Morrowind fan-art on our website and in other places. Thanks to DokterDume, for kindly providing us with the Moon and Star logo, used as the application icon and project logo. Thanks to Kevin Ryan, for creating the icon used for the Data Files tab of the OpenMW Launcher. Thanks to DejaVu team, for their DejaVuLGCSansMono fontface, see DejaVu Font License.txt for their license terms. openmw-openmw-0.38.0/CHANGELOG.md000066400000000000000000003423651264522266000162450ustar00rootroot000000000000000.38.0 ------ Bug #1699: Guard will continuously run into mudcrab Bug #1934: Saw in Dome of Kasia doesnt harm the player Bug #1962: Rat floats when killed near the door Bug #1963: Kwama eggsacks pulse too fast Bug #2198: NPC voice sound source should be placed at their head Bug #2210: OpenMW installation wizard crashes... Bug #2211: Editor: handle DELE subrecord at the end of a record Bug #2413: ESM error Unknown subrecord in Grandmaster of Hlaalu Bug #2537: Bloodmoon quest Ristaag: Sattir not consistently dying, plot fails to advance; same with Grerid Bug #2697: "The Swimmer" moves away after leading you to underwater cave Bug #2724: Loading previous save duplicates containers and harvestables Bug #2769: Inventory doll - Cursor not respecting order of clothes Bug #2865: Scripts silently fail when moving NPCs between cells. Bug #2873: Starting a new game leads to CTD / Fatal Error Bug #2918: Editor: it's not possible to create an omwaddon containing a dot in the file name Bug #2933: Dialog box can't disable a npc if it is in another cell. (Rescue Madura Seran). Bug #2942: atronach sign behavior (spell absorption) changes when trying to receive a blessing at "shrine of tribunal" Bug #2952: Enchantment Merchant Items reshuffled EVERY time 'barter' is clicked Bug #2961: ESM Error: Unknown subrecord if Deus Ex Machina mod is loaded Bug #2972: Resurrecting the player via console does not work when health was 0 Bug #2986: Projectile weapons work underwater Bug #2988: "Expected subrecord" bugs showing up. Bug #2991: Can't use keywords in strings for MessageBox Bug #2993: Tribunal:The Shrine of the Dead – Urvel Dulni can't stop to follow the player. Bug #3008: NIFFile Error while loading meshes with a NiLODNode Bug #3010: Engine: items should sink to the ground when dropped under water Bug #3011: NIFFile Error while loading meshes with a NiPointLight Bug #3016: Engine: something wrong with scripting - crash / fatal error Bug #3020: Editor: verify does not check if given "item ID" (as content) for a "container" exists Bug #3026: [MOD: Julan Ashlander Companion] Dialogue not triggering correctly Bug #3028: Tooltips for Health, Magicka and Fatigue show in Options menu even when bars aren't visible Bug #3034: Item count check dialogue option doesn't work (Guards accept gold even if you don't have enough) Bug #3036: Owned tooltip color affects spell tooltips incorrrectly Bug #3037: Fatal error loading old ES_Landscape.esp in Store::search Bug #3038: Player sounds come from underneath Bug #3040: Execution of script failed: There is a message box already Bug #3047: [MOD: Julan Ashlander Companion] Scripts KS_Bedscript or KS_JulanNight not working as intended Bug #3048: Fatal Error Bug #3051: High field of view results in first person rendering glitches Bug #3053: Crash on new game at character class selection Bug #3058: Physiched sleeves aren't rendered correctly. Bug #3060: NPCs use wrong landing sound Bug #3062: Mod support regression: Andromeda's fast travel. Bug #3063: Missing Journal Textures without Tribunal and Bloodmoon installed Bug #3077: repeated aifollow causes the distance to stack Bug #3078: Creature Dialogues not showing when certain Function/Conditions are required. Bug #3082: Crash when entering Holamayan Monastery with mesh replacer installed Bug #3086: Party at Boro's House – Creature with Class don't talk under OpenMW Bug #3089: Dreamers spawn too soon Bug #3100: Certain controls erroneously work as a werewolf Bug #3102: Multiple unique soultrap spell sources clone souls. Bug #3105: Summoned creatures and objects disappear at midnight Bug #3112: gamecontrollerdb file creation with wrong extension Bug #3116: Dialogue Function "Same Race" is avoided Bug #3117: Dialogue Bug: Choice conditions are tested when not in a choice Bug #3118: Body Parts are not rendered when used in a pose. Bug #3122: NPC direction is reversed during sneak awareness check Feature #776: Sound effects from one direction don't necessarily affect both speakers in stereo Feature #858: Different fov settings for hands and the game world Feature #1176: Handle movement of objects between cells Feature #2507: Editor: choosing colors for syntax highlighting Feature #2867: Editor: hide script error list when there are no errors Feature #2885: Accept a file format other than nif Feature #2982: player->SetDelete 1 results in: PC can't move, menu can be opened Feature #2996: Editor: make it possible to preset the height of the script check area in a script view Feature #3014: Editor: Tooltips in 3D scene Feature #3064: Werewolf field of view Feature #3074: Quicksave indicator Task #287: const version of Ptr Task #2542: Editor: redo user settings system 0.37.0 ------ Bug #385: Light emitting objects have a too short distance of activation Bug #455: Animation doesn't resize creature's bounding box Bug #602: Only collision model is updated when modifying objects trough console Bug #639: Sky horizon at nighttime Bug #672: incorrect trajectory of the moons Bug #814: incorrect NPC width Bug #827: Inaccurate raycasting for dead actors Bug #996: Can see underwater clearly when at right height/angle Bug #1317: Erene Llenim in Seyda Neen does not walk around Bug #1330: Cliff racers fail to hit the player Bug #1366: Combat AI can't aim down (in order to hit small creatures) Bug #1511: View distance while under water is much too short Bug #1563: Terrain positioned incorrectly and appears to vibrate in far-out cells Bug #1612: First person models clip through walls Bug #1647: Crash switching from full screen to windows mode - D3D9 Bug #1650: No textures with directx on windows Bug #1730: Scripts names starting with digit(s) fail to compile Bug #1738: Socucius Ergalla's greetings are doubled during the tutorial Bug #1784: First person weapons always in the same position Bug #1813: Underwater flora lighting up entire area. Bug #1871: Handle controller extrapolation flags Bug #1921: Footstep frequency and velocity do not immediately update when speed attribute changes Bug #2001: OpenMW crashes on start with OpenGL 1.4 drivers Bug #2014: Antialiasing setting does nothing on Linux Bug #2037: Some enemies attack the air when spotting the player Bug #2052: NIF rotation matrices including scales are not supported Bug #2062: Crank in Old Mournhold: Forgotten Sewer turns about the wrong axis Bug #2111: Raindrops in front of fire look wrong Bug #2140: [OpenGL] Water effects, flames and parts of creatures solid black when observed through brazier flame Bug #2147: Trueflame and Hopesfire flame effects not properly aligned with blade Bug #2148: Verminous fabricants have little coloured box beneath their feet Bug #2149: Sparks in Clockwork City should bounce off the floor Bug #2151: Clockwork City dicer trap doesn't activate when you're too close Bug #2186: Mini map contains scrambled pixels that cause the mini map to flicker Bug #2187: NIF file with more than 255 NiBillboardNodes does not load Bug #2191: Editor: Crash when trying to view cell in render view in OpenCS Bug #2270: Objects flicker transparently Bug #2280: Latest 32bit windows build of openmw runns out of vram Bug #2281: NPCs don't scream when they die Bug #2286: Jumping animation restarts when equipping mid-air Bug #2287: Weapon idle animation stops when turning Bug #2355: Light spell doesn't work in 1st person view Bug #2362: Lantern glas opaque to flame effect from certain viewing angles Bug #2364: Light spells are not as bright as in Morrowind Bug #2383: Remove the alpha testing override list Bug #2436: Crash on entering cell "Tower of Tel Fyr, Hall of Fyr" Bug #2457: Player followers should not report crimes Bug #2458: crash in some fighting situations Bug #2464: Hiding an emitter node should make that emitter stop firing particles Bug #2466: Can't load a save created with OpenMW-0.35.0-win64 Bug #2468: music from title screen continues after loading savegame Bug #2494: Map not consistent between saves Bug #2504: Dialog scroll should always start at the top Bug #2506: Editor: Undo/Redo shortcuts do not work in script editor Bug #2513: Mannequins in mods appear as dead bodies Bug #2524: Editor: TopicInfo "custom" condition section is missing Bug #2540: Editor: search and verification result table can not be sorted by clicking on the column names Bug #2543: Editor: there is a problem with spell effects Bug #2544: Editor fails to save NPC information correctly. Bug #2545: Editor: delete record in Objects (referenceables) table messes up data Bug #2546: Editor: race base attributes and skill boni are not displayed, thus not editable Bug #2547: Editor: some NPC data is not displayed, thus not editable Bug #2551: Editor: missing data in cell definition Bug #2553: Editor: value filter does not work for float values Bug #2555: Editor: undo leaves the record status as Modified Bug #2559: Make Detect Enchantment marks appear on top of the player arrow Bug #2563: position consoling npc doesn't work without cell reload Bug #2564: Editor: Closing a subview from code does not clean up properly and will lead to crash on opening the next subview Bug #2568: Editor: Setting default window size is ignored Bug #2569: Editor: saving from an esp to omwaddon file results in data loss for TopicInfo Bug #2575: Editor: Deleted record (with Added (ModifiedOnly) status) remains in the Dialog SubView Bug #2576: Editor: Editor doesn't scroll to a newly opened subview, when ScrollBar Only mode is active Bug #2578: Editor: changing Level or Reputation of an NPC crashes the editor Bug #2579: Editor: filters not updated when adding or cloning records Bug #2580: Editor: omwaddon makes OpenMW crash Bug #2581: Editor: focus problems in edit subviews single- and multiline input fields Bug #2582: Editor: object verifier should check for non-existing scripts being referenced Bug #2583: Editor: applying filter to TopicInfo on mods that have added dialouge makes the Editor crash Bug #2586: Editor: some dialogue only editable items do not refresh after undo Bug #2588: Editor: Cancel button exits program Bug #2589: Editor: Regions table - mapcolor does not change correctly Bug #2591: Placeatme - spurious 5th parameter raises error Bug #2593: COC command prints multiple times when GUI is hidden Bug #2598: Editor: scene view of instances has to be zoomed out to displaying something - center camera instance please Bug #2607: water behind an invisible NPC becomes invisible as well Bug #2611: Editor: Sort problem in Objects table when few nested rows are added Bug #2621: crash when a creature has no model Bug #2624: Editor: missing columns in tables Bug #2627: Character sheet doesn't properly update when backing out of CharGen Bug #2642: Editor: endif without if - is not reported as error when "verify" was executed Bug #2644: Editor: rebuild the list of available content files when opening the open/new dialogues Bug #2656: OpenMW & OpenMW-CS: setting "Flies" flag for ghosts has no effect Bug #2659: OpenMW & OpenMW-CS: savegame load fail due to script attached to NPCs Bug #2668: Editor: reputation value in the input field is not stored Bug #2696: Horkers use land idle animations under water Bug #2705: Editor: Sort by Record Type (Objects table) is incorrect Bug #2711: Map notes on an exterior cell that shows up with a map marker on the world map do not show up in the tooltip for that cell's marker on the world map Bug #2714: Editor: Can't reorder rows with the same topic in different letter case Bug #2720: Head tracking for creatures not implemented Bug #2722: Alchemy should only include effects shared by at least 2 ingredients Bug #2723: "ori" console command is not working Bug #2726: Ashlanders in front of Ghostgate start wandering around Bug #2727: ESM writer does not handle encoding when saving the TES3 header Bug #2728: Editor: Incorrect position of an added row in Info tables Bug #2731: Editor: Deleting a record triggers a Qt warning Bug #2733: Editor: Undo doesn't restore the Modified status of a record when a nested data is changed Bug #2734: Editor: The Search doesn't work Bug #2738: Additive moon blending Bug #2746: NIF node names should be case insensitive Bug #2752: Fog depth/density not handled correctly Bug #2753: Editor: line edit in dialogue subview tables shows after a single click Bug #2755: Combat AI changes target too frequently Bug #2761: Can't attack during block animations Bug #2764: Player doesn't raise arm in 3rd person for weathertype 9 Bug #2768: Current screen resolution not selected in options when starting OpenMW Bug #2773: Editor: Deleted scripts are editable Bug #2776: ordinators still think I'm wearing their helm even though Khajiit and argonians can't Bug #2779: Slider bars continue to move if you don't release mouse button Bug #2781: sleep interruption is a little off (is this an added feature?) Bug #2782: erroneously able to ready weapon/magic (+sheathe weapon/magic) while paralyzed Bug #2785: Editor: Incorrect GMSTs for newly created omwgame files Bug #2786: Kwama Queen head is inverted under OpenMW Bug #2788: additem and removeitem incorrect gold behavior Bug #2790: --start doesn't trace down Bug #2791: Editor: Listed attributes and skill should not be based on number of NPC objects. Bug #2792: glitched merchantile/infinite free items Bug #2794: Need to ignore quotes in names of script function Bug #2797: Editor: Crash when removing the first row in a nested table Bug #2800: Show an error message when S3TC support is missing Bug #2811: Targetted Open spell effect persists. Bug #2819: Editor: bodypart's race filter not displayed correctly Bug #2820: Editor: table sorting is inverted Bug #2821: Editor: undo/redo command labels are incorrect Bug #2826: locking beds that have been locked via magic psuedo-freezes the game Bug #2830: Script compiler does not accept IDs as instruction/functions arguments if the ID is also a keyword Bug #2832: Cell names are not localized on the world map Bug #2833: [cosmetic] Players swimming at water's surface are slightly too low. Bug #2840: Save/load menu is not entirely localized Bug #2853: [exploit/bug] disintegrate weapon incorrectly applying to lockpicks, probes. creates unbreakable lockpicks Bug #2855: Mouse wheel in journal is not disabled by "Options" panel. Bug #2856: Heart of Lorkhan doesn't visually respond to attacks Bug #2863: Inventory highlights wrong category after load Bug #2864: Illuminated Order 1.0c Bug – The teleport amulet is not placed in the PC inventory. Bug #2866: Editor: use checkbox instead of combobox for boolean values Bug #2875: special cases of fSleepRandMod not behaving properly. Bug #2878: Editor: Verify reports "creature has non-positive level" but there is no level setting Bug #2879: Editor: entered value of field "Buys *" is not saved for a creature Bug #2880: OpenMW & OpenMW-CS: having a scale value of 0.000 makes the game laggy Bug #2882: Freeze when entering cell "Guild of Fighters (Ald'ruhn)" after dropping some items inside Bug #2883: game not playable if mod providing a spell is removed but the list of known spells still contains it Bug #2884: NPC chats about wrong player race Bug #2886: Adding custom races breaks existing numbering of PcRace Bug #2888: Editor: value entered in "AI Wander Idle" is not kept Bug #2889: Editor: creatures made with the CS (not cloned) are always dead Bug #2890: Editor: can't make NPC say a specific "Hello" voice-dialouge Bug #2893: Editor: making a creature use textual dialogue doesn't work. Bug #2901: Editor: gold for trading can not be set for creatures Bug #2907: looking from uderwater part of the PC that is below the surface looks like it would be above the water Bug #2914: Magicka not recalculated on character generation Bug #2915: When paralyzed, you can still enter and exit sneak Bug #2917: chameleon does not work for creatures Bug #2927: Editor: in the automatic script checker local variable caches are not invalidated/updated on modifications of other scripts Bug #2930: Editor: AIWander Idle can not be set for a creature Bug #2932: Editor: you can add rows to "Creature Attack" but you can not enter values Bug #2938: Editor: Can't add a start script. Bug #2944: Spell chance for power to show as 0 on hud when used Bug #2953: Editor: rightclick in an empty place in the menu bar shows an unnamed checkbox Bug #2956: Editor: freezes while editing Filter Bug #2959: space character in field enchantment (of an amulet) prevents rendering of surroundings Bug #2962: OpenMW: Assertion `it != invStore.end()' failed Bug #2964: Recursive script execution can corrupt script runtime data Bug #2973: Editor: placing a chest in the game world and activating it heavily blurrs the character portrait Bug #2978: Editor: Cannot edit alchemy ingredient properties Bug #2980: Editor: Attribute and Skill can be selected for spells that do not require these parameters, leading to non-functional spells Bug #2990: Compiling a script with warning mode 2 and enabled error downgrading leads to infinite recursion Bug #2992: [Mod: Great House Dagoth] Killing Dagoth Gares freezes the game Bug #3007: PlaceItem takes radians instead of degrees + angle reliability Feature #706: Editor: Script Editor enhancements Feature #872: Editor: Colour values in tables Feature #880: Editor: ID auto-complete Feature #928: Editor: Partial sorting in info tables Feature #942: Editor: Dialogue for editing/viewing content file meta information Feature #1057: NiStencilProperty Feature #1278: Editor: Mouse picking in worldspace widget Feature #1280: Editor: Cell border arrows Feature #1401: Editor: Cloning enhancements Feature #1463: Editor: Fine grained configuration of extended revert/delete commands Feature #1591: Editor: Make fields in creation bar drop targets where applicable Feature #1998: Editor: Magic effect record verifier Feature #1999: Editor Sound Gen record verifier Feature #2000: Editor: Pathgrid record verifier Feature #2528: Game Time Tracker Feature #2534: Editor: global search does not auomatically focus the search input field Feature #2535: OpenMW: allow comments in openmw.cfg Feature #2541: Editor: provide a go to the very bottom button for TopicInfo and JournalInfo Feature #2549: Editor: add a horizontal slider to scroll between opened tables Feature #2558: Editor: provide a shortcut for closing the subview that has the focus Feature #2565: Editor: add context menu for dialogue sub view fields with an item matching "Edit 'x'" from the table subview context menu Feature #2585: Editor: Ignore mouse wheel input for numeric values unless the respective widget has the focus Feature #2620: Editor: make the verify-view refreshable Feature #2622: Editor: Make double click behaviour in result tables configurable (see ID tables) Feature #2717: Editor: Add severity column to report tables Feature #2729: Editor: Various dialogue button bar improvements Feature #2739: Profiling overlay Feature #2740: Resource manager optimizations Feature #2741: Make NIF files into proper resources Feature #2742: Use the skinning data in NIF files as-is Feature #2743: Small feature culling Feature #2744: Configurable near clip distance Feature #2745: GUI scaling option Feature #2747: Support anonymous textures Feature #2749: Loading screen optimizations Feature #2751: Character preview optimization Feature #2804: Editor: Merge Tool Feature #2818: Editor: allow copying a record ID to the clipboard Feature #2946: Editor: add script line number in results of search Feature #2963: Editor: Mouse button bindings in 3D scene Feature #2983: Sun Glare fader Feature #2999: Scaling of journal and books Task #2665: Support building with Qt5 Task #2725: Editor: Remove Display_YesNo Task #2730: Replace hardcoded column numbers in SimpleDialogueSubView/DialogueSubView Task #2750: Bullet shape instancing optimization Task #2793: Replace grid size setting with half grid size setting Task #3003: Support FFMPEG 2.9 (Debian request) 0.36.1 ------ Bug #2590: Start scripts not added correctly 0.36.0 ------ Bug #923: Editor: Operations-Multithreading is broken Bug #1317: Erene Llenim in Seyda Neen does not walk around Bug #1405: Water rendering glitch near Seyda Neen lighthouse Bug #1621: "Error Detecting Morrowind Installation" in the default directory Bug #2216: Creating a clone of the player stops you moving. Bug #2387: Casting bound weapon spell doesn't switch to "ready weapon" mode Bug #2407: Default to (0, 0) when "unknown cell" is encountered. Bug #2411: enchanted item charges don't update/refresh if spell list window is pinned open Bug #2428: Editor: cloning / creating new container class results in invalid omwaddon file - openmw-0.35 Bug #2429: Editor - cloning omits some values or sets different values than the original has Bug #2430: NPC with negative fatigue don't fall (LGNPC Vivec, Foreign Quarter v2.21) Bug #2432: Error on startup with Uvirith's Legacy enabled Bug #2435: Editor: changed entries in the objects window are not shown as such Bug #2437: Editor: changing an entry of a container/NPC/clothing/ingredient/globals will not be saved in the omwaddon file Bug #2447: Editor doesn't save terrain information Bug #2451: Editor not listing files with accented characters Bug #2453: Chargen: sex, race and hair sliders not initialized properly Bug #2459: Minor terrain clipping through statics due to difference in triangle alignment Bug #2461: Invisible sound mark has collision in Sandus Ancestral Tomb Bug #2465: tainted gold stack Bug #2475: cumulative stacks of 100 point fortify skill speechcraft boosts do not apply correctly Bug #2498: Editor: crash when issuing undo command after the table subview is closed Bug #2500: Editor: object table - can't undo delete record Bug #2518: OpenMW detect spell returns false positives Bug #2521: NPCs don't react to stealing when inventory menu is open. Bug #2525: Can't click on red dialogue choice [rise of house telvanni][60fffec] Bug #2530: GetSpellEffects not working as in vanilla Bug #2557: Crash on first launch after choosing "Run installation wizard" Feature #139: Editor: Global Search & Replace Feature #1219: Editor: Add dialogue mode only columns Feature #2024: Hotkey for hand to hand (i.e. unequip any weapon) Feature #2119: "Always Sneak" key bind Feature #2262: Editor: Handle moved instances Feature #2425: Editor: Add start script table Feature #2426: Editor: start script record verifier Feature #2480: Launcher: Multiselect entries in the Data Files list Feature #2505: Editor: optionally show a line number column in the script editor Feature #2512: Editor: Offer use of monospace fonts in the script editor as an option Feature #2514: Editor: focus on ID input field on clone/add Feature #2519: it is not possible to change icons that appear on the map after casting the Detect spells Task #2460: OS X: Use Application Support directory as user data path Task #2516: Editor: Change References / Referenceables terminology 0.35.1 ------ Bug #781: incorrect trajectory of the sun Bug #1079: Wrong starting position in "Character Stuff Wonderland" Bug #1443: Repetitive taking of a stolen object is repetitively considered as a crime Bug #1533: Divine Intervention goes to the wrong place. Bug #1714: No visual indicator for time passed during training Bug #1916: Telekinesis does not allow safe opening of traps Bug #2227: Editor: addon file name inconsistency Bug #2271: Player can melee enemies from water with impunity Bug #2275: Objects with bigger scale move further using Move script Bug #2285: Aryon's Dominator enchantment does not work properly Bug #2290: No punishment for stealing gold from owned containers Bug #2328: Launcher does not respond to Ctrl+C Bug #2334: Drag-and-drop on a content file in the launcher creates duplicate items Bug #2338: Arrows reclaimed from corpses do not stack sometimes Bug #2344: Launcher - Settings importer running correctly? Bug #2346: Launcher - Importing plugins into content list screws up the load order Bug #2348: Mod: H.E.L.L.U.V.A. Handy Holdables does not appear in the content list Bug #2353: Detect Animal detects dead creatures Bug #2354: Cmake does not respect LIB_SUFFIX Bug #2356: Active magic set inactive when switching magic items Bug #2361: ERROR: ESM Error: Previous record contains unread bytes Bug #2382: Switching spells with "next spell" or "previous spell" while holding shift promps delete spell dialog Bug #2388: Regression: Can't toggle map on/off Bug #2392: MOD Shrines - Restore Health and Cancel Options adds 100 health points Bug #2394: List of Data Files tab in openmw-laucher needs to show all content files. Bug #2402: Editor: skills saved incorrectly Bug #2408: Equipping a constant effect Restore Health/Magicka/Fatigue item will permanently boost the stat it's restoring Bug #2415: It is now possible to fall off the prison ship into the water when starting a new game Bug #2419: MOD MCA crash to desktop Bug #2420: Game crashes when character enters a certain area Bug #2421: infinite loop when using cycle weapon without having a weapon Feature #2221: Cannot dress dead NPCs Feature #2349: Check CMake sets correct MSVC compiler settings for release build. Feature #2397: Set default values for global mandatory records. Feature #2412: Basic joystick support 0.35.0 ------ Bug #244: Clipping/static in relation to the ghostgate/fence sound. Bug #531: Missing transparent menu items Bug #811: Content Lists in openmw.cfg are overwritten Bug #925: OpenCS doesn't launch because it thinks its already started Bug #969: Water shader strange behaviour on AMD card Bug #1049: Partially highlighted word in dialogue may cause incorrect line break Bug #1069: omwlauncher.exe crashes due to file lock Bug #1192: It is possible to jump on top of hostile creatures in combat Bug #1342: Loud ambient sounds Bug #1431: Creatures can climb the player Bug #1605: Guard in CharGen doesn't turn around to face you when reaching stairs Bug #1624: Moon edges don't transition properly Bug #1634: Items dropped by PC have collision Bug #1637: Weird NPC behaviour in Vivec, Hlaalu Ancestral Vaults? Bug #1638: Cannot climb staircases Bug #1648: Enchanted equipment badly handled at game reload Bug #1663: Crash when casting spell at enemy near you Bug #1683: Scale doesn't apply to animated collision nodes Bug #1702: Active enchanted item forgotten Bug #1730: Scripts names starting with digit(s) fail to compile Bug #1743: Moons are transparent Bug #1745: Shadows crash: Assertion `mEffects.empty()' failed. Bug #1785: Can't equip two-handed weapon and shield Bug #1809: Player falls too easily Bug #1825: Sword of Perithia can´t run in OpenMW Bug #1899: The launcher resets any alterations you´ve made in the mod list order, Bug #1964: Idle voices/dialogs not triggered correctly Bug #1980: Please, change default click behavior in OpenMW Launchers Data Files list Bug #1984: Vampire corpses standing up when looting the first item Bug #1985: Calm spell does nothing Bug #1986: Spell name lights up on mouseover but spell cost does not Bug #1989: Tooltip still shown when menu toggled off Bug #2010: Raindrops Displayed While Underwater Bug #2023: Walking into plants causes massive framedrop Bug #2031: [MOD: Shrines - Restore Health and Cancel Options]: Restore health option doesn't work Bug #2039: Lake Fjalding pillar of fire not rendered Bug #2040: AI_follow should stop further from the target Bug #2076: Slaughterfish AI Bug #2077: Direction of long jump can be changed much more than it is possible in vanilla Bug #2078: error during rendering: Object '' not found (const) Bug #2105: Lockpicking causes screen sync glitch Bug #2113: [MOD: Julan Ashlander Companion] Julan does not act correctly within the Ghostfence. Bug #2123: Window glow mod: Collision issues Bug #2133: Missing collision for bridges in Balmora when using Morrowind Rebirth 2.81 Bug #2135: Casting a summon spell while the summon is active does not reset the summon. Bug #2144: Changing equipment will unequip drawn arrows/bolts Bug #2169: Yellow on faces when using opengl renderer and mods from overhaul on windows Bug #2175: Pathgrid mods do not overwrite the existing pathgrid Bug #2176: Morrowind -Russian localization end add-on ChaosHeart. Error in framelistener;object ;frenzying toush; not found Bug #2181: Mod Morrowind crafting merchants die. Bug #2182: mods changing skill progression double the bonus for class specialization Bug #2183: Editor: Skills "use value" only allows integer between 0 and 99 Bug #2184: Animated Morrowind Expanded produces an error on Open MW Launch Bug #2185: Conditional Operator formats Bug #2193: Quest: Gateway Ghost Bug #2194: Cannot summon multiples of the same creature Bug #2195: Pathgrid in the (0,0) exterior cell not loaded Bug #2200: Outdoor NPCs can stray away and keep walking into a wall Bug #2201: Creatures do not receive fall damage Bug #2202: The enchantment the item can hold is calculated incorrectly Bug #2203: Having the mod Living Cities of Vvardenfall running causes the game world to fail to load after leaving the prison ship Bug #2204: Abot's Water Life - Book rendered incorrectly Bug #2205: sound_waterfall script no longer compiles Bug #2206: Dialogue script fails to compile (extra .) Bug #2207: Script using – instead of - character does not compile Bug #2208: Failing dialogue scripts in french Morrowind.esm Bug #2214: LGNPC Vivec Redoran 1.62 and The King Rat (Size and inventory Issues) Bug #2215: Beast races can use enchanted boots Bug #2218: Incorrect names body parts in 3D models for open helmet with skinning Bug #2219: Orcs in Ghorak Manor in Caldera don't attack if you pick their pockets. Bug #2220: Chargen race preview head incorrect orientation Bug #2223: Reseting rock falling animation Bug #2224: Fortify Attribute effects do not stack when Spellmaking. Bug #2226: OpenCS pseudo-crash Bug #2230: segfaulting when entering Ald'ruhn with a specific mod: "fermeture la nuit" (closed by night) Bug #2233: Area effect spells on touch do not have the area effect Bug #2234: Dwarven Crossbow clips through the ground when dropped Bug #2235: class SettingsBase<> reverses the order of entries with multiple keys. Bug #2236: Weird two handed longsword + torch interaction Bug #2237: Shooting arrows while sneaking do not agro Bug #2238: Bipedal creatures not using weapons are not handled properly Bug #2245: Incorrect topic highlighting in HT_SpyBaladas quest Bug #2252: Tab completion incomplete for places using COC from the console. Bug #2255: Camera reverts to first person on load Bug #2259: enhancement: the save/load progress bar is not very progressive Bug #2263: TogglePOV can not be bound to Alt key Bug #2267: dialogue disabling via mod Bug #2268: Highlighting Files with load order problems in Data Files tab of Launcher Bug #2276: [Mod]ShotN issues with Karthwasten Bug #2283: Count argument for PlaceAt functions not working Bug #2284: Local map notes should be visible on door marker leading to the cell with the note Bug #2293: There is a graphical glitch at the end of the spell's animation in 3rd Person (looking over the shoulder) view Bug #2294: When using Skyrim UI Overhaul, the tops of pinnable menus are invisible Bug #2302: Random leveled items repeat way too often in a single dungeon Bug #2306: Enchanted arrows should not be retrievable from corpses Bug #2308: No sound effect when drawing the next throwing knife Bug #2309: Guards chase see the player character even if they're invisible Bug #2319: Inverted controls and other issues after becoming a vampire Bug #2324: Spells cast when crossing cell border are imprinted on the local map Bug #2330: Actors with Drain Health effect retain health after dying Bug #2331: tgm (god mode) won't allow the player to cast spells if the player doesn't have enough mana Bug #2332: Error in framelistener: Need a skeleton to attach the arrow to Feature #114: ess-Importer Feature #504: Editor: Delete selected rows from result windows Feature #1024: Addition of remaining equipping hotkeys Feature #1067: Handle NIF interpolation type 4 (XYZ_ROTATION_KEY) Feature #1125: AI fast-forward Feature #1228: Drowning while knocked out Feature #1325: Editor: Opening window and User Settings window cleanup Feature #1537: Ability to change the grid size from 3x3 to 5x5 (or more with good pc) Feature #1546: Leveled list script functions Feature #1659: Test dialogue scripts in --script-all Feature #1720: NPC lookAt controller Feature #2178: Load initial particle system state from NIF files Feature #2197: Editor: When clicking on a script error in the report window set cursor in script editor to the respective line/column Feature #2261: Warn when loading save games with mod mismatch Feature #2313: ess-Importer: convert global map exploration overlay Feature #2318: Add commandline option to load a save game Task #810: Rename "profile" to "content list" Task #2196: Label local/global openmw.cfg files via comments 0.34.0 ------ Bug #904: omwlauncher doesn't allow installing Tribunal and Bloodmoon if only MW is installed Bug #986: Launcher: renaming profile names is broken Bug #1061: "Browse to CD..." launcher crash Bug #1135: Launcher crashes if user does not have write permission Bug #1231: Current installer in launcher does not correctly import russian Morrowind.ini settings from setup.inx Bug #1288: Fix the Alignment of the Resolution Combobox Bug #1343: BIK videos occasionally out of sync with audio Bug #1684: Morrowind Grass Mod graphical glitches Bug #1734: NPC in fight with invisible/sneaking player Bug #1982: Long class names are cut off in the UI Bug #2012: Editor: OpenCS script compiler sometimes fails to find IDs Bug #2015: Running while levitating does not affect speed but still drains fatigue Bug #2018: OpenMW don´t reset modified cells to vanilla when a plugin is deselected and don´t apply changes to cells already visited. Bug #2045: ToggleMenus command should close dialogue windows Bug #2046: Crash: light_de_streetlight_01_223 Bug #2047: Buglamp tooltip minor correction Bug #2050: Roobrush floating texture bits Bug #2053: Slaves react negatively to PC picking up slave's bracers Bug #2055: Dremora corpses use the wrong model Bug #2056: Mansilamat Vabdas's corpse is floating in the water Bug #2057: "Quest: Larius Varro Tells A Little Story": Bounty not completely removed after finishing quest Bug #2059: Silenced enemies try to cast spells anyway Bug #2060: Editor: Special case implementation for top level window with single sub-window should be optional Bug #2061: Editor: SubView closing that is not directly triggered by the user isn't handled properly Bug #2063: Tribunal: Quest 'The Warlords' doesn't work Bug #2064: Sneak attack on hostiles causes bounty Bug #2065: Editor: Qt signal-slot error when closing a dialogue subview Bug #2070: Loading ESP in OpenMW works but fails in OpenCS Bug #2071: CTD in 0.33 Bug #2073: Storm atronach animation stops now and then Bug #2075: Molag Amur Region, Map shows water on solid ground Bug #2080: game won't work with fair magicka regen Bug #2082: NPCs appear frozen or switched off after leaving and quickly reentering a cell Bug #2088: OpenMW is unable to play OGG files. Bug #2093: Darth Gares talks to you in Ilunibi even when he's not there, screwing up the Main Quests Bug #2095: Coordinate and rotation editing in the Reference table does not work. Bug #2096: Some overflow fun and bartering exploit Bug #2098: [D3D] Game crash on maximize Bug #2099: Activate, player seems not to work Bug #2104: Only labels are sensitive in buttons Bug #2107: "Slowfall" effect is too weak Bug #2114: OpenCS doesn't load an ESP file full of errors even though Vanilla MW Construction Set can Bug #2117: Crash when encountering bandits on opposite side of river from the egg mine south of Balmora Bug #2124: [Mod: Baldurians Transparent Glass Amor] Armor above head Bug #2125: Unnamed NiNodes in weapons problem in First Person Bug #2126: Dirty dialog script in tribunal.esm causing bug in Tribunal MQ Bug #2128: Crash when picking character's face Bug #2129: Disable the third-person zoom feature by default Bug #2130: Ash storm particles shown too long during transition to clear sky Bug #2137: Editor: exception caused by following the Creature column of a SoundGen record Bug #2139: Mouse movement should be ignored during intro video Bug #2143: Editor: Saving is broken Bug #2145: OpenMW - crash while exiting x64 debug build Bug #2152: You can attack Almalexia during her final monologue Bug #2154: Visual effects behave weirdly after loading/taking a screenshot Bug #2155: Vivec has too little magicka Bug #2156: Azura's spirit fades away too fast Bug #2158: [Mod]Julan Ashlander Companion 2.0: Negative magicka Bug #2161: Editor: combat/magic/stealth values of creature not displayed correctly Bug #2163: OpenMW can't detect death if the NPC die by the post damage effect of a magic weapon. Bug #2168: Westly's Master Head Pack X – Some hairs aren't rendered correctly. Bug #2170: Mods using conversations to update PC inconsistant Bug #2180: Editor: Verifier doesn't handle Windows-specific path issues when dealing with resources Bug #2212: Crash or unexpected behavior while closing OpenCS cell render window on OS X Feature #238: Add UI to run INI-importer from the launcher Feature #854: Editor: Add user setting to show status bar Feature #987: Launcher: first launch instructions for CD need to be more explicit Feature #1232: There is no way to set the "encoding" option using launcher UI. Feature #1281: Editor: Render cell markers Feature #1918: Editor: Functionality for Double-Clicking in Tables Feature #1966: Editor: User Settings dialogue grouping/labelling/tooltips Feature #2097: Editor: Edit position of references in 3D scene Feature #2121: Editor: Add edit mode button to scene toolbar Task #1965: Editor: Improve layout of user settings dialogue 0.33.1 ------ Bug #2108: OpenCS fails to build 0.33.0 ------ Bug #371: If console assigned to ` (probably to any symbolic key), "`" symbol will be added to console every time it closed Bug #1148: Some books'/scrolls' contents are displayed incorrectly Bug #1290: Editor: status bar is not updated when record filter is changed Bug #1292: Editor: Documents are not removed on closing the last view Bug #1301: Editor: File->Exit only checks the document it was issued from. Bug #1353: Bluetooth on with no speaker connected results in significantly longer initial load times Bug #1436: NPCs react from too far distance Bug #1472: PC is placed on top of following NPC when changing cell Bug #1487: Tall PC can get stuck in staircases Bug #1565: Editor: Subviews are deleted on shutdown instead when they are closed Bug #1623: Door marker on Ghorak Manor's balcony makes PC stuck Bug #1633: Loaddoor to Sadrith Mora, Telvanni Council House spawns PC in the air Bug #1655: Use Appropriate Application Icons on Windows Bug #1679: Tribunal expansion, Meryn Othralas the backstage manager in the theatre group in Mournhold in the great bazaar district is floating a good feet above the ground. Bug #1705: Rain is broken in third person Bug #1706: Thunder and lighting still occurs while the game is paused during the rain Bug #1708: No long jumping Bug #1710: Editor: ReferenceableID drag to references record filter field creates incorrect filter Bug #1712: Rest on Water Bug #1715: "Cancel" button is not always on the same side of menu Bug #1725: Editor: content file can be opened multiple times from the same dialogue Bug #1730: [MOD: Less Generic Nerevarine] Compile failure attempting to enter the Corprusarium. Bug #1733: Unhandled ffmpeg sample formats Bug #1735: Editor: "Edit Record" context menu button not opening subview for journal infos Bug #1750: Editor: record edits result in duplicate entries Bug #1789: Editor: Some characters cannot be used in addon name Bug #1803: Resizing the map does not keep the pre-resize center at the post-resize center Bug #1821: Recovering Cloudcleaver quest: attacking Sosia is considered a crime when you side with Hlormar Bug #1838: Editor: Preferences window appears off screen Bug #1839: Editor: Record filter title should be moved two pixels to the right Bug #1849: Subrecord error in MAO_Containers Bug #1854: Knocked-out actors don't fully act knocked out Bug #1855: "Soul trapped" sound doesn't play Bug #1857: Missing sound effect for enchanted items with empty charge Bug #1859: Missing console command: ResetActors (RA) Bug #1861: Vendor category "MagicItems" is unhandled Bug #1862: Launcher doesn't start if a file listed in launcher.cfg has correct name but wrong capitalization Bug #1864: Editor: Region field for cell record in dialogue subview not working Bug #1869: Editor: Change label "Musics" to "Music" Bug #1870: Goblins killed while knocked down remain in knockdown-pose Bug #1874: CellChanged events should not trigger when crossing exterior cell border Bug #1877: Spriggans killed instantly if hit while regening Bug #1878: Magic Menu text not un-highlighting correctly when going from spell to item as active magic Bug #1881: Stuck in ceiling when entering castle karstaags tower Bug #1884: Unlit torches still produce a burning sound Bug #1885: Can type text in price field in barter window Bug #1887: Equipped items do not emit sounds Bug #1889: draugr lord aesliip will attack you and remain non-hostile Bug #1892: Guard asks player to pay bounty of 0 gold Bug #1895: getdistance should only return max float if ref and target are in different worldspaces Bug #1896: Crash Report Bug #1897: Conjured Equipment cant be re-equipped if removed Bug #1898: Only Gidar Verothan follows you during establish the mine quest Bug #1900: Black screen when you open the door and breath underwater Bug #1904: Crash on casting recall spell Bug #1906: Bound item checks should use the GMSTs Bug #1907: Bugged door. Mournhold, The Winged Guar Bug #1908: Crime reported for attacking Drathas Nerus's henchmen while they attack Dilborn Bug #1909: Weird Quest Flow Infidelities quest Bug #1910: Follower fighting with gone npc Bug #1911: Npcs will drown themselves Bug #1912: World map arrow stays static when inside a building Bug #1920: Ulyne Henim disappears when game is loaded inside Vas Bug #1922: alchemy-> potion of paralyze Bug #1923: "levitation magic cannot be used here" shows outside of tribunal Bug #1927: AI prefer melee over magic. Bug #1929: Tamriel Rebuilt: Named cells that lie within the overlap with Morrowind.esm are not shown Bug #1932: BTB - Spells 14.1 magic effects don´t overwrite the Vanilla ones but are added Bug #1935: Stacks of items are worth more when sold individually Bug #1940: Launcher does not list addon files if base game file is renamed to a different case Bug #1946: Mod "Tel Nechim - moved" breaks savegames Bug #1947: Buying/Selling price doesn't properly affect the growth of mercantile skill Bug #1950: followers from east empire company quest will fight each other if combat happens with anything Bug #1958: Journal can be scrolled indefinitely with a mouse wheel Bug #1959: Follower not leaving party on quest end Bug #1960: Key bindings not always saved correctly Bug #1961: Spell merchants selling racial bonus spells Bug #1967: segmentation fault on load saves Bug #1968: Jump sounds are not controlled by footsteps slider, sound weird compared to footsteps Bug #1970: PC suffers silently when taking damage from lava Bug #1971: Dwarven Sceptre collision area is not removed after killing one Bug #1974: Dalin/Daris Norvayne follows player indefinitely Bug #1975: East Empire Company faction rank breaks during Raven Rock questline Bug #1979: 0 strength = permanently over encumbered Bug #1993: Shrine blessing in Maar Gan doesn't work Bug #2008: Enchanted items do not recharge Bug #2011: Editor: OpenCS script compiler doesn't handle member variable access properly Bug #2016: Dagoth Ur already dead in Facility Cavern Bug #2017: Fighters Guild Quest: The Code Book - dialogue loop when UMP is loaded. Bug #2019: Animation of 'Correct UV Mudcrabs' broken Bug #2022: Alchemy window - Removing ingredient doesn't remove the number of ingredients Bug #2025: Missing mouse-over text for non affordable items Bug #2028: [MOD: Tamriel Rebuilt] Crashing when trying to enter interior cell "Ruinous Keep, Great Hall" Bug #2029: Ienith Brothers Thiev's Guild quest journal entry not adding Feature #471: Editor: Special case implementation for top-level window with single sub-window Feature #472: Editor: Sub-Window re-use settings Feature #704: Font colors import from fallback settings Feature #879: Editor: Open sub-views in a new top-level window Feature #932: Editor: magic effect table Feature #937: Editor: Path Grid table Feature #938: Editor: Sound Gen table Feature #1117: Death and LevelUp music Feature #1226: Editor: Request UniversalId editing from table columns Feature #1545: Targeting console on player Feature #1597: Editor: Render terrain Feature #1695: Editor: add column for CellRef's global variable Feature #1696: Editor: use ESM::Cell's RefNum counter Feature #1697: Redden player's vision when hit Feature #1856: Spellcasting for non-biped creatures Feature #1879: Editor: Run OpenMW with the currently edited content list Task #1851: Move AI temporary state out of AI packages Task #1865: Replace char type in records 0.32.0 ------ Bug #1132: Unable to jump when facing a wall Bug #1341: Summoned Creatures do not immediately disappear when killed. Bug #1430: CharGen Revamped script does not compile Bug #1451: NPCs shouldn't equip weapons prior to fighting Bug #1461: Stopped start scripts do not restart on load Bug #1473: Dead NPC standing and in 2 pieces Bug #1482: Abilities are depleted when interrupted during casting Bug #1503: Behaviour of NPCs facing the player Bug #1506: Missing character, French edition: three-points Bug #1528: Inventory very slow after 2 hours Bug #1540: Extra arguments should be ignored for script functions Bug #1541: Helseth's Champion: Tribunal Bug #1570: Journal cannot be opened while in inventory screen Bug #1573: PC joins factions at random Bug #1576: NPCs aren't switching their weapons when out of ammo Bug #1579: Guards detect creatures in far distance, instead on sight Bug #1588: The Siege of the Skaal Village: bloodmoon Bug #1593: The script compiler isn't recognising some names that contain a - Bug #1606: Books: Question marks instead of quotation marks Bug #1608: Dead bodies prevent door from opening/closing. Bug #1609: Imperial guards in Sadrith Mora are not using their spears Bug #1610: The bounty number is not displayed properly with high numbers Bug #1620: Implement correct formula for auto-calculated NPC spells Bug #1630: Boats standing vertically in Vivec Bug #1635: Arrest dialogue is executed second time after I select "Go to jail" Bug #1637: Weird NPC behaviour in Vivec, Hlaalu Ancestral Vaults? Bug #1641: Persuasion dialog remains after loading, possibly resulting in crash Bug #1644: "Goodbye" and similar options on dialogues prevents escape working properly. Bug #1646: PC skill stats are not updated immediately when changing equipment Bug #1652: Non-aggressive creature Bug #1653: Quickloading while the container window is open crashes the game Bug #1654: Priority of checks in organic containers Bug #1656: Inventory items merge issue when repairing Bug #1657: Attacked state of NPCs is not saved properly Bug #1660: Rank dialogue condition ignored Bug #1668: Game starts on day 2 instead of day 1 Bug #1669: Critical Strikes while fighting a target who is currently fighting me Bug #1672: OpenCS doesn't save the projects Bug #1673: Fatigue decreasing by only one point when running Bug #1675: Minimap and localmap graphic glitches Bug #1676: Pressing the OK button on the travel menu cancels the travel and exits the menu Bug #1677: Sleeping in a rented bed is considered a crime Bug #1685: NPCs turn towards player even if invisible/sneaking Bug #1686: UI bug: cursor is clicking "world/local" map button while inventory window is closed? Bug #1690: Double clicking on a inventory window header doesn't close it. Bug #1693: Spell Absorption does not absorb shrine blessings Bug #1694: journal displays learned topics as quests Bug #1700: Sideways scroll of text boxes Bug #1701: Player enchanting requires player hold money, always 100% sucessful. Bug #1704: self-made Fortify Intelligence/Drain willpower potions are broken Bug #1707: Pausing the game through the esc menu will silence rain, pausing it by opening the inventory will not. Bug #1709: Remesa Othril is hostile to Hlaalu members Bug #1713: Crash on load after death Bug #1719: Blind effect has slight border at the edge of the screen where it is ineffective. Bug #1722: Crash after creating enchanted item, reloading saved game Bug #1723: Content refs that are stacked share the same index after unstacking Bug #1726: Can't finish Aengoth the Jeweler's quest : Retrieve the Scrap Metal Bug #1727: Targets almost always resist soultrap scrolls Bug #1728: Casting a soultrap spell on invalid target yields no message Bug #1729: Chop attack doesn't work if walking diagonally Bug #1732: Error handling for missing script function arguments produces weird message Bug #1736: Alt-tabbing removes detail from overworld map. Bug #1737: Going through doors with (high magnitude?) leviation will put the player high up, possibly even out of bounds. Bug #1739: Setting a variable on an NPC from another NPC's dialogue result sets the wrong variable Bug #1741: The wait dialogue doesn't black the screen out properly during waiting. Bug #1742: ERROR: Object 'sDifficulty' not found (const) Bug #1744: Night sky in Skies V.IV (& possibly v3) by SWG rendered incorrectly Bug #1746: Bow/marksman weapon condition does not degrade with use Bug #1749: Constant Battle Music Bug #1752: Alt-Tabbing in the character menus makes the paper doll disappear temporarily Bug #1753: Cost of training is not added to merchant's inventory Bug #1755: Disposition changes do not persist if the conversation menu is closed by purchasing training. Bug #1756: Caught Blight after being cured of Corprus Bug #1758: Crash Upon Loading New Cell Bug #1760: Player's Magicka is not recalculated upon drained or boosted intelligence Bug #1761: Equiped torches lost on reload Bug #1762: Your spell did not get a target. Soul trap. Gorenea Andrano Bug #1763: Custom Spell Magicka Cost Bug #1765: Azuras Star breaks on recharging item Bug #1767: GetPCRank did not handle ignored explicit references Bug #1772: Dark Brotherhood Assassins never use their Carved Ebony Dart, sticking to their melee weapon. Bug #1774: String table overflow also occurs when loading TheGloryRoad.esm Bug #1776: dagoth uthol runs in slow motion Bug #1778: Incorrect values in spellmaking window Bug #1779: Icon of Master Propylon Index is not visible Bug #1783: Invisible NPC after looting corpse Bug #1787: Health Calculation Bug #1788: Skeletons, ghosts etc block doors when we try to open Bug #1791: [MOD: LGNPC Foreign Quarter] NPC in completely the wrong place. Bug #1792: Potions should show more effects Bug #1793: Encumbrance while bartering Bug #1794: Fortify attribute not affecting fatigue Bug #1795: Too much magicka Bug #1796: "Off by default" torch burning Bug #1797: Fish too slow Bug #1798: Rest until healed shouldn't show with full health and magicka Bug #1802: Mark location moved Bug #1804: stutter with recent builds Bug #1810: attack gothens dremora doesnt agro the others. Bug #1811: Regression: Crash Upon Loading New Cell Bug #1812: Mod: "QuickChar" weird button placement Bug #1815: Keys show value and weight, Vanilla Morrowind's keys dont. Bug #1817: Persuasion results do not show using unpatched MW ESM Bug #1818: Quest B3_ZainabBride moves to stage 47 upon loading save while Falura Llervu is following Bug #1823: AI response to theft incorrect - only guards react, in vanilla everyone does. Bug #1829: On-Target Spells Rendered Behind Water Surface Effects Bug #1830: Galsa Gindu's house is on fire Bug #1832: Fatal Error: OGRE Exception(2:InvalidParametersException) Bug #1836: Attacked Guards open "fine/jail/resist"-dialogue after killing you Bug #1840: Infinite recursion in ActionTeleport Bug #1843: Escorted people change into player's cell after completion of escort stage Bug #1845: Typing 'j' into 'Name' fields opens the journal Bug #1846: Text pasted into the console still appears twice (Windows) Bug #1847: "setfatigue 0" doesn't render NPC unconscious Bug #1848: I can talk to unconscious actors Bug #1866: Crash when player gets killed by a creature summoned by him Bug #1868: Memory leaking when openmw window is minimized Feature #47: Magic Effects Feature #642: Control NPC mouth movement using current Say sound Feature #939: Editor: Resources tables Feature #961: AI Combat for magic (spells, potions and enchanted items) Feature #1111: Collision script instructions (used e.g. by Lava) Feature #1120: Command creature/humanoid magic effects Feature #1121: Elemental shield magic effects Feature #1122: Light magic effect Feature #1139: AI: Friendly hits Feature #1141: AI: combat party Feature #1326: Editor: Add tooltips to all graphical buttons Feature #1489: Magic effect Get/Mod/Set functions Feature #1505: Difficulty slider Feature #1538: Targeted scripts Feature #1571: Allow creating custom markers on the local map Feature #1615: Determine local variables from compiled scripts instead of the values in the script record Feature #1616: Editor: Body part record verifier Feature #1651: Editor: Improved keyboard navigation for scene toolbar Feature #1666: Script blacklisting Feature #1711: Including the Git revision number from the command line "--version" switch. Feature #1721: NPC eye blinking Feature #1740: Scene toolbar buttons for selecting which type of elements are rendered Feature #1790: Mouse wheel scrolling for the journal Feature #1850: NiBSPArrayController Task #768: On windows, settings folder should be "OpenMW", not "openmw" Task #908: Share keyframe data Task #1716: Remove defunct option for building without FFmpeg 0.31.0 ------ Bug #245: Cloud direction and weather systems differ from Morrowind Bug #275: Local Map does not always show objects that span multiple cells Bug #538: Update CenterOnCell (COC) function behavior Bug #618: Local and World Map Textures are sometimes Black Bug #640: Water behaviour at night Bug #668: OpenMW doesn't support non-latin paths on Windows Bug #746: OpenMW doesn't check if the background music was already played Bug #747: Door is stuck if cell is left before animation finishes Bug #772: Disabled statics are visible on map Bug #829: OpenMW uses up all available vram, when playing for extended time Bug #869: Dead bodies don't collide with anything Bug #894: Various character creation issues Bug #897/#1369: opencs Segmentation Fault after "new" or "load" Bug #899: Various jumping issues Bug #952: Reflection effects are one frame delayed Bug #993: Able to interact with world during Wait/Rest dialog Bug #995: Dropped items can be placed inside the wall Bug #1008: Corpses always face up upon reentering the cell Bug #1035: Random colour patterns appearing in automap Bug #1037: Footstep volume issues Bug #1047: Creation of wrong links in dialogue window Bug #1129: Summoned creature time life duration seems infinite Bug #1134: Crimes can be committed against hostile NPCs Bug #1136: Creature run speed formula is incorrect Bug #1150: Weakness to Fire doesn't apply to Fire Damage in the same spell Bug #1155: NPCs killing each other Bug #1166: Bittercup script still does not work Bug #1178: .bsa file names are case sensitive. Bug #1179: Crash after trying to load game after being killed Bug #1180: Changing footstep sound location Bug #1196: Jumping not disabled when showing messageboxes Bug #1202: "strange" keys are not shown in binding menu, and are not saved either, but works Bug #1216: Broken dialog topics in russian Morrowind Bug #1217: Container content changes based on the current position of the mouse Bug #1234: Loading/saving issues with dynamic records Bug #1277: Text pasted into the console appears twice Bug #1284: Crash on New Game Bug #1303: It's possible to skip the chargen Bug #1304: Slaughterfish should not detect the player unless the player is in the water Bug #1311: Editor: deleting Record Filter line does not reset the filter Bug #1324: ERROR: ESM Error: String table overflow when loading Animated Morrowind.esp Bug #1328: Editor: Bogus Filter created when dragging multiple records to filter bar of non-applicable table Bug #1331: Walking/running sound persist after killing NPC`s that are walking/running. Bug #1334: Previously equipped items not shown as unequipped after attempting to sell them. Bug #1335: Actors ignore vertical axis when deciding to attack Bug #1338: Unknown toggle option for shadows Bug #1339: "Ashlands Region" is visible when beginning new game during "Loading Area" process Bug #1340: Guards prompt Player with punishment options after resisting arrest with another guard. Bug #1348: Regression: Bug #1098 has returned with a vengeance Bug #1349: [TR] TR_Data mesh tr_ex_imp_gatejamb01 cannot be activated Bug #1352: Disabling an ESX file does not disable dependent ESX files Bug #1355: CppCat Checks OpenMW Bug #1356: Incorrect voice type filtering for sleep interrupts Bug #1357: Restarting the game clears saves Bug #1360: Seyda Neen silk rider dialog problem Bug #1361: Some lights don't work Bug #1364: It is difficult to bind "Mouse 1" to an action in the options menu Bug #1370: Animation compilation mod does not work properly Bug #1371: SL_Pick01.nif from third party fails to load in openmw, but works in Vanilla Bug #1373: When stealing in front of Sellus Gravius cannot exit the dialog Bug #1378: Installs to /usr/local are not working Bug #1380: Loading a save file fail if one of the content files is disabled Bug #1382: "getHExact() size mismatch" crash on loading official plugin "Siege at Firemoth.esp" Bug #1386: Arkngthand door will not open Bug #1388: Segfault when modifying View Distance in Menu options Bug #1389: Crash when loading a save after dying Bug #1390: Apostrophe characters not displayed [French version] Bug #1391: Custom made icon background texture for magical weapons and stuff isn't scaled properly on GUI. Bug #1393: Coin icon during the level up dialogue are off of the background Bug #1394: Alt+F4 doesn't work on Win version Bug #1395: Changing rings switches only the last one put on Bug #1396: Pauldron parts aren't showing when the robe is equipped Bug #1402: Dialogue of some shrines have wrong button orientation Bug #1403: Items are floating in the air when they're dropped onto dead bodies. Bug #1404: Forearms are not rendered on Argonian females Bug #1407: Alchemy allows making potions from two of the same item Bug #1408: "Max sale" button gives you all the items AND all the trader's gold Bug #1409: Rest "Until Healed" broken for characters with stunted magicka. Bug #1412: Empty travel window opens while playing through start game Bug #1413: Save game ignores missing writing permission Bug #1414: The Underground 2 ESM Error Bug #1416: Not all splash screens in the Splash directory are used Bug #1417: Loading saved game does not terminate Bug #1419: Skyrim: Home of the Nords error Bug #1422: ClearInfoActor Bug #1423: ForceGreeting closes existing dialogue windows Bug #1425: Cannot load save game Bug #1426: Read skill books aren't stored in savegame Bug #1427: Useless items can be set under hotkeys Bug #1429: Text variables in journal Bug #1432: When attacking friendly NPC, the crime is reported and bounty is raised after each swing Bug #1435: Stealing priceless items is without punishment Bug #1437: Door marker at Jobasha's Rare Books is spawning PC in the air Bug #1440: Topic selection menu should be wider Bug #1441: Dropping items on the rug makes them inaccessible Bug #1442: When dropping and taking some looted items, bystanders consider that as a crime Bug #1444: Arrows and bolts are not dropped where the cursor points Bug #1445: Security trainers offering acrobatics instead Bug #1447: Character dash not displayed, French edition Bug #1448: When the player is killed by the guard while having a bounty on his head, the guard dialogue opens over and over instead of loading dialogue Bug #1454: Script error in SkipTutorial Bug #1456: Bad lighting when using certain Morrowind.ini generated by MGE Bug #1457: Heart of Lorkan comes after you when attacking it Bug #1458: Modified Keybindings are not remembered Bug #1459: Dura Gra-Bol doesn't respond to PC attack Bug #1462: Interior cells not loaded with Morrowind Patch active Bug #1469: Item tooltip should show the base value, not real value Bug #1477: Death count is not stored in savegame Bug #1478: AiActivate does not trigger activate scripts Bug #1481: Weapon not rendered when partially submerged in water Bug #1483: Enemies are attacking even while dying Bug #1486: ESM Error: Don't know what to do with INFO Bug #1490: Arrows shot at PC can end up in inventory Bug #1492: Monsters respawn on top of one another Bug #1493: Dialogue box opens with follower NPC even if NPC is dead Bug #1494: Paralysed cliffracers remain airbourne Bug #1495: Dialogue box opens with follower NPC even the game is paused Bug #1496: GUI messages are not cleared when loading another saved game Bug #1499: Underwater sound sometimes plays when transitioning from interior. Bug #1500: Targetted spells and water. Bug #1502: Console error message on info refusal Bug #1507: Bloodmoon MQ The Ritual of Beasts: Can't remove the arrow Bug #1508: Bloodmoon: Fort Frostmoth, cant talk with Carnius Magius Bug #1516: PositionCell doesn't move actors to current cell Bug #1518: ForceGreeting broken for explicit references Bug #1522: Crash after attempting to play non-music file Bug #1523: World map empty after loading interior save Bug #1524: Arrows in waiting/resting dialog act like minimum and maximum buttons Bug #1525: Werewolf: Killed NPC's don't fill werewolfs hunger for blood Bug #1527: Werewolf: Detect life detects wrong type of actor Bug #1529: OpenMW crash during "the shrine of the dead" mission (tribunal) Bug #1530: Selected text in the console has the same color as the background Bug #1539: Barilzar's Mazed Band: Tribunal Bug #1542: Looping taunts from NPC`s after death: Tribunal Bug #1543: OpenCS crash when using drag&drop in script editor Bug #1547: Bamz-Amschend: Centurion Archers combat problem Bug #1548: The Missing Hand: Tribunal Bug #1549: The Mad God: Tribunal, Dome of Serlyn Bug #1557: A bounty is calculated from actual item cost Bug #1562: Invisible terrain on top of Red Mountain Bug #1564: Cave of the hidden music: Bloodmoon Bug #1567: Editor: Deleting of referenceables does not work Bug #1568: Picking up a stack of items and holding the enter key and moving your mouse around paints a bunch of garbage on screen. Bug #1574: Solstheim: Drauger cant inflict damage on player Bug #1578: Solstheim: Bonewolf running animation not working Bug #1585: Particle effects on PC are stopped when paralyzed Bug #1589: Tribunal: Crimson Plague quest does not update when Gedna Relvel is killed Bug #1590: Failed to save game: compile error Bug #1598: Segfault when making Drain/Fortify Skill spells Bug #1599: Unable to switch to fullscreen Bug #1613: Morrowind Rebirth duplicate objects / vanilla objects not removed Bug #1618: Death notice fails to show up Bug #1628: Alt+Tab Segfault Feature #32: Periodic Cleanup/Refill Feature #41: Precipitation and weather particles Feature #568: Editor: Configuration setup Feature #649: Editor: Threaded loading Feature #930: Editor: Cell record saving Feature #934: Editor: Body part table Feature #935: Editor: Enchantment effect table Feature #1162: Dialogue merging Feature #1174: Saved Game: add missing creature state Feature #1177: Saved Game: fog of war state Feature #1312: Editor: Combat/Magic/Stealth values for creatures are not displayed Feature #1314: Make NPCs and creatures fight each other Feature #1315: Crime: Murder Feature #1321: Sneak skill enhancements Feature #1323: Handle restocking items Feature #1332: Saved Game: levelled creatures Feature #1347: modFactionReaction script instruction Feature #1362: Animated main menu support Feature #1433: Store walk/run toggle Feature #1449: Use names instead of numbers for saved game files and folders Feature #1453: Adding Delete button to the load menu Feature #1460: Enable Journal screen while in dialogue Feature #1480: Play Battle music when in combat Feature #1501: Followers unable to fast travel with you Feature #1520: Disposition and distance-based aggression/ShouldAttack Feature #1595: Editor: Object rendering in cells Task #940: Move license to locations where applicable Task #1333: Remove cmake git tag reading Task #1566: Editor: Object rendering refactoring 0.30.0 ------ Bug #416: Extreme shaking can occur during cell transitions while moving Bug #1003: Province Cyrodiil: Ogre Exception in Stirk Bug #1071: Crash when given a non-existent content file Bug #1080: OpenMW allows resting/using a bed while in combat Bug #1097: Wrong punishment for stealing in Census and Excise Office at the start of a new game Bug #1098: Unlocked evidence chests should get locked after new evidence is put into them Bug #1099: NPCs that you attacked still fight you after you went to jail/paid your fine Bug #1100: Taking items from a corpse is considered stealing Bug #1126: Some creatures can't get close enough to attack Bug #1144: Killed creatures seem to die again each time player transitions indoors/outdoors Bug #1181: loading a saved game does not reset the player control status Bug #1185: Collision issues in Addamasartus Bug #1187: Athyn Sarethi mission, rescuing varvur sarethi from the doesnt end the mission Bug #1189: Crash when entering interior cell "Gnisis, Arvs-Drelen" Bug #1191: Picking up papers without inventory in new game Bug #1195: NPCs do not equip torches in certain interiors Bug #1197: mouse wheel makes things scroll too fast Bug #1200: door blocked by monsters Bug #1201: item's magical charges are only refreshed when they are used Bug #1203: Scribs do not defend themselves Bug #1204: creatures life is not empty when they are dead Bug #1205: armor experience does not progress when hits are taken Bug #1206: blood particules always red. Undeads and mechanicals should have a different one. Bug #1209: Tarhiel never falls Bug #1210: journal adding script is ran again after having saved/loaded Bug #1224: Names of custom classes are not properly handled in save games Bug #1227: Editor: Fixed case handling for broken localised versions of Morrowind.esm Bug #1235: Indoors walk stutter Bug #1236: Aborting intro movie brings up the menu Bug #1239: NPCs get stuck when walking past each other Bug #1240: BTB - Settings 14.1 and Health Bar. Bug #1241: BTB - Character and Khajiit Prejudice Bug #1248: GUI Weapon icon is changed to hand-to-hand after save load Bug #1254: Guild ranks do not show in dialogue Bug #1255: When opening a container and selecting "Take All", the screen flashes blue Bug #1260: Level Up menu doesn't show image when using a custom class Bug #1265: Quit Menu Has Misaligned Buttons Bug #1270: Active weapon icon is not updated when weapon is repaired Bug #1271: NPC Stuck in hovering "Jumping" animation Bug #1272: Crash when attempting to load Big City esm file. Bug #1276: Editor: Dropping a region into the filter of a cell subview fails Bug #1286: Dialogue topic list clips with window frame Bug #1291: Saved game: store faction membership Bug #1293: Pluginless Khajiit Head Pack by ashiraniir makes OpenMW close. Bug #1294: Pasting in console adds text to end, not at cursor Bug #1295: Conversation loop when asking about "specific place" in Vivec Bug #1296: Caius doesn't leave at start of quest "Mehra Milo and the Lost Prophecies" Bug #1297: Saved game: map markers Bug #1302: ring_keley script causes vector::_M_range_check exception Bug #1309: Bug on "You violated the law" dialog Bug #1319: Creatures sometimes rendered incorrectly Feature #50: Ranged Combat Feature #58: Sneaking Skill Feature #73: Crime and Punishment Feature #135: Editor: OGRE integration Feature #541: Editor: Dialogue Sub-Views Feature #853: Editor: Rework User Settings Feature #944: Editor: lighting modes Feature #945: Editor: Camera navigation mode Feature #953: Trader gold Feature #1140: AI: summoned creatures Feature #1142: AI follow: Run stance Feature #1154: Not all NPCs get aggressive when one is attacked Feature #1169: Terrain threading Feature #1172: Loading screen and progress bars during saved/loading game Feature #1173: Saved Game: include weather state Feature #1207: Class creation form does not remember Feature #1220: Editor: Preview Subview Feature #1223: Saved Game: Local Variables Feature #1229: Quicksave, quickload, autosave Feature #1230: Deleting saves Feature #1233: Bribe gold is placed into NPCs inventory Feature #1252: Saved Game: quick key bindings Feature #1273: Editor: Region Map context menu Feature #1274: Editor: Region Map drag & drop Feature #1275: Editor: Scene subview drop Feature #1282: Non-faction member crime recognition. Feature #1289: NPCs return to default position Task #941: Remove unused cmake files 0.29.0 ------ Bug #556: Video soundtrack not played when music volume is set to zero Bug #829: OpenMW uses up all available vram, when playing for extended time Bug #848: Wrong amount of footsteps playing in 1st person Bug #888: Ascended Sleepers have movement issues Bug #892: Explicit references are allowed on all script functions Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly Bug #1009: Lake Fjalding AI related slowdown. Bug #1041: Music playback issues on OS X >= 10.9 Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window Bug #1060: Some message boxes are cut off at the bottom Bug #1062: Bittercup script does not work ('end' variable) Bug #1074: Inventory paperdoll obscures armour rating Bug #1077: Message after killing an essential NPC disappears too fast Bug #1078: "Clutterbane" shows empty charge bar Bug #1083: UndoWerewolf fails Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered Bug #1090: Start scripts fail when going to a non-predefined cell Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior Bug #1105: Magicka is depleted when using uncastable spells Bug #1106: Creatures should be able to run Bug #1107: TR cliffs have way too huge collision boxes in OpenMW Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) Bug #1115: Memory leak when spying on Fargoth Bug #1137: Script execution fails (drenSlaveOwners script) Bug #1143: Mehra Milo quest (vivec informants) is broken Bug #1145: Issues with moving gold between inventory and containers Bug #1146: Issues with picking up stacks of gold Bug #1147: Dwemer Crossbows are held incorrectly Bug #1158: Armor rating should always stay below inventory mannequin Bug #1159: Quick keys can be set during character generation Bug #1160: Crash on equip lockpick when Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file Feature #30: Loading/Saving (still missing a few parts) Feature #101: AI Package: Activate Feature #103: AI Package: Follow, FollowCell Feature #138: Editor: Drag & Drop Feature #428: Player death Feature #505: Editor: Record Cloning Feature #701: Levelled creatures Feature #708: Improved Local Variable handling Feature #709: Editor: Script verifier Feature #764: Missing journal backend features Feature #777: Creature weapons/shields Feature #789: Editor: Referenceable record verifier Feature #924: Load/Save GUI (still missing loading screen and progress bars) Feature #946: Knockdown Feature #947: Decrease fatigue when running, swimming and attacking Feature #956: Melee Combat: Blocking Feature #957: Area magic Feature #960: Combat/AI combat for creatures Feature #962: Combat-Related AI instructions Feature #1075: Damage/Restore skill/attribute magic effects Feature #1076: Soultrap magic effect Feature #1081: Disease contraction Feature #1086: Blood particles Feature #1092: Interrupt resting Feature #1101: Inventory equip scripts Feature #1116: Version/Build number in Launcher window Feature #1119: Resistance/weakness to normal weapons magic effect Feature #1123: Slow Fall magic effect Feature #1130: Auto-calculate spells Feature #1164: Editor: Case-insensitive sorting in tables 0.28.0 ------ Bug #399: Inventory changes are not visible immediately Bug #417: Apply weather instantly when teleporting Bug #566: Global Map position marker not updated for interior cells Bug #712: Looting corpse delay Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod Bug #805: Two TR meshes appear black (v0.24RC) Bug #841: Third-person activation distance taken from camera rather than head Bug #845: NPCs hold torches during the day Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24 Bug #856: Maormer race by Mac Kom - The heads are way up Bug #864: Walk locks during loading in 3rd person Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog Bug #882: Hircine's Ring doesn't always work Bug #909: [Tamriel Rebuilt] crashes in Akamora Bug #922: Launcher writing merged openmw.cfg files Bug #943: Random magnitude should be calculated per effect Bug #948: Negative fatigue level should be allowed Bug #949: Particles in world space Bug #950: Hard crash on x64 Linux running --new-game (on startup) Bug #951: setMagicka and setFatigue have no effect Bug #954: Problem with equipping inventory items when using a keyboard shortcut Bug #955: Issues with equipping torches Bug #966: Shield is visible when casting spell Bug #967: Game crashes when equipping silver candlestick Bug #970: Segmentation fault when starting at Bal Isra Bug #977: Pressing down key in console doesn't go forward in history Bug #979: Tooltip disappears when changing inventory Bug #980: Barter: item category is remembered, but not shown Bug #981: Mod: replacing model has wrong position/orientation Bug #982: Launcher: Addon unchecking is not saved Bug #983: Fix controllers to affect objects attached to the base node Bug #985: Player can talk to NPCs who are in combat Bug #989: OpenMW crashes when trying to include mod with capital .ESP Bug #991: Merchants equip items with harmful constant effect enchantments Bug #994: Don't cap skills/attributes when set via console Bug #998: Setting the max health should also set the current health Bug #1005: Torches are visible when casting spells and during hand to hand combat. Bug #1006: Many NPCs have 0 skill Bug #1007: Console fills up with text Bug #1013: Player randomly loses health or dies Bug #1014: Persuasion window is not centered in maximized window Bug #1015: Player status window scroll state resets on status change Bug #1016: Notification window not big enough for all skill level ups Bug #1020: Saved window positions are not rescaled appropriately on resolution change Bug #1022: Messages stuck permanently on screen when they pile up Bug #1023: Journals doesn't open Bug #1026: Game loses track of torch usage. Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level Bug #1029: Quick keys menu: Select compatible replacement when tool used up Bug #1042: TES3 header data wrong encoding Bug #1045: OS X: deployed OpenCS won't launch Bug #1046: All damaged weaponry is worth 1 gold Bug #1048: Links in "locked" dialogue are still clickable Bug #1052: Using color codes when naming your character actually changes the name's color Bug #1054: Spell effects not visible in front of water Bug #1055: Power-Spell animation starts even though you already casted it that day Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability Bug #1063: Crash upon checking out game start ship area in Seyda Neen Bug #1064: openmw binaries link to unnecessary libraries Bug #1065: Landing from a high place in water still causes fall damage Bug #1072: Drawing weapon increases torch brightness Bug #1073: Merchants sell stacks of gold Feature #43: Visuals for Magic Effects Feature #51: Ranged Magic Feature #52: Touch Range Magic Feature #53: Self Range Magic Feature #54: Spell Casting Feature #70: Vampirism Feature #100: Combat AI Feature #171: Implement NIF record NiFlipController Feature #410: Window to restore enchanted item charge Feature #647: Enchanted item glow Feature #723: Invisibility/Chameleon magic effects Feature #737: Resist Magicka magic effect Feature #758: GetLOS Feature #926: Editor: Info-Record tables Feature #958: Material controllers Feature #959: Terrain bump, specular, & parallax mapping Feature #990: Request: unlock mouse when in any menu Feature #1018: Do not allow view mode switching while performing an action Feature #1027: Vertex morph animation (NiGeomMorpherController) Feature #1031: Handle NiBillboardNode Feature #1051: Implement NIF texture slot DarkTexture Task #873: Unify OGRE initialisation 0.27.0 ------ Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp Bug #794: incorrect display of decimal numbers Bug #840: First-person sneaking camera height Bug #887: Ambient sounds playing while paused Bug #902: Problems with Polish character encoding Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key Bug #910: Some CDs not working correctly with Unshield installer Bug #917: Quick character creation plugin does not work Bug #918: Fatigue does not refill Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) Feature #57: Acrobatics Skill Feature #462: Editor: Start Dialogue Feature #546: Modify ESX selector to handle new content file scheme Feature #588: Editor: Adjust name/path of edited content files Feature #644: Editor: Save Feature #710: Editor: Configure script compiler context Feature #790: God Mode Feature #881: Editor: Allow only one instance of OpenCS Feature #889: Editor: Record filtering Feature #895: Extinguish torches Feature #898: Breath meter enhancements Feature #901: Editor: Default record filter Feature #913: Merge --master and --plugin switches 0.26.0 ------ Bug #274: Inconsistencies in the terrain Bug #557: Already-dead NPCs do not equip clothing/items. Bug #592: Window resizing Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) Bug #664: Heart of lorkhan acts like a dead body (container) Bug #767: Wonky ramp physics & water Bug #780: Swimming out of water Bug #792: Wrong ground alignment on actors when no clipping Bug #796: Opening and closing door sound issue Bug #797: No clipping hinders opening and closing of doors Bug #799: sliders in enchanting window Bug #838: Pressing key during startup procedure freezes the game Bug #839: Combat/magic stances during character creation Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment Bug #844: Resting "until healed" option given even with full stats Bug #846: Equipped torches are invisible. Bug #847: Incorrect formula for autocalculated NPC initial health Bug #850: Shealt weapon sound plays when leaving magic-ready stance Bug #852: Some boots do not produce footstep sounds Bug #860: FPS bar misalignment Bug #861: Unable to print screen Bug #863: No sneaking and jumping at the same time Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. Bug #868: Idle animations are repeated Bug #874: Underwater swimming close to the ground is jerky Bug #875: Animation problem while swimming on the surface and looking up Bug #876: Always a starting upper case letter in the inventory Bug #878: Active spell effects don't update the layout properly when ended Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load Bug #896: New game sound issue Feature #49: Melee Combat Feature #71: Lycanthropy Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList Feature #622: Multiple positions for inventory window Feature #627: Drowning Feature #786: Allow the 'Activate' key to close the countdialog window Feature #798: Morrowind installation via Launcher (Linux/Max OS only) Feature #851: First/Third person transitions with mouse wheel Task #689: change PhysicActor::enableCollisions Task #707: Reorganise Compiler 0.25.0 ------ Bug #411: Launcher crash on OS X < 10.8 Bug #604: Terrible performance drop in the Census and Excise Office. Bug #676: Start Scripts fail to load Bug #677: OpenMW does not accept script names with - Bug #766: Extra space in front of topic links Bug #793: AIWander Isn't Being Passed The Repeat Parameter Bug #795: Sound playing with drawn weapon and crossing cell-border Bug #800: can't select weapon for enchantment Bug #801: Player can move while over-encumbered Bug #802: Dead Keys not working Bug #808: mouse capture Bug #809: ini Importer does not work without an existing cfg file Bug #812: Launcher will run OpenMW with no ESM or ESP selected Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected Bug #817: Dead NPCs and Creatures still have collision boxes Bug #820: Incorrect sorting of answers (Dialogue) Bug #826: mwinimport dumps core when given an unknown parameter Bug #833: getting stuck in door Bug #835: Journals/books not showing up properly. Feature #38: SoundGen Feature #105: AI Package: Wander Feature #230: 64-bit compatibility for OS X Feature #263: Hardware mouse cursors Feature #449: Allow mouse outside of window while paused Feature #736: First person animations Feature #750: Using mouse wheel in third person mode Feature #822: Autorepeat for slider buttons 0.24.0 ------ Bug #284: Book's text misalignment Bug #445: Camera able to get slightly below floor / terrain Bug #582: Seam issue in Red Mountain Bug #632: Journal Next Button shows white square Bug #653: IndexedStore ignores index Bug #694: Parser does not recognize float values starting with . Bug #699: Resource handling broken with Ogre 1.9 trunk Bug #718: components/esm/loadcell is using the mwworld subsystem Bug #729: Levelled item list tries to add nonexistent item Bug #730: Arrow buttons in the settings menu do not work. Bug #732: Erroneous behavior when binding keys Bug #733: Unclickable dialogue topic Bug #734: Book empty line problem Bug #738: OnDeath only works with implicit references Bug #740: Script compiler fails on scripts with special names Bug #742: Wait while no clipping Bug #743: Problem with changeweather console command Bug #744: No wait dialogue after starting a new game Bug #748: Player is not able to unselect objects with the console Bug #751: AddItem should only spawn a message box when called from dialogue Bug #752: The enter button has several functions in trade and looting that is not impelemted. Bug #753: Fargoth's Ring Quest Strange Behavior Bug #755: Launcher writes duplicate lines into settings.cfg Bug #759: Second quest in mages guild does not work Bug #763: Enchantment cast cost is wrong Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly Bug #773: AIWander Isn't Being Passed The Correct idle Values Bug #778: The journal can be opened at the start of a new game Bug #779: Divayth Fyr starts as dead Bug #787: "Batch count" on detailed FPS counter gets cut-off Bug #788: chargen scroll layout does not match vanilla Feature #60: Atlethics Skill Feature #65: Security Skill Feature #74: Interaction with non-load-doors Feature #98: Render Weapon and Shield Feature #102: AI Package: Escort, EscortCell Feature #182: Advanced Journal GUI Feature #288: Trading enhancements Feature #405: Integrate "new game" into the menu Feature #537: Highlight dialogue topic links Feature #658: Rotate, RotateWorld script instructions and local rotations Feature #690: Animation Layering Feature #722: Night Eye/Blind magic effects Feature #735: Move, MoveWorld script instructions. Feature #760: Non-removable corpses 0.23.0 ------ Bug #522: Player collides with placeable items Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open Bug #561: Tooltip word wrapping delay Bug #578: Bribing works incorrectly Bug #601: PositionCell fails on negative coordinates Bug #606: Some NPCs hairs not rendered with Better Heads addon Bug #609: Bad rendering of bone boots Bug #613: Messagebox causing assert to fail Bug #631: Segfault on shutdown Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard Bug #635: Scale NPCs depending on race Bug #643: Dialogue Race select function is inverted Bug #646: Twohanded weapons don't work properly Bug #654: Crash when dropping objects without a collision shape Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell Bug #660: "g" in "change" cut off in Race Menu Bug #661: Arrille sells me the key to his upstairs room Bug #662: Day counter starts at 2 instead of 1 Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. Bug #666: Looking up/down problem Bug #667: Active effects border visible during loading Bug #669: incorrect player position at new game start Bug #670: race selection menu: sex, face and hair left button not totally clickable Bug #671: new game: player is naked Bug #674: buying or selling items doesn't change amount of gold Bug #675: fatigue is not set to its maximum when starting a new game Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly Bug #680: different gold coins in Tel Mara Bug #682: Race menu ignores playable flag for some hairs and faces Bug #685: Script compiler does not accept ":" after a function name Bug #688: dispose corpse makes cross-hair to disappear Bug #691: Auto equipping ignores equipment conditions Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder Bug #696: Draugr incorrect head offset Bug #697: Sail transparency issue Bug #700: "On the rocks" mod does not load its UV coordinates correctly. Bug #702: Some race mods don't work Bug #711: Crash during character creation Bug #715: Growing Tauryon Bug #725: Auto calculate stats Bug #728: Failure to open container and talk dialogue Bug #731: Crash with Mush-Mere's "background" topic Feature #55/657: Item Repairing Feature #62/87: Enchanting Feature #99: Pathfinding Feature #104: AI Package: Travel Feature #129: Levelled items Feature #204: Texture animations Feature #239: Fallback-Settings Feature #535: Console object selection improvements Feature #629: Add levelup description in levelup layout dialog Feature #630: Optional format subrecord in (tes3) header Feature #641: Armor rating Feature #645: OnDeath script function Feature #683: Companion item UI Feature #698: Basic Particles Task #648: Split up components/esm/loadlocks Task #695: mwgui cleanup 0.22.0 ------ Bug #311: Potential infinite recursion in script compiler Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. Bug #382: Weird effect in 3rd person on water Bug #387: Always use detailed shape for physics raycasts Bug #420: Potion/ingredient effects do not stack Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips Bug #434/Bug #605: Object movement between cells not properly implemented Bug #502: Duplicate player collision model at origin Bug #509: Dialogue topic list shifts inappropriately Bug #513: Sliding stairs Bug #515: Launcher does not support non-latin strings Bug #525: Race selection preview camera wrong position Bug #526: Attributes / skills should not go below zero Bug #529: Class and Birthsign menus options should be preselected Bug #530: Lock window button graphic missing Bug #532: Missing map menu graphics Bug #545: ESX selector does not list ESM files properly Bug #547: Global variables of type short are read incorrectly Bug #550: Invisible meshes collision and tooltip Bug #551: Performance drop when loading multiple ESM files Bug #552: Don't list CG in options if it is not available Bug #555: Character creation windows "OK" button broken Bug #558: Segmentation fault when Alt-tabbing with console opened Bug #559: Dialog window should not be available before character creation is finished Bug #560: Tooltip borders should be stretched Bug #562: Sound should not be played when an object cannot be picked up Bug #565: Water animation speed + timescale Bug #572: Better Bodies' textures don't work Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) Bug #574: Moving left/right should not cancel auto-run Bug #575: Crash entering the Chamber of Song Bug #576: Missing includes Bug #577: Left Gloves Addon causes ESMReader exception Bug #579: Unable to open container "Kvama Egg Sack" Bug #581: Mimicking vanilla Morrowind water Bug #583: Gender not recognized Bug #586: Wrong char gen behaviour Bug #587: "End" script statements with spaces don't work Bug #589: Closing message boxes by pressing the activation key Bug #590: Ugly Dagoth Ur rendering Bug #591: Race selection issues Bug #593: Persuasion response should be random Bug #595: Footless guard Bug #599: Waterfalls are invisible from a certain distance Bug #600: Waterfalls rendered incorrectly, cut off by water Bug #607: New beast bodies mod crashes Bug #608: Crash in cell "Mournhold, Royal Palace" Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt Bug #613: Messagebox causing assert to fail Bug #615: Meshes invisible from above water Bug #617: Potion effects should be hidden until discovered Bug #619: certain moss hanging from tree has rendering bug Bug #621: Batching bloodmoon's trees Bug #623: NiMaterialProperty alpha unhandled Bug #628: Launcher in latest master crashes the game Bug #633: Crash on startup: Better Heads Bug #636: Incorrect Char Gen Menu Behavior Feature #29: Allow ESPs and multiple ESMs Feature #94: Finish class selection-dialogue Feature #149: Texture Alphas Feature #237: Run Morrowind-ini importer from launcher Feature #286: Update Active Spell Icons Feature #334: Swimming animation Feature #335: Walking animation Feature #360: Proper collision shapes for NPCs and creatures Feature #367: Lights that behave more like original morrowind implementation Feature #477: Special local scripting variables Feature #528: Message boxes should close when enter is pressed under certain conditions. Feature #543: Add bsa files to the settings imported by the ini importer Feature #594: coordinate space and utility functions Feature #625: Zoom in vanity mode Task #464: Refactor launcher ESX selector into a re-usable component Task #624: Unified implementation of type-variable sub-records 0.21.0 ------ Bug #253: Dialogs don't work for Russian version of Morrowind Bug #267: Activating creatures without dialogue can still activate the dialogue GUI Bug #354: True flickering lights Bug #386: The main menu's first entry is wrong (in french) Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations Bug #495: Activation Range Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available Bug #500: Disposition for most NPCs is 0/100 Bug #501: Getdisposition command wrongly returns base disposition Bug #506: Journal UI doesn't update anymore Bug #507: EnableRestMenu is not a valid command - change it to EnableRest Bug #508: Crash in Ald Daedroth Shrine Bug #517: Wrong price calculation when untrading an item Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin Bug #524: Beast races are able to wear shoes Bug #527: Background music fails to play Bug #533: The arch at Gnisis entrance is not displayed Bug #534: Terrain gets its correct shape only some time after the cell is loaded Bug #536: The same entry can be added multiple times to the journal Bug #539: Race selection is broken Bug #544: Terrain normal map corrupt when the map is rendered Feature #39: Video Playback Feature #151: ^-escape sequences in text output Feature #392: Add AI related script functions Feature #456: Determine required ini fallback values and adjust the ini importer accordingly Feature #460: Experimental DirArchives improvements Feature #540: Execute scripts of objects in containers/inventories in active cells Task #401: Review GMST fixing Task #453: Unify case smashing/folding Task #512: Rewrite utf8 component 0.20.0 ------ Bug #366: Changing the player's race during character creation does not change the look of the player character Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell Bug #437: Stop animations when paused Bug #438: Time displays as "0 a.m." when it should be "12 a.m." Bug #439: Text in "name" field of potion/spell creation window is persistent Bug #440: Starting date at a new game is off by one day Bug #442: Console window doesn't close properly sometimes Bug #448: Do not break container window formatting when item names are very long Bug #458: Topics sometimes not automatically added to known topic list Bug #476: Auto-Moving allows player movement after using DisablePlayerControls Bug #478: After sleeping in a bed the rest dialogue window opens automtically again Bug #492: On creating potions the ingredients are removed twice Feature #63: Mercantile skill Feature #82: Persuasion Dialogue Feature #219: Missing dialogue filters/functions Feature #369: Add a FailedAction Feature #377: Select head/hair on character creation Feature #391: Dummy AI package classes Feature #435: Global Map, 2nd Layer Feature #450: Persuasion Feature #457: Add more script instructions Feature #474: update the global variable pcrace when the player's race is changed Task #158: Move dynamically generated classes from Player class to World Class Task #159: ESMStore rework and cleanup Task #163: More Component Namespace Cleanup Task #402: Move player data from MWWorld::Player to the player's NPC record Task #446: Fix no namespace in BulletShapeLoader 0.19.0 ------ Bug #374: Character shakes in 3rd person mode near the origin Bug #404: Gamma correct rendering Bug #407: Shoes of St. Rilm do not work Bug #408: Rugs has collision even if they are not supposed to Bug #412: Birthsign menu sorted incorrectly Bug #413: Resolutions presented multiple times in launcher Bug #414: launcher.cfg file stored in wrong directory Bug #415: Wrong esm order in openmw.cfg Bug #418: Sound listener position updates incorrectly Bug #423: wrong usage of "Version" entry in openmw.desktop Bug #426: Do not use hardcoded splash images Bug #431: Don't use markers for raycast Bug #432: Crash after picking up items from an NPC Feature #21/#95: Sleeping/resting Feature #61: Alchemy Skill Feature #68: Death Feature #69/#86: Spell Creation Feature #72/#84: Travel Feature #76: Global Map, 1st Layer Feature #120: Trainer Window Feature #152: Skill Increase from Skill Books Feature #160: Record Saving Task #400: Review GMST access 0.18.0 ------ Bug #310: Button of the "preferences menu" are too small Bug #361: Hand-to-hand skill is always 100 Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to Bug #372: playSound3D uses original coordinates instead of current coordinates. Bug #373: Static OGRE build faulty Bug #375: Alt-tab toggle view Bug #376: Screenshots are disable Bug #378: Exception when drinking self-made potions Bug #380: Cloth visibility problem Bug #384: Weird character on doors tooltip. Bug #398: Some objects do not collide in MW, but do so in OpenMW Feature #22: Implement level-up Feature #36: Hide Marker Feature #88: Hotkey Window Feature #91: Level-Up Dialogue Feature #118: Keyboard and Mouse-Button bindings Feature #119: Spell Buying Window Feature #133: Handle resources across multiple data directories Feature #134: Generate a suitable default-value for --data-local Feature #292: Object Movement/Creation Script Instructions Feature #340: AIPackage data structures Feature #356: Ingredients use Feature #358: Input system rewrite Feature #370: Target handling in actions Feature #379: Door markers on the local map Feature #389: AI framework Feature #395: Using keys to open doors / containers Feature #396: Loading screens Feature #397: Inventory avatar image and race selection head preview Task #339: Move sounds into Action 0.17.0 ------ Bug #225: Valgrind reports about 40MB of leaked memory Bug #241: Some physics meshes still don't match Bug #248: Some textures are too dark Bug #300: Dependency on proprietary CG toolkit Bug #302: Some objects don't collide although they should Bug #308: Freeze in Balmora, Meldor: Armorer Bug #313: openmw without a ~/.config/openmw folder segfault. Bug #317: adding non-existing spell via console locks game Bug #318: Wrong character normals Bug #341: Building with Ogre Debug libraries does not use debug version of plugins Bug #347: Crash when running openmw with --start="XYZ" Bug #353: FindMyGUI.cmake breaks path on Windows Bug #359: WindowManager throws exception at destruction Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation Feature #33: Allow objects to cross cell-borders Feature #59: Dropping Items (replaced stopgap implementation with a proper one) Feature #93: Main Menu Feature #96/329/330/331/332/333: Player Control Feature #180: Object rotation and scaling. Feature #272: Incorrect NIF material sharing Feature #314: Potion usage Feature #324: Skill Gain Feature #342: Drain/fortify dynamic stats/attributes magic effects Feature #350: Allow console only script instructions Feature #352: Run scripts in console on startup Task #107: Refactor mw*-subsystems Task #325: Make CreatureStats into a class Task #345: Use Ogre's animation system Task #351: Rewrite Action class to support automatic sound playing 0.16.0 ------ Bug #250: OpenMW launcher erratic behaviour Bug #270: Crash because of underwater effect on OS X Bug #277: Auto-equipping in some cells not working Bug #294: Container GUI ignores disabled inventory menu Bug #297: Stats review dialog shows all skills and attribute values as 0 Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses Bug #299: Crash in World::disable Bug #306: Non-existent ~/.config/openmw "crash" the launcher. Bug #307: False "Data Files" location make the launcher "crash" Feature #81: Spell Window Feature #85: Alchemy Window Feature #181: Support for x.y script syntax Feature #242: Weapon and Spell icons Feature #254: Ingame settings window Feature #293: Allow "stacking" game modes Feature #295: Class creation dialog tooltips Feature #296: Clicking on the HUD elements should show/hide the respective window Feature #301: Direction after using a Teleport Door Feature #303: Allow object selection in the console Feature #305: Allow the use of = as a synonym for == Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts Task #176: Restructure enabling/disabling of MW-references Task #283: Integrate ogre.cfg file in settings file Task #290: Auto-Close MW-reference related GUI windows 0.15.0 ------ Bug #5: Physics reimplementation (fixes various issues) Bug #258: Resizing arrow's background is not transparent Bug #268: Widening the stats window in X direction causes layout problems Bug #269: Topic pane in dialgoue window is too small for some longer topics Bug #271: Dialog choices are sorted incorrectly Bug #281: The single quote character is not rendered on dialog windows Bug #285: Terrain not handled properly in cells that are not predefined Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs Feature #15: Collision with Terrain Feature #17: Inventory-, Container- and Trade-Windows Feature #44: Floating Labels above Focussed Objects Feature #80: Tooltips Feature #83: Barter Dialogue Feature #90: Book and Scroll Windows Feature #156: Item Stacking in Containers Feature #213: Pulsating lights Feature #218: Feather & Burden Feature #256: Implement magic effect bookkeeping Feature #259: Add missing information to Stats window Feature #260: Correct case for dialogue topics Feature #280: GUI texture atlasing Feature #291: Ability to use GMST strings from GUI layout files Task #255: Make MWWorld::Environment into a singleton 0.14.0 ------ Bug #1: Meshes rendered with wrong orientation Bug #6/Task #220: Picking up small objects doesn't always work Bug #127: tcg doesn't work Bug #178: Compablity problems with Ogre 1.8.0 RC 1 Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI Bug #227: Terrain crashes when moving away from predefined cells Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces Bug #235: TGA texture loading problem Bug #246: wireframe mode does not work in water Feature #8/#232: Water Rendering Feature #13: Terrain Rendering Feature #37: Render Path Grid Feature #66: Factions Feature #77: Local Map Feature #78: Compass/Mini-Map Feature #97: Render Clothing/Armour Feature #121: Window Pinning Feature #205: Auto equip Feature #217: Contiainer should track changes to its content Feature #221: NPC Dialogue Window Enhancements Feature #233: Game settings manager Feature #240: Spell List and selected spell (no GUI yet) Feature #243: Draw State Task #113: Morrowind.ini Importer Task #215: Refactor the sound code Task #216: Update MyGUI 0.13.0 ------ Bug #145: Fixed sound problems after cell change Bug #179: Pressing space in console triggers activation Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux Bug #189: ASCII 16 character added to console on it's activation on Mac OS X Bug #190: Case Folding fails with music files Bug #192: Keypresses write Text into Console no matter which gui element is active Bug #196: Collision shapes out of place Bug #202: ESMTool doesn't not work with localised ESM files anymore Bug #203: Torch lights only visible on short distance Bug #207: Ogre.log not written Bug #209: Sounds do not play Bug #210: Ogre crash at Dren plantation Bug #214: Unsupported file format version Bug #222: Launcher is writing openmw.cfg file to wrong location Feature #9: NPC Dialogue Window Feature #16/42: New sky/weather implementation Feature #40: Fading Feature #48: NPC Dialogue System Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) Feature #161: Load REC_PGRD records Feature #195: Wireframe-mode Feature #198/199: Various sound effects Feature #206: Allow picking data path from launcher if non is set Task #108: Refactor window manager class Task #172: Sound Manager Cleanup Task #173: Create OpenEngine systems in the appropriate manager classes Task #184: Adjust MSVC and gcc warning levels Task #185: RefData rewrite Task #201: Workaround for transparency issues Task #208: silenced esm_reader.hpp warning 0.12.0 ------ Bug #154: FPS Drop Bug #169: Local scripts continue running if associated object is deleted Bug #174: OpenMW fails to start if the config directory doesn't exist Bug #187: Missing lighting Bug #188: Lights without a mesh are not rendered Bug #191: Taking screenshot causes crash when running installed Feature #28: Sort out the cell load problem Feature #31: Allow the player to move away from pre-defined cells Feature #35: Use alternate storage location for modified object position Feature #45: NPC animations Feature #46: Creature Animation Feature #89: Basic Journal Window Feature #110: Automatically pick up the path of existing MW-installations Feature #183: More FPS display settings Task #19: Refactor engine class Task #109/Feature #162: Automate Packaging Task #112: Catch exceptions thrown in input handling functions Task #128/#168: Cleanup Configuration File Handling Task #131: NPC Activation doesn't work properly Task #144: MWRender cleanup Task #155: cmake cleanup 0.11.1 ------ Bug #2: Resources loading doesn't work outside of bsa files Bug #3: GUI does not render non-English characters Bug #7: openmw.cfg location doesn't match Bug #124: The TCL alias for ToggleCollision is missing. Bug #125: Some command line options can't be used from a .cfg file Bug #126: Toggle-type script instructions are less verbose compared with original MW Bug #130: NPC-Record Loading fails for some NPCs Bug #167: Launcher sets invalid parameters in ogre config Feature #10: Journal Feature #12: Rendering Optimisations Feature #23: Change Launcher GUI to a tabbed interface Feature #24: Integrate the OGRE settings window into the launcher Feature #25: Determine openmw.cfg location (Launcher) Feature #26: Launcher Profiles Feature #79: MessageBox Feature #116: Tab-Completion in Console Feature #132: --data-local and multiple --data Feature #143: Non-Rendering Performance-Optimisations Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs Feature #157: Version Handling Task #14: Replace tabs with 4 spaces Task #18: Move components from global namespace into their own namespace Task #123: refactor header files in components/esm 0.10.0 ------ * NPC dialogue window (not functional yet) * Collisions with objects * Refactor the PlayerPos class * Adjust file locations * CMake files and test linking for Bullet * Replace Ogre raycasting test for activation with something more precise * Adjust player movement according to collision results * FPS display * Various Portability Improvements * Mac OS X support is back! 0.9.0 ----- * Exterior cells loading, unloading and management * Character Creation GUI * Character creation * Make cell names case insensitive when doing internal lookups * Music player * NPCs rendering 0.8.0 ----- * GUI * Complete and working script engine * In game console * Sky rendering * Sound and music * Tons of smaller stuff 0.7.0 ----- * This release is a complete rewrite in C++. * All D code has been culled, and all modules have been rewritten. * The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. 0.6.0 ----- * Coded a GUI system using MyGUI * Skinned MyGUI to look like Morrowind (work in progress) * Integrated the Monster script engine * Rewrote some functions into script code * Very early MyGUI < > Monster binding * Fixed Windows sound problems (replaced old openal32.dll) 0.5.0 ----- * Collision detection with Bullet * Experimental walk & fall character physics * New key bindings: * t toggle physics mode (walking, flying, ghost), * n night eye, brightens the scene * Fixed incompatability with DMD 1.032 and newer compilers * * (thanks to tomqyp) * Various minor changes and updates 0.4.0 ----- * Switched from Audiere to OpenAL * * (BIG thanks to Chris Robinson) * Added complete Makefile (again) as a alternative build tool * More realistic lighting (thanks again to Chris Robinson) * Various localization fixes tested with Russian and French versions * Temporary workaround for the Unicode issue: invalid UTF displayed as '?' * Added ns option to disable sound, for debugging * Various bug fixes * Cosmetic changes to placate gdc Wall 0.3.0 ----- * Built and tested on Windows XP * Partial support for FreeBSD (exceptions do not work) * You no longer have to download Monster separately * Made an alternative for building without DSSS (but DSSS still works) * Renamed main program from 'morro' to 'openmw' * Made the config system more robust * Added oc switch for showing Ogre config window on startup * Removed some config files, these are auto generated when missing. * Separated plugins.cfg into linux and windows versions. * Updated Makefile and sources for increased portability * confirmed to work against OIS 1.0.0 (Ubuntu repository package) 0.2.0 ----- * Compiles with gdc * Switched to DSSS for building D code * Includes the program esmtool 0.1.0 ----- first release openmw-openmw-0.38.0/CI/000077500000000000000000000000001264522266000147125ustar00rootroot00000000000000openmw-openmw-0.38.0/CI/before_install.linux.sh000077500000000000000000000026741264522266000214100ustar00rootroot00000000000000#!/bin/sh if [ "${ANALYZE}" ]; then if [ $(lsb_release -sc) = "precise" ]; then echo "yes" | sudo apt-add-repository ppa:ubuntu-toolchain-r/test fi echo "yes" | sudo add-apt-repository "deb http://llvm.org/apt/`lsb_release -sc`/ llvm-toolchain-`lsb_release -sc`-3.6 main" wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add - fi echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" echo "yes" | sudo apt-add-repository ppa:openmw/openmw echo "yes" | sudo apt-add-repository ppa:boost-latest/ppa sudo apt-get update -qq sudo apt-get install -qq libgtest-dev google-mock sudo apt-get install -qq libboost-filesystem1.55-dev libboost-program-options1.55-dev libboost-system1.55-dev libboost-thread1.55-dev sudo apt-get install -qq ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev sudo apt-get install -qq cmake-data #workaround for broken osgqt cmake script in ubuntu 12.04 if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi sudo mkdir /usr/src/gtest/build cd /usr/src/gtest/build sudo cmake .. -DBUILD_SHARED_LIBS=1 sudo make -j4 sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so openmw-openmw-0.38.0/CI/before_install.osx.sh000077500000000000000000000003001264522266000210420ustar00rootroot00000000000000#!/bin/sh export CXX=clang++ export CC=clang brew tap openmw/openmw brew update brew unlink boost brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg openmw/openmw/qt unshield openmw-openmw-0.38.0/CI/before_script.linux.sh000077500000000000000000000004631264522266000212400ustar00rootroot00000000000000#!/bin/sh mkdir build cd build export CODE_COVERAGE=1 if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi ${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE openmw-openmw-0.38.0/CI/before_script.osx.sh000077500000000000000000000004061264522266000207070ustar00rootroot00000000000000#!/bin/sh mkdir build cd build cmake -DCMAKE_FRAMEWORK_PATH="/usr/local/lib/macosx/Release" -DCMAKE_EXE_LINKER_FLAGS="-F/usr/local/lib/macosx/Release" -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" -DCMAKE_BUILD_TYPE=Debug -DBUILD_MYGUI_PLUGIN=OFF -G"Unix Makefiles" .. openmw-openmw-0.38.0/CI/check_tabs.sh000077500000000000000000000003011264522266000173310ustar00rootroot00000000000000#!/bin/bash OUTPUT=$(grep -nRP '\t' --include=\*.{cpp,hpp,c,h} --exclude=ui_\* apps components) if [[ $OUTPUT ]] ; then echo "Error: Tab characters found!" echo $OUTPUT exit 1 fi openmw-openmw-0.38.0/CMakeLists.txt000066400000000000000000001047241264522266000171670ustar00rootroot00000000000000project(OpenMW) # If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them. IF(NOT 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." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS None Debug Release RelWithDebInfo MinSizeRel) ENDIF() if (APPLE) set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") endif (APPLE) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) # Version message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 38) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_TAGHASH "") set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") set(GIT_CHECKOUT FALSE) if(EXISTS ${PROJECT_SOURCE_DIR}/.git) if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) find_package(Git) if(GIT_FOUND) set(GIT_CHECKOUT TRUE) else(GIT_FOUND) message(WARNING "Git executable not found") endif(GIT_FOUND) else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) message(STATUS "Shallow Git clone detected, not attempting to retrieve version info") endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) # Macros include(OpenMWMacros) # doxygen main page configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_BINARY_DIR}/docs/mainpage.hpp") option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE) option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE) option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE) option(OSG_STATIC "Link static build of OpenSceneGraph into the binaries" FALSE) option(QT_STATIC "Link static build of QT into the binaries" FALSE) option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE) # Apps and tools option(BUILD_OPENMW "build OpenMW" ON) option(BUILD_BSATOOL "build BSA extractor" ON) option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_LAUNCHER "build Launcher" ON) option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON) option(BUILD_OPENCS "build OpenMW Construction Set" ON) option(BUILD_WIZARD "build Installation Wizard" ON) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF) option(BUILD_NIFTEST "build nif file tester" OFF) option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON) # OS X deployment option(OPENMW_OSX_DEPLOYMENT OFF) if (MSVC) option(OPENMW_MP_BUILD "Build OpenMW with /MP flag" OFF) option(OPENMW_LTO_BUILD "Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)" OFF) endif() # Set up common paths if (APPLE) set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") set(OPENMW_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files") elseif(UNIX) # Paths SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Where to install libraries") SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") SET(GLOBAL_DATA_PATH "${DATAROOTDIR}/games/" CACHE PATH "Set data path prefix") SET(DATADIR "${GLOBAL_DATA_PATH}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.") IF("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr") SET(GLOBAL_CONFIG_PATH "/etc/" CACHE PATH "Set config dir prefix") ELSE() SET(GLOBAL_CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/etc/" CACHE PATH "Set config dir prefix") ENDIF() SET(SYSCONFDIR "${GLOBAL_CONFIG_PATH}/openmw" CACHE PATH "Set config dir") set(MORROWIND_DATA_FILES "${DATADIR}/data" CACHE PATH "location of Morrowind data files") set(OPENMW_RESOURCE_FILES "${DATADIR}/resources" CACHE PATH "location of OpenMW resources files") else() set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files") set(OPENMW_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files") endif(APPLE) if (WIN32) option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON) endif() # We probably support older versions than this. cmake_minimum_required(VERSION 2.6) # Sound setup unset(FFMPEG_LIBRARIES CACHE) find_package(FFmpeg REQUIRED) set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY} ${SWRESAMPLE_LIBRARIES}) if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND OR NOT SWRESAMPLE_FOUND) message(FATAL_ERROR "FFmpeg component required, but not found!") endif() # Required for building the FFmpeg headers add_definitions(-D__STDC_CONSTANT_MACROS) # TinyXML option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) if(USE_SYSTEM_TINYXML) find_library(TINYXML_LIBRARIES tinyxml) find_path(TINYXML_INCLUDE_DIR tinyxml.h) message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}") add_definitions (-DTIXML_USE_STL) if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR) include_directories(${TINYXML_INCLUDE_DIR}) message(STATUS "Using system TinyXML library.") else() message(FATAL_ERROR "Detection of system TinyXML incomplete.") endif() endif() # Platform specific if (WIN32) if(NOT MINGW) set(Boost_USE_STATIC_LIBS ON) add_definitions(-DBOOST_ALL_NO_LIB) endif(NOT MINGW) # Suppress WinMain(), provided by SDL add_definitions(-DSDL_MAIN_HANDLED) # Get rid of useless crud from windows.h add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) endif() if (ANDROID) set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}") set(OPENGL_ES TRUE CACHE BOOL "enable opengl es support for android" FORCE) endif (ANDROID) option(OPENGL_ES "enable opengl es support" FALSE ) if (OPENGL_ES) add_definitions(-DOPENGL_ES) endif(OPENGL_ES) if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD) set(USE_QT FALSE) else() set(USE_QT TRUE) endif() # Dependencies if (USE_QT) set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)") set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5) message(STATUS "Using Qt${DESIRED_QT_VERSION}") if (DESIRED_QT_VERSION MATCHES 4) find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL) else() find_package(Qt5Widgets REQUIRED) find_package(Qt5Core REQUIRED) find_package(Qt5Network REQUIRED) find_package(Qt5OpenGL REQUIRED) # Instruct CMake to run moc automatically when needed. #set(CMAKE_AUTOMOC ON) endif() endif() # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) find_package (Threads) endif() # Look for stdint.h include(CheckIncludeFile) check_include_file(stdint.h HAVE_STDINT_H) if(NOT HAVE_STDINT_H) unset(HAVE_STDINT_H CACHE) message(FATAL_ERROR "stdint.h was not found" ) endif() include (CheckIncludeFileCXX) check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP) if (HAVE_UNORDERED_MAP) add_definitions(-DHAVE_UNORDERED_MAP) endif () set(BOOST_COMPONENTS system filesystem program_options thread) if(WIN32) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) endif(WIN32) IF(BOOST_STATIC) set(Boost_USE_STATIC_LIBS ON) endif() if (USE_QT) set (OSG_QT osgQt) endif() find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle ${OSG_QT} osgUtil osgFX) include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) if(OSG_STATIC) macro(use_static_osg_plugin_library PLUGIN_NAME) set(PLUGIN_NAME_DBG ${PLUGIN_NAME}d ${PLUGIN_NAME}D ${PLUGIN_NAME}_d ${PLUGIN_NAME}_D ${PLUGIN_NAME}_debug ${PLUGIN_NAME}) # For now, users wishing to do a static build will need to pass the path to where the plugins reside # More clever logic would need to deduce the path, probably installed under /lib/osgPlugins- find_library(${PLUGIN_NAME}_LIBRARY_REL NAMES ${PLUGIN_NAME} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH}) find_library(${PLUGIN_NAME}_LIBRARY_DBG NAMES ${PLUGIN_NAME_DBG} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH}) make_library_set(${PLUGIN_NAME}_LIBRARY) if("${${PLUGIN_NAME}_LIBRARY}" STREQUAL "") message(FATAL_ERROR "Unable to find static OpenSceneGraph plugin: ${PLUGIN_NAME}") endif() set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${PLUGIN_NAME}_LIBRARY}) endmacro() macro(use_static_osg_plugin_dep DEPENDENCY) find_package(${DEPENDENCY} REQUIRED) set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${DEPENDENCY}_LIBRARIES}) endmacro() add_definitions(-DOSG_LIBRARY_STATIC) set(PLUGIN_LIST osgdb_png # depends on libpng, zlib osgdb_tga osgdb_dds osgdb_jpeg # depends on libjpeg ) foreach(PLUGIN ${PLUGIN_LIST}) use_static_osg_plugin_library(${PLUGIN}) endforeach() # OSG static plugins need to linked against their respective dependencies set(PLUGIN_DEPS_LIST PNG # needed by osgdb_png ZLIB # needed by osgdb_png JPEG # needed by osgdb_jpeg ) foreach(DEPENDENCY ${PLUGIN_DEPS_LIST}) use_static_osg_plugin_dep(${DEPENDENCY}) endforeach() endif() if(QT_STATIC) if(WIN32) if(DESIRED_QT_VERSION MATCHES 4) # QtCore needs WSAAsyncSelect from Ws2_32.lib set(QT_QTCORE_LIBRARY ${QT_QTCORE_LIBRARY} Ws2_32.lib) message("QT_QTCORE_LIBRARY: ${QT_QTCORE_LIBRARY}") endif() endif() endif() find_package(MyGUI REQUIRED) if (${MYGUI_VERSION} VERSION_LESS "3.2.1") message(FATAL_ERROR "OpenMW requires MyGUI 3.2.1 or later, please install the latest version from http://mygui.info") endif() find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(SDL2 REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) include_directories("." SYSTEM ${SDL2_INCLUDE_DIR} ${Boost_INCLUDE_DIR} ${MYGUI_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} ${BULLET_INCLUDE_DIRS} ) link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${MYGUI_LIB_DIR}) if(MYGUI_STATIC) add_definitions(-DMYGUI_STATIC) endif (MYGUI_STATIC) if (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw-Info.plist.in "${APP_BUNDLE_DIR}/Contents/Info.plist") configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) endif (APPLE) # Set up DEBUG define set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1) add_subdirectory(files/) # Specify build paths if (APPLE) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS") if (OPENMW_OSX_DEPLOYMENT) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) endif() else (APPLE) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}") endif (APPLE) # Other files configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg "${OpenMW_BINARY_DIR}/settings-default.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local "${OpenMW_BINARY_DIR}/openmw.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg "${OpenMW_BINARY_DIR}/openmw.cfg.install") configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg "${OpenMW_BINARY_DIR}/openmw-cs.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) configure_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt") if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop "${OpenMW_BINARY_DIR}/openmw.desktop") configure_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml "${OpenMW_BINARY_DIR}/openmw.appdata.xml") configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop "${OpenMW_BINARY_DIR}/openmw-cs.desktop") endif() # CXX Compiler settings if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long") if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE) execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION) string(REGEX REPLACE ".*version ([0-9\\.]*).*" "\\1" CLANG_VERSION ${CLANG_VERSION}) if ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression") endif ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6) endif(CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter") endif(CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6) elseif (MSVC) # Enable link-time code generation globally for all linking if (OPENMW_LTO_BUILD) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG") set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} /LTCG") endif() set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE") endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) IF(NOT WIN32 AND NOT APPLE) # Linux installation # Install binaries IF(BUILD_OPENMW) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) ENDIF(BUILD_OPENMW) IF(BUILD_LAUNCHER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" ) ENDIF(BUILD_LAUNCHER) IF(BUILD_BSATOOL) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" ) ENDIF(BUILD_BSATOOL) IF(BUILD_ESMTOOL) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) ENDIF(BUILD_ESMTOOL) IF(BUILD_NIFTEST) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" ) ENDIF(BUILD_NIFTEST) IF(BUILD_MWINIIMPORTER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" ) ENDIF(BUILD_MWINIIMPORTER) IF(BUILD_ESSIMPORTER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-essimporter" DESTINATION "${BINDIR}" ) ENDIF(BUILD_ESSIMPORTER) IF(BUILD_OPENCS) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-cs" DESTINATION "${BINDIR}" ) ENDIF(BUILD_OPENCS) IF(BUILD_WIZARD) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-wizard" DESTINATION "${BINDIR}" ) ENDIF(BUILD_WIZARD) #if(BUILD_MYGUI_PLUGIN) # INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Plugin_MyGUI_OpenMW_Resources.so" DESTINATION "${LIBDIR}" ) #ENDIF(BUILD_MYGUI_PLUGIN) # Install licenses INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" ) # Install icon and desktop file INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw") INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/appdata" COMPONENT "openmw") IF(BUILD_OPENCS) INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs") INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs") ENDIF(BUILD_OPENCS) # Install global configuration files INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") IF(BUILD_OPENCS) INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs") ENDIF(BUILD_OPENCS) # Install resources INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" COMPONENT "Resources") INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources") ENDIF(NOT WIN32 AND NOT APPLE) if(WIN32) FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll") INSTALL(FILES ${dll_files} DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt" "${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt" "${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" "${OpenMW_BINARY_DIR}/Release/openmw.exe" DESTINATION ".") IF(BUILD_LAUNCHER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-launcher.exe" DESTINATION ".") ENDIF(BUILD_LAUNCHER) IF(BUILD_MWINIIMPORTER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-iniimporter.exe" DESTINATION ".") ENDIF(BUILD_MWINIIMPORTER) IF(BUILD_ESSIMPORTER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".") ENDIF(BUILD_ESSIMPORTER) IF(BUILD_OPENCS) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".") ENDIF(BUILD_OPENCS) IF(BUILD_WIZARD) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-wizard.exe" DESTINATION ".") ENDIF(BUILD_WIZARD) if(BUILD_MYGUI_PLUGIN) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION ".") ENDIF(BUILD_MYGUI_PLUGIN) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") FILE(GLOB plugin_dir "${OpenMW_BINARY_DIR}/Release/osgPlugins-*") INSTALL(DIRECTORY ${plugin_dir} DESTINATION ".") SET(CPACK_GENERATOR "NSIS") SET(CPACK_PACKAGE_NAME "OpenMW") SET(CPACK_PACKAGE_VENDOR "OpenMW.org") SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW") IF(BUILD_LAUNCHER) SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher") ENDIF(BUILD_LAUNCHER) IF(BUILD_OPENCS) SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set") ENDIF(BUILD_OPENCS) IF(BUILD_WIZARD) SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-wizard;OpenMW Wizard") ENDIF(BUILD_WIZARD) SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\README.txt'") SET(CPACK_NSIS_DELETE_ICONS_EXTRA " !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP Delete \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Readme.lnk\\\" ") SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/README.md") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/README.md") SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}") SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe") SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico") SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico") SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp") SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe") if(EXISTS ${VCREDIST32}) INSTALL(FILES ${VCREDIST32} DESTINATION "redist") SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q'" ) endif(EXISTS ${VCREDIST32}) SET(VCREDIST64 "${OpenMW_BINARY_DIR}/vcredist_x64.exe") if(EXISTS ${VCREDIST64}) INSTALL(FILES ${VCREDIST64} DESTINATION "redist") SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q'" ) endif(EXISTS ${VCREDIST64}) SET(OALREDIST "${OpenMW_BINARY_DIR}/oalinst.exe") if(EXISTS ${OALREDIST}) INSTALL(FILES ${OALREDIST} DESTINATION "redist") SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} ExecWait '\\\"$INSTDIR\\\\redist\\\\oalinst.exe\\\" /s'" ) endif(EXISTS ${OALREDIST}) if(CMAKE_CL_64) SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") endif() include(CPack) endif(WIN32) # Extern add_subdirectory (extern/osg-ffmpeg-videoplayer) add_subdirectory (extern/oics) # Components add_subdirectory (components) # Plugins #if (BUILD_MYGUI_PLUGIN) # add_subdirectory(plugins/mygui_resource_plugin) #endif() # Apps and tools if (BUILD_OPENMW) add_subdirectory( apps/openmw ) endif() if (BUILD_BSATOOL) add_subdirectory( apps/bsatool ) endif() if (BUILD_ESMTOOL) add_subdirectory( apps/esmtool ) endif() if (BUILD_LAUNCHER) add_subdirectory( apps/launcher ) endif() if (BUILD_MWINIIMPORTER) add_subdirectory( apps/mwiniimporter ) endif() if (BUILD_ESSIMPORTER) add_subdirectory (apps/essimporter ) endif() if (BUILD_OPENCS) add_subdirectory (apps/opencs) endif() if (BUILD_WIZARD) add_subdirectory(apps/wizard) endif() if (BUILD_NIFTEST) add_subdirectory(apps/niftest) endif(BUILD_NIFTEST) # UnitTests if (BUILD_UNITTESTS) add_subdirectory( apps/openmw_test_suite ) endif() if (WIN32) if (MSVC) if (OPENMW_MP_BUILD) set( MT_BUILD "/MP") endif() foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} "$(SolutionDir)$(Configuration)" ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} "$(ProjectDir)$(Configuration)" ) endforeach( OUTPUTCONFIG ) if (USE_DEBUG_CONSOLE AND BUILD_OPENMW) set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE") set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE") elseif (BUILD_OPENMW) # Turn off debug console, debug output will be written to visual studio output instead set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS") set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS") endif() if (BUILD_OPENMW) # Release builds use the debug console set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE") set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE") set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE") endif() # Play a bit with the warning levels set(WARNINGS "/Wall") # Since windows can only disable specific warnings, not enable them set(WARNINGS_DISABLE # Warnings that aren't enabled normally and don't need to be enabled # They're unneeded and sometimes completely retarded warnings that /Wall enables # Not going to bother commenting them as they tend to warn on every standard library file 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946 # Warnings that are thrown on standard libraries and not OpenMW 4347 # Non-template function with same name and parameter count as template function 4365 # Variable signed/unsigned mismatch 4510 4512 # Unable to generate copy constructor/assignment operator as it's not public in the base 4706 # Assignment in conditional expression 4738 # Storing 32-bit float result in memory, possible loss of performance 4986 # Undocumented warning that occurs in the crtdbg.h file 4987 # nonstandard extension used (triggered by setjmp.h) 4996 # Function was declared deprecated # caused by boost 4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off) # OpenMW specific warnings 4099 # Type mismatch, declared class or struct is defined with other type 4100 # Unreferenced formal parameter (-Wunused-parameter) 4101 # Unreferenced local variable (-Wunused-variable) 4127 # Conditional expression is constant 4242 # Storing value in a variable of a smaller type, possible loss of data 4244 # Storing value of one type in variable of another (size_t in int, for example) 4245 # Signed/unsigned mismatch 4267 # Conversion from 'size_t' to 'int', possible loss of data 4305 # Truncating value (double to float, for example) 4309 # Variable overflow, trying to store 128 in a signed char for example 4351 # New behavior: elements of array 'array' will be default initialized (desired behavior) 4355 # Using 'this' in member initialization list 4505 # Unreferenced local function has been removed 4701 # Potentially uninitialized local variable used 4702 # Unreachable code 4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt ) foreach(d ${WARNINGS_DISABLE}) set(WARNINGS "${WARNINGS} /wd${d}") endforeach(d) set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") # oics uses tinyxml, which has an initialized but unused variable set_target_properties(oics PROPERTIES COMPILE_FLAGS "${WARNINGS} /wd4189 ${MT_BUILD}") set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") if (BUILD_BSATOOL) set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") endif() if (BUILD_ESMTOOL) set_target_properties(esmtool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") endif() if (BUILD_ESSIMPORTER) set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") endif() if (BUILD_LAUNCHER) set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") endif() if (BUILD_MWINIIMPORTER) set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") endif() if (BUILD_OPENCS) set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") endif() if (BUILD_OPENMW) set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") endif() if (BUILD_WIZARD) set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") endif() endif(MSVC) # TODO: At some point release builds should not use the console but rather write to a log file #set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") #set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") endif() # Apple bundling if (APPLE) get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE) get_filename_component(QT_COCOA_PLUGIN_DIR "${QT_COCOA_PLUGIN_PATH}" DIRECTORY) get_filename_component(QT_COCOA_PLUGIN_GROUP "${QT_COCOA_PLUGIN_DIR}" NAME) get_filename_component(QT_COCOA_PLUGIN_NAME "${QT_COCOA_PLUGIN_PATH}" NAME) configure_file("${QT_COCOA_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY) if (BUILD_OPENCS) get_property(OPENCS_BUNDLE_NAME_TMP TARGET openmw-cs PROPERTY OUTPUT_NAME) set(OPENCS_BUNDLE_NAME "${OPENCS_BUNDLE_NAME_TMP}.app") configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY) endif () set(INSTALL_SUBDIR OpenMW) install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}") install(CODE " set(BU_CHMOD_BUNDLE_ITEMS ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}) include(BundleUtilities) cmake_minimum_required(VERSION 3.1) " COMPONENT Runtime) set(ABSOLUTE_PLUGINS "") set(USED_OSG_PLUGINS osgdb_dds osgdb_jpeg osgdb_png osgdb_tga ) foreach (PLUGIN_NAME ${USED_OSG_PLUGINS}) set(PLUGIN_ABS "${OSG_PLUGIN_LIB_SEARCH_PATH}/${PLUGIN_NAME}.so") set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS}) endforeach () get_filename_component(OSG_PLUGIN_PREFIX_DIR "${OSG_PLUGIN_LIB_SEARCH_PATH}" NAME) # installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX}) # and returns list of install paths for all installed plugins function (install_plugins_for_bundle bundle_path plugins_var) set(RELATIVE_PLUGIN_INSTALL_BASE "${bundle_path}/Contents/PlugIns/${OSG_PLUGIN_PREFIX_DIR}") set(PLUGINS "") set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${RELATIVE_PLUGIN_INSTALL_BASE}") foreach (PLUGIN ${ABSOLUTE_PLUGINS}) get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME) get_filename_component(PLUGIN_RELATIVE_WE ${PLUGIN} NAME_WE) set(PLUGIN_DYLIB_IN_BUNDLE "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}") set(PLUGINS ${PLUGINS} "${PLUGIN_DYLIB_IN_BUNDLE}") install(CODE " copy_resolved_item_into_bundle(\"${PLUGIN}\" \"${PLUGIN_DYLIB_IN_BUNDLE}\") " COMPONENT Runtime) endforeach () set(${plugins_var} ${PLUGINS} PARENT_SCOPE) endfunction (install_plugins_for_bundle) install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS) install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS) set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") install(CODE " function(gp_item_default_embedded_path_override item default_embedded_path_var) if (\${item} MATCHES ${OSG_PLUGIN_PREFIX_DIR}) set(path \"@executable_path/../PlugIns/${OSG_PLUGIN_PREFIX_DIR}\") set(\${default_embedded_path_var} \"\${path}\" PARENT_SCOPE) endif() endfunction() cmake_policy(SET CMP0009 OLD) fixup_bundle(\"${INSTALLED_OPENMW_APP}\" \"${PLUGINS}\" \"\") fixup_bundle(\"${INSTALLED_OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"\") " COMPONENT Runtime) include(CPack) endif (APPLE) # Doxygen Target -- simply run 'make doc' or 'make doc_pages' # output directory for 'make doc' is "${OpenMW_BINARY_DIR}/docs/Doxygen" # output directory for 'make doc_pages' is "${DOXYGEN_PAGES_OUTPUT_DIR}" if defined # or "${OpenMW_BINARY_DIR}/docs/Pages" otherwise find_package(Doxygen) if (DOXYGEN_FOUND) # determine output directory for doc_pages if (NOT DEFINED DOXYGEN_PAGES_OUTPUT_DIR) set(DOXYGEN_PAGES_OUTPUT_DIR "${OpenMW_BINARY_DIR}/docs/Pages") endif () configure_file(${OpenMW_SOURCE_DIR}/docs/Doxyfile.cmake ${OpenMW_BINARY_DIR}/docs/Doxyfile @ONLY) configure_file(${OpenMW_SOURCE_DIR}/docs/DoxyfilePages.cmake ${OpenMW_BINARY_DIR}/docs/DoxyfilePages @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${OpenMW_BINARY_DIR}/docs/Doxyfile WORKING_DIRECTORY ${OpenMW_BINARY_DIR} COMMENT "Generating Doxygen documentation at ${OpenMW_BINARY_DIR}/docs/Doxygen" VERBATIM) add_custom_target(doc_pages ${DOXYGEN_EXECUTABLE} ${OpenMW_BINARY_DIR}/docs/DoxyfilePages WORKING_DIRECTORY ${OpenMW_BINARY_DIR} COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM) endif () openmw-openmw-0.38.0/CONTRIBUTING.md000066400000000000000000000023271264522266000166540ustar00rootroot00000000000000Description =========== Your pull request description should include (if applicable): * A link back to the bug report or forum discussion that prompted the change * Summary of the changes made * Reasoning / motivation behind the change * What testing you have carried out to verify the change Other notes =========== * Separate your work into multiple pull requests whenever possible. As a rule of thumb, each feature and each bugfix should go into a separate PR, unless they are closely related or dependent upon each other. Small pull requests are easier to review, and are less likely to require further changes before we can merge them. A "mega" pull request with lots of unrelated commits in it is likely to get held up in review for a long time. * Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title. * If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards). openmw-openmw-0.38.0/README.md000066400000000000000000000164531264522266000157070ustar00rootroot00000000000000OpenMW ====== [![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) OpenMW is a recreation of the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set. * Version: 0.38.0 * License: GPL (see docs/license/GPL3.txt for more information) * Website: http://www.openmw.org * IRC: #openmw on irc.freenode.net Font Licenses: * DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information) Current Status -------------- The main quests in Morrowind, Tribunal and Bloodmoon are all completable. Some issues with side quests are to be expected (but rare). Check the [bug tracker](https://bugs.openmw.org/versions/21) for a list of issues we need to resolve before the "1.0" release. Even before the "1.0" release however, OpenMW boasts some new [features](https://wiki.openmw.org/index.php?title=Features), such as improved graphics and user interfaces. Pre-existing modifications created for the original Morrowind engine can be hit-and-miss. The OpenMW script compiler performs more thorough error-checking than Morrowind does, meaning that a mod created for Morrowind may not necessarily run in OpenMW. Some mods also rely on quirky behaviour or engine bugs in order to work. We are considering such compatibility issues on a case-by-case basis - in some cases adding a workaround to OpenMW may be feasible, in other cases fixing the mod will be the only option. If you know of any mods that work or don't work, feel free to add them to the [Mod status](https://wiki.openmw.org/index.php?title=Mod_status) wiki page. Getting Started --------------- * [Official forums](https://forum.openmw.org/) * [Installation instructions](https://wiki.openmw.org/index.php?title=Installation_Instructions) * [Build from source](https://wiki.openmw.org/index.php?title=Development_Environment_Setup) * [Testing the game](https://wiki.openmw.org/index.php?title=Testing) * [How to contribute](https://wiki.openmw.org/index.php?title=Contribution_Wanted) * [Report a bug](http://bugs.openmw.org/projects/openmw) - read the [guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) before submitting your first bug! * [Known issues](http://bugs.openmw.org/projects/openmw/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=%3D&v%5Bstatus_id%5D%5B%5D=7&f%5B%5D=tracker_id&op%5Btracker_id%5D=%3D&v%5Btracker_id%5D%5B%5D=1&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=priority&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=tracker) The data path ------------- The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install). Command line options -------------------- Syntax: openmw Allowed options: --help print help message --version print version information and quit --data arg (=data) set data directories (later directories have higher priority) --data-local arg set local data directory (highest priority) --fallback-archive arg (=fallback-archive) set fallback BSA archives (later archives have higher priority) --resources arg (=resources) set resources directory --start arg set initial cell --content arg content file(s): esm/esp, or omwgame/omwaddon --no-sound [=arg(=1)] (=0) disable all sounds --script-verbose [=arg(=1)] (=0) verbose script output --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup --script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup --script-console [=arg(=1)] (=0) enable console-only script functionality --script-run arg select a file containing a list of console commands that is executed on startup --script-warn [=arg(=1)] (=1) handling of warnings when compiling scripts 0 - ignore warning 1 - show warning but consider script as correctly compiled anyway 2 - treat warnings as errors --script-blacklist arg ignore the specified script (if the use of the blacklist is enabled) --script-blacklist-use [=arg(=1)] (=1) enable script blacklisting --load-savegame arg load a save game file on game startup (specify an absolute filename or a filename relative to the current working directory) --skip-menu [=arg(=1)] (=0) skip main menu on game startup --new-game [=arg(=1)] (=0) run new game sequence (ignored if skip-menu=0) --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) --encoding arg (=win1252) Character encoding used in OpenMW game messages: win1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages win1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages win1252 - Western European (Latin) alphabet, used by default --fallback arg fallback values --no-grab Don't grab mouse cursor --export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG image and XML file in current directory --activate-dist arg (=-1) activation distance override openmw-openmw-0.38.0/apps/000077500000000000000000000000001264522266000153625ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/bsatool/000077500000000000000000000000001264522266000170255ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/bsatool/CMakeLists.txt000066400000000000000000000005331264522266000215660ustar00rootroot00000000000000set(BSATOOL bsatool.cpp ) source_group(apps\\bsatool FILES ${BSATOOL}) # Main executable add_executable(bsatool ${BSATOOL} ) target_link_libraries(bsatool ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} components ) if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(bsatool gcov) endif() openmw-openmw-0.38.0/apps/bsatool/bsatool.cpp000066400000000000000000000211261264522266000211760ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #define BSATOOL_VERSION 1.1 // Create local aliases for brevity namespace bpo = boost::program_options; namespace bfs = boost::filesystem; struct Arguments { std::string mode; std::string filename; std::string extractfile; std::string outdir; bool longformat; bool fullpath; }; void replaceAll(std::string& str, const std::string& needle, const std::string& substitute) { size_t pos = str.find(needle); while(pos != std::string::npos) { str.replace(pos, needle.size(), substitute); pos = str.find(needle); } } bool parseOptions (int argc, char** argv, Arguments &info) { bpo::options_description desc("Inspect and extract files from Bethesda BSA archives\n\n" "Usages:\n" " bsatool list [-l] archivefile\n" " List the files presents in the input archive.\n\n" " bsatool extract [-f] archivefile [file_to_extract] [output_directory]\n" " Extract a file from the input archive.\n\n" " bsatool extractall archivefile [output_directory]\n" " Extract all files from the input archive.\n\n" "Allowed options"); desc.add_options() ("help,h", "print help message.") ("version,v", "print version information and quit.") ("long,l", "Include extra information in archive listing.") ("full-path,f", "Create directory hierarchy on file extraction " "(always true for extractall).") ; // input-file is hidden and used as a positional argument bpo::options_description hidden("Hidden Options"); hidden.add_options() ( "mode,m", bpo::value(), "bsatool mode") ( "input-file,i", bpo::value< std::vector >(), "input file") ; bpo::positional_options_description p; p.add("mode", 1).add("input-file", 3); // there might be a better way to do this bpo::options_description all; all.add(desc).add(hidden); bpo::variables_map variables; try { bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) .options(all).positional(p).run(); bpo::store(valid_opts, variables); } catch(std::exception &e) { std::cout << "ERROR parsing arguments: " << e.what() << "\n\n" << desc << std::endl; return false; } bpo::notify(variables); if (variables.count ("help")) { std::cout << desc << std::endl; return false; } if (variables.count ("version")) { std::cout << "BSATool version " << BSATOOL_VERSION << std::endl; return false; } if (!variables.count("mode")) { std::cout << "ERROR: no mode specified!\n\n" << desc << std::endl; return false; } info.mode = variables["mode"].as(); if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall")) { std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n" << desc << std::endl; return false; } if (!variables.count("input-file")) { std::cout << "\nERROR: missing BSA archive\n\n" << desc << std::endl; return false; } info.filename = variables["input-file"].as< std::vector >()[0]; // Default output to the working directory info.outdir = "."; if (info.mode == "extract") { if (variables["input-file"].as< std::vector >().size() < 2) { std::cout << "\nERROR: file to extract unspecified\n\n" << desc << std::endl; return false; } if (variables["input-file"].as< std::vector >().size() > 1) info.extractfile = variables["input-file"].as< std::vector >()[1]; if (variables["input-file"].as< std::vector >().size() > 2) info.outdir = variables["input-file"].as< std::vector >()[2]; } else if (variables["input-file"].as< std::vector >().size() > 1) info.outdir = variables["input-file"].as< std::vector >()[1]; info.longformat = variables.count("long") != 0; info.fullpath = variables.count("full-path") != 0; return true; } int list(Bsa::BSAFile& bsa, Arguments& info); int extract(Bsa::BSAFile& bsa, Arguments& info); int extractAll(Bsa::BSAFile& bsa, Arguments& info); int main(int argc, char** argv) { try { Arguments info; if(!parseOptions (argc, argv, info)) return 1; // Open file Bsa::BSAFile bsa; bsa.open(info.filename); if (info.mode == "list") return list(bsa, info); else if (info.mode == "extract") return extract(bsa, info); else if (info.mode == "extractall") return extractAll(bsa, info); else { std::cout << "Unsupported mode. That is not supposed to happen." << std::endl; return 1; } } catch (std::exception& e) { std::cerr << "ERROR reading BSA archive\nDetails:\n" << e.what() << std::endl; return 2; } } int list(Bsa::BSAFile& bsa, Arguments& info) { // List all files const Bsa::BSAFile::FileList &files = bsa.getList(); for(unsigned int i=0; irdbuf(); out.close(); return 0; } int extractAll(Bsa::BSAFile& bsa, Arguments& info) { // Get the list of files present in the archive Bsa::BSAFile::FileList list = bsa.getList(); // Iter on the list for(Bsa::BSAFile::FileList::iterator it = list.begin(); it != list.end(); ++it) { const char* archivePath = it->name; std::string extractPath (archivePath); replaceAll(extractPath, "\\", "/"); // Get the target path (the path the file will be extracted to) bfs::path target (info.outdir); target /= extractPath; // Create the directory hierarchy bfs::create_directories(target.parent_path()); bfs::file_status s = bfs::status(target.parent_path()); if (!bfs::is_directory(s)) { std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl; return 3; } // Get a stream for the file to extract // (inefficient because getFile iter on the list again) Files::IStreamPtr data = bsa.getFile(archivePath); bfs::ofstream out(target, std::ios::binary); // Write the file to disk std::cout << "Extracting " << target << std::endl; out << data->rdbuf(); out.close(); } return 0; } openmw-openmw-0.38.0/apps/doc.hpp000066400000000000000000000001151264522266000166350ustar00rootroot00000000000000// Note: This is not a regular source file. /// \defgroup apps Applications openmw-openmw-0.38.0/apps/esmtool/000077500000000000000000000000001264522266000170445ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/esmtool/.gitignore000066400000000000000000000000531264522266000210320ustar00rootroot00000000000000*.esp *.esm *.ess *_test esmtool *_raw.txt openmw-openmw-0.38.0/apps/esmtool/CMakeLists.txt000066400000000000000000000005631264522266000216100ustar00rootroot00000000000000set(ESMTOOL esmtool.cpp labels.hpp labels.cpp record.hpp record.cpp ) source_group(apps\\esmtool FILES ${ESMTOOL}) # Main executable add_executable(esmtool ${ESMTOOL} ) target_link_libraries(esmtool ${Boost_PROGRAM_OPTIONS_LIBRARY} components ) if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(esmtool gcov) endif() openmw-openmw-0.38.0/apps/esmtool/esmtool.cpp000066400000000000000000000437271264522266000212470ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "record.hpp" #define ESMTOOL_VERSION 1.2 // Create a local alias for brevity namespace bpo = boost::program_options; struct ESMData { std::string author; std::string description; unsigned int version; std::vector masters; std::deque mRecords; // Value: (Reference, Deleted flag) std::map > > mCellRefs; std::map mRecordStats; static const std::set sLabeledRec; }; static const int sLabeledRecIds[] = { ESM::REC_GLOB, ESM::REC_CLAS, ESM::REC_FACT, ESM::REC_RACE, ESM::REC_SOUN, ESM::REC_REGN, ESM::REC_BSGN, ESM::REC_LTEX, ESM::REC_STAT, ESM::REC_DOOR, ESM::REC_MISC, ESM::REC_WEAP, ESM::REC_CONT, ESM::REC_SPEL, ESM::REC_CREA, ESM::REC_BODY, ESM::REC_LIGH, ESM::REC_ENCH, ESM::REC_NPC_, ESM::REC_ARMO, ESM::REC_CLOT, ESM::REC_REPA, ESM::REC_ACTI, ESM::REC_APPA, ESM::REC_LOCK, ESM::REC_PROB, ESM::REC_INGR, ESM::REC_BOOK, ESM::REC_ALCH, ESM::REC_LEVI, ESM::REC_LEVC, ESM::REC_SNDG, ESM::REC_CELL, ESM::REC_DIAL }; const std::set ESMData::sLabeledRec = std::set(sLabeledRecIds, sLabeledRecIds + 34); // Based on the legacy struct struct Arguments { bool raw_given; bool quiet_given; bool loadcells_given; bool plain_given; std::string mode; std::string encoding; std::string filename; std::string outname; std::vector types; std::string name; ESMData data; ESM::ESMReader reader; ESM::ESMWriter writer; }; bool parseOptions (int argc, char** argv, Arguments &info) { bpo::options_description desc("Inspect and extract from Morrowind ES files (ESM, ESP, ESS)\nSyntax: esmtool [options] mode infile [outfile]\nAllowed modes:\n dump\t Dumps all readable data from the input file.\n clone\t Clones the input file to the output file.\n comp\t Compares the given files.\n\nAllowed options"); desc.add_options() ("help,h", "print help message.") ("version,v", "print version information and quit.") ("raw,r", "Show an unformatted list of all records and subrecords.") // The intention is that this option would interact better // with other modes including clone, dump, and raw. ("type,t", bpo::value< std::vector >(), "Show only records of this type (four character record code). May " "be specified multiple times. Only affects dump mode.") ("name,n", bpo::value(), "Show only the record with this name. Only affects dump mode.") ("plain,p", "Print contents of dialogs, books and scripts. " "(skipped by default)" "Only affects dump mode.") ("quiet,q", "Supress all record information. Useful for speed tests.") ("loadcells,C", "Browse through contents of all cells.") ( "encoding,e", bpo::value(&(info.encoding))-> default_value("win1252"), "Character encoding used in ESMTool:\n" "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" "\n\twin1252 - Western European (Latin) alphabet, used by default") ; std::string finalText = "\nIf no option is given, the default action is to parse all records in the archive\nand display diagnostic information."; // input-file is hidden and used as a positional argument bpo::options_description hidden("Hidden Options"); hidden.add_options() ( "mode,m", bpo::value(), "esmtool mode") ( "input-file,i", bpo::value< std::vector >(), "input file") ; bpo::positional_options_description p; p.add("mode", 1).add("input-file", 2); // there might be a better way to do this bpo::options_description all; all.add(desc).add(hidden); bpo::variables_map variables; try { bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) .options(all).positional(p).run(); bpo::store(valid_opts, variables); } catch(boost::program_options::unknown_option & x) { std::cerr << "ERROR: " << x.what() << std::endl; return false; } catch(boost::program_options::invalid_command_line_syntax & x) { std::cerr << "ERROR: " << x.what() << std::endl; return false; } bpo::notify(variables); if (variables.count ("help")) { std::cout << desc << finalText << std::endl; return false; } if (variables.count ("version")) { std::cout << "ESMTool version " << ESMTOOL_VERSION << std::endl; return false; } if (!variables.count("mode")) { std::cout << "No mode specified!" << std::endl << std::endl << desc << finalText << std::endl; return false; } if (variables.count("type") > 0) info.types = variables["type"].as< std::vector >(); if (variables.count("name") > 0) info.name = variables["name"].as(); info.mode = variables["mode"].as(); if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp")) { std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"" << std::endl << std::endl << desc << finalText << std::endl; return false; } if ( !variables.count("input-file") ) { std::cout << "\nERROR: missing ES file\n\n"; std::cout << desc << finalText << std::endl; return false; } // handling gracefully the user adding multiple files /* if (variables["input-file"].as< std::vector >().size() > 1) { std::cout << "\nERROR: more than one ES file specified\n\n"; std::cout << desc << finalText << std::endl; return false; }*/ info.filename = variables["input-file"].as< std::vector >()[0]; if (variables["input-file"].as< std::vector >().size() > 1) info.outname = variables["input-file"].as< std::vector >()[1]; info.raw_given = variables.count ("raw") != 0; info.quiet_given = variables.count ("quiet") != 0; info.loadcells_given = variables.count ("loadcells") != 0; info.plain_given = variables.count("plain") != 0; // Font encoding settings info.encoding = variables["encoding"].as(); if(info.encoding != "win1250" && info.encoding != "win1251" && info.encoding != "win1252") { std::cout << info.encoding << " is not a valid encoding option." << std::endl; info.encoding = "win1252"; } std::cout << ToUTF8::encodingUsingMessage(info.encoding) << std::endl; return true; } void printRaw(ESM::ESMReader &esm); void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info); int load(Arguments& info); int clone(Arguments& info); int comp(Arguments& info); int main(int argc, char**argv) { try { Arguments info; if(!parseOptions (argc, argv, info)) return 1; if (info.mode == "dump") return load(info); else if (info.mode == "clone") return clone(info); else if (info.mode == "comp") return comp(info); else { std::cout << "Invalid or no mode specified, dying horribly. Have a nice day." << std::endl; return 1; } } catch (std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; return 1; } return 0; } void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) { bool quiet = (info.quiet_given || info.mode == "clone"); bool save = (info.mode == "clone"); // Skip back to the beginning of the reference list // FIXME: Changes to the references backend required to support multiple plugins have // almost certainly broken this following line. I'll leave it as is for now, so that // the compiler does not complain. cell.restore(esm, 0); // Loop through all the references ESM::CellRef ref; if(!quiet) std::cout << " References:\n"; bool deleted = false; while(cell.getNextRef(esm, ref, deleted)) { if (save) { info.data.mCellRefs[&cell].push_back(std::make_pair(ref, deleted)); } if(quiet) continue; std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl; std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n"; std::cout << " Global: '" << ref.mGlobalVariable << "'" << std::endl; std::cout << " Faction: '" << ref.mFaction << "'" << std::endl; std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; std::cout << " Uses/health: '" << ref.mChargeInt << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Deleted: " << deleted << std::endl; if (!ref.mKey.empty()) std::cout << " Key: '" << ref.mKey << "'" << std::endl; } } void printRaw(ESM::ESMReader &esm) { while(esm.hasMoreRecs()) { ESM::NAME n = esm.getRecName(); std::cout << "Record: " << n.toString() << std::endl; esm.getRecHeader(); while(esm.hasMoreSubs()) { size_t offs = esm.getFileOffset(); esm.getSubName(); esm.skipHSub(); n = esm.retSubName(); std::ios::fmtflags f(std::cout.flags()); std::cout << " " << n.toString() << " - " << esm.getSubSize() << " bytes @ 0x" << std::hex << offs << "\n"; std::cout.flags(f); } } } int load(Arguments& info) { ESM::ESMReader& esm = info.reader; ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding)); esm.setEncoder(&encoder); std::string filename = info.filename; std::cout << "Loading file: " << filename << std::endl; std::list skipped; try { if(info.raw_given && info.mode == "dump") { std::cout << "RAW file listing:\n"; esm.openRaw(filename); printRaw(esm); return 0; } bool quiet = (info.quiet_given || info.mode == "clone"); bool loadCells = (info.loadcells_given || info.mode == "clone"); bool save = (info.mode == "clone"); esm.open(filename); info.data.author = esm.getAuthor(); info.data.description = esm.getDesc(); info.data.masters = esm.getGameFiles(); if (!quiet) { std::cout << "Author: " << esm.getAuthor() << std::endl << "Description: " << esm.getDesc() << std::endl << "File format version: " << esm.getFVer() << std::endl; std::vector m = esm.getGameFiles(); if (!m.empty()) { std::cout << "Masters:" << std::endl; for(unsigned int i=0;isetFlags(static_cast(flags)); record->setPrintPlain(info.plain_given); record->load(esm); // Is the user interested in this record type? bool interested = true; if (!info.types.empty()) { std::vector::iterator match; match = std::find(info.types.begin(), info.types.end(), n.toString()); if (match == info.types.end()) interested = false; } if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, record->getId())) interested = false; if(!quiet && interested) { std::cout << "\nRecord: " << n.toString() << " '" << record->getId() << "'\n"; record->print(); } if (record->getType().val == ESM::REC_CELL && loadCells && interested) { loadCell(record->cast()->get(), esm, info); } if (save) { info.data.mRecords.push_back(record); } else { delete record; } ++info.data.mRecordStats[n.val]; } } catch(std::exception &e) { std::cout << "\nERROR:\n\n " << e.what() << std::endl; typedef std::deque RecStore; RecStore &store = info.data.mRecords; for (RecStore::iterator it = store.begin(); it != store.end(); ++it) { delete *it; } store.clear(); return 1; } return 0; } #include int clone(Arguments& info) { if (info.outname.empty()) { std::cout << "You need to specify an output name" << std::endl; return 1; } if (load(info) != 0) { std::cout << "Failed to load, aborting." << std::endl; return 1; } size_t recordCount = info.data.mRecords.size(); int digitCount = 1; // For a nicer output if (recordCount > 9) ++digitCount; if (recordCount > 99) ++digitCount; if (recordCount > 999) ++digitCount; if (recordCount > 9999) ++digitCount; if (recordCount > 99999) ++digitCount; if (recordCount > 999999) ++digitCount; std::cout << "Loaded " << recordCount << " records:" << std::endl << std::endl; ESM::NAME name; int i = 0; typedef std::map Stats; Stats &stats = info.data.mRecordStats; for (Stats::iterator it = stats.begin(); it != stats.end(); ++it) { name.val = it->first; int amount = it->second; std::cout << std::setw(digitCount) << amount << " " << name.toString() << " "; if (++i % 3 == 0) std::cout << std::endl; } if (i % 3 != 0) std::cout << std::endl; std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl; ESM::ESMWriter& esm = info.writer; ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding)); esm.setEncoder(&encoder); esm.setAuthor(info.data.author); esm.setDescription(info.data.description); esm.setVersion(info.data.version); esm.setRecordCount (recordCount); for (std::vector::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it) esm.addMaster(it->name, it->size); std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary); esm.save(save); int saved = 0; typedef std::deque Records; Records &records = info.data.mRecords; for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it) { EsmTool::RecordBase *record = *it; name.val = record->getType().val; esm.startRecord(name.toString(), record->getFlags()); record->save(esm); if (name.val == ESM::REC_CELL) { ESM::Cell *ptr = &record->cast()->get(); if (!info.data.mCellRefs[ptr].empty()) { typedef std::deque > RefList; RefList &refs = info.data.mCellRefs[ptr]; for (RefList::iterator refIt = refs.begin(); refIt != refs.end(); ++refIt) { refIt->first.save(esm, refIt->second); } } } esm.endRecord(name.toString()); saved++; int perc = (int)((saved / (float)recordCount)*100); if (perc % 10 == 0) { std::cerr << "\r" << perc << "%"; } } std::cout << "\rDone!" << std::endl; esm.close(); save.close(); return 0; } int comp(Arguments& info) { if (info.filename.empty() || info.outname.empty()) { std::cout << "You need to specify two input files" << std::endl; return 1; } Arguments fileOne; Arguments fileTwo; fileOne.raw_given = 0; fileTwo.raw_given = 0; fileOne.mode = "clone"; fileTwo.mode = "clone"; fileOne.encoding = info.encoding; fileTwo.encoding = info.encoding; fileOne.filename = info.filename; fileTwo.filename = info.outname; if (load(fileOne) != 0) { std::cout << "Failed to load " << info.filename << ", aborting comparison." << std::endl; return 1; } if (load(fileTwo) != 0) { std::cout << "Failed to load " << info.outname << ", aborting comparison." << std::endl; return 1; } if (fileOne.data.mRecords.size() != fileTwo.data.mRecords.size()) { std::cout << "Not equal, different amount of records." << std::endl; return 1; } return 0; } openmw-openmw-0.38.0/apps/esmtool/labels.cpp000066400000000000000000000650571264522266000210270ustar00rootroot00000000000000#include "labels.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include std::string bodyPartLabel(int idx) { if (idx >= 0 && idx <= 26) { static const char *bodyPartLabels[] = { "Head", "Hair", "Neck", "Cuirass", "Groin", "Skirt", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield", "Right Forearm", "Left Forearm", "Right Upperarm", "Left Upperarm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Leg", "Left Leg", "Right Shoulder", "Left Shoulder", "Weapon", "Tail" }; return bodyPartLabels[idx]; } else return "Invalid"; } std::string meshPartLabel(int idx) { if (idx >= 0 && idx <= ESM::BodyPart::MP_Tail) { static const char *meshPartLabels[] = { "Head", "Hair", "Neck", "Chest", "Groin", "Hand", "Wrist", "Forearm", "Upperarm", "Foot", "Ankle", "Knee", "Upper Leg", "Clavicle", "Tail" }; return meshPartLabels[idx]; } else return "Invalid"; } std::string meshTypeLabel(int idx) { if (idx >= 0 && idx <= ESM::BodyPart::MT_Armor) { static const char *meshTypeLabels[] = { "Skin", "Clothing", "Armor" }; return meshTypeLabels[idx]; } else return "Invalid"; } std::string clothingTypeLabel(int idx) { if (idx >= 0 && idx <= 9) { static const char *clothingTypeLabels[] = { "Pants", "Shoes", "Shirt", "Belt", "Robe", "Right Glove", "Left Glove", "Skirt", "Ring", "Amulet" }; return clothingTypeLabels[idx]; } else return "Invalid"; } std::string armorTypeLabel(int idx) { if (idx >= 0 && idx <= 10) { static const char *armorTypeLabels[] = { "Helmet", "Cuirass", "Left Pauldron", "Right Pauldron", "Greaves", "Boots", "Left Gauntlet", "Right Gauntlet", "Shield", "Left Bracer", "Right Bracer" }; return armorTypeLabels[idx]; } else return "Invalid"; } std::string dialogTypeLabel(int idx) { if (idx >= 0 && idx <= 4) { static const char *dialogTypeLabels[] = { "Topic", "Voice", "Greeting", "Persuasion", "Journal" }; return dialogTypeLabels[idx]; } else if (idx == -1) return "Deleted"; else return "Invalid"; } std::string questStatusLabel(int idx) { if (idx >= 0 && idx <= 4) { static const char *questStatusLabels[] = { "None", "Name", "Finished", "Restart", "Deleted" }; return questStatusLabels[idx]; } else return "Invalid"; } std::string creatureTypeLabel(int idx) { if (idx >= 0 && idx <= 3) { static const char *creatureTypeLabels[] = { "Creature", "Daedra", "Undead", "Humanoid", }; return creatureTypeLabels[idx]; } else return "Invalid"; } std::string soundTypeLabel(int idx) { if (idx >= 0 && idx <= 7) { static const char *soundTypeLabels[] = { "Left Foot", "Right Foot", "Swim Left", "Swim Right", "Moan", "Roar", "Scream", "Land" }; return soundTypeLabels[idx]; } else return "Invalid"; } std::string weaponTypeLabel(int idx) { if (idx >= 0 && idx <= 13) { static const char *weaponTypeLabels[] = { "Short Blade One Hand", "Long Blade One Hand", "Long Blade Two Hand", "Blunt One Hand", "Blunt Two Close", "Blunt Two Wide", "Spear Two Wide", "Axe One Hand", "Axe Two Hand", "Marksman Bow", "Marksman Crossbow", "Marksman Thrown", "Arrow", "Bolt" }; return weaponTypeLabels[idx]; } else return "Invalid"; } std::string aiTypeLabel(int type) { if (type == ESM::AI_Wander) return "Wander"; else if (type == ESM::AI_Travel) return "Travel"; else if (type == ESM::AI_Follow) return "Follow"; else if (type == ESM::AI_Escort) return "Escort"; else if (type == ESM::AI_Activate) return "Activate"; else return "Invalid"; } std::string magicEffectLabel(int idx) { if (idx >= 0 && idx <= 142) { const char* magicEffectLabels [] = { "Water Breathing", "Swift Swim", "Water Walking", "Shield", "Fire Shield", "Lightning Shield", "Frost Shield", "Burden", "Feather", "Jump", "Levitate", "SlowFall", "Lock", "Open", "Fire Damage", "Shock Damage", "Frost Damage", "Drain Attribute", "Drain Health", "Drain Magicka", "Drain Fatigue", "Drain Skill", "Damage Attribute", "Damage Health", "Damage Magicka", "Damage Fatigue", "Damage Skill", "Poison", "Weakness to Fire", "Weakness to Frost", "Weakness to Shock", "Weakness to Magicka", "Weakness to Common Disease", "Weakness to Blight Disease", "Weakness to Corprus Disease", "Weakness to Poison", "Weakness to Normal Weapons", "Disintegrate Weapon", "Disintegrate Armor", "Invisibility", "Chameleon", "Light", "Sanctuary", "Night Eye", "Charm", "Paralyze", "Silence", "Blind", "Sound", "Calm Humanoid", "Calm Creature", "Frenzy Humanoid", "Frenzy Creature", "Demoralize Humanoid", "Demoralize Creature", "Rally Humanoid", "Rally Creature", "Dispel", "Soultrap", "Telekinesis", "Mark", "Recall", "Divine Intervention", "Almsivi Intervention", "Detect Animal", "Detect Enchantment", "Detect Key", "Spell Absorption", "Reflect", "Cure Common Disease", "Cure Blight Disease", "Cure Corprus Disease", "Cure Poison", "Cure Paralyzation", "Restore Attribute", "Restore Health", "Restore Magicka", "Restore Fatigue", "Restore Skill", "Fortify Attribute", "Fortify Health", "Fortify Magicka", "Fortify Fatigue", "Fortify Skill", "Fortify Maximum Magicka", "Absorb Attribute", "Absorb Health", "Absorb Magicka", "Absorb Fatigue", "Absorb Skill", "Resist Fire", "Resist Frost", "Resist Shock", "Resist Magicka", "Resist Common Disease", "Resist Blight Disease", "Resist Corprus Disease", "Resist Poison", "Resist Normal Weapons", "Resist Paralysis", "Remove Curse", "Turn Undead", "Summon Scamp", "Summon Clannfear", "Summon Daedroth", "Summon Dremora", "Summon Ancestral Ghost", "Summon Skeletal Minion", "Summon Bonewalker", "Summon Greater Bonewalker", "Summon Bonelord", "Summon Winged Twilight", "Summon Hunger", "Summon Golden Saint", "Summon Flame Atronach", "Summon Frost Atronach", "Summon Storm Atronach", "Fortify Attack", "Command Creature", "Command Humanoid", "Bound Dagger", "Bound Longsword", "Bound Mace", "Bound Battle Axe", "Bound Spear", "Bound Longbow", "EXTRA SPELL", "Bound Cuirass", "Bound Helm", "Bound Boots", "Bound Shield", "Bound Gloves", "Corprus", "Vampirism", "Summon Centurion Sphere", "Sun Damage", "Stunted Magicka", "Summon Fabricant", "sEffectSummonCreature01", "sEffectSummonCreature02", "sEffectSummonCreature03", "sEffectSummonCreature04", "sEffectSummonCreature05" }; return magicEffectLabels[idx]; } else return "Invalid"; } std::string attributeLabel(int idx) { if (idx >= 0 && idx <= 7) { const char* attributeLabels [] = { "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", "Luck" }; return attributeLabels[idx]; } else return "Invalid"; } std::string spellTypeLabel(int idx) { if (idx >= 0 && idx <= 5) { const char* spellTypeLabels [] = { "Spells", "Abilities", "Blight Disease", "Disease", "Curse", "Powers" }; return spellTypeLabels[idx]; } else return "Invalid"; } std::string specializationLabel(int idx) { if (idx >= 0 && idx <= 2) { const char* specializationLabels [] = { "Combat", "Magic", "Stealth" }; return specializationLabels[idx]; } else return "Invalid"; } std::string skillLabel(int idx) { if (idx >= 0 && idx <= 26) { const char* skillLabels [] = { "Block", "Armorer", "Medium Armor", "Heavy Armor", "Blunt Weapon", "Long Blade", "Axe", "Spear", "Athletics", "Enchant", "Destruction", "Alteration", "Illusion", "Conjuration", "Mysticism", "Restoration", "Alchemy", "Unarmored", "Security", "Sneak", "Acrobatics", "Light Armor", "Short Blade", "Marksman", "Mercantile", "Speechcraft", "Hand-to-hand" }; return skillLabels[idx]; } else return "Invalid"; } std::string apparatusTypeLabel(int idx) { if (idx >= 0 && idx <= 3) { const char* apparatusTypeLabels [] = { "Mortar", "Alembic", "Calcinator", "Retort", }; return apparatusTypeLabels[idx]; } else return "Invalid"; } std::string rangeTypeLabel(int idx) { if (idx >= 0 && idx <= 2) { const char* rangeTypeLabels [] = { "Self", "Touch", "Target" }; return rangeTypeLabels[idx]; } else return "Invalid"; } std::string schoolLabel(int idx) { if (idx >= 0 && idx <= 5) { const char* schoolLabels [] = { "Alteration", "Conjuration", "Destruction", "Illusion", "Mysticism", "Restoration" }; return schoolLabels[idx]; } else return "Invalid"; } std::string enchantTypeLabel(int idx) { if (idx >= 0 && idx <= 3) { const char* enchantTypeLabels [] = { "Cast Once", "Cast When Strikes", "Cast When Used", "Constant Effect" }; return enchantTypeLabels[idx]; } else return "Invalid"; } std::string ruleFunction(int idx) { if (idx >= 0 && idx <= 72) { std::string ruleFunctions[] = { "Reaction Low", "Reaction High", "Rank Requirement", "NPC? Reputation", "Health Percent", "Player Reputation", "NPC Level", "Player Health Percent", "Player Magicka", "Player Fatigue", "Player Attribute Strength", "Player Skill Block", "Player Skill Armorer", "Player Skill Medium Armor", "Player Skill Heavy Armor", "Player Skill Blunt Weapon", "Player Skill Long Blade", "Player Skill Axe", "Player Skill Spear", "Player Skill Athletics", "Player Skill Enchant", "Player Skill Destruction", "Player Skill Alteration", "Player Skill Illusion", "Player Skill Conjuration", "Player Skill Mysticism", "Player SKill Restoration", "Player Skill Alchemy", "Player Skill Unarmored", "Player Skill Security", "Player Skill Sneak", "Player Skill Acrobatics", "Player Skill Light Armor", "Player Skill Short Blade", "Player Skill Marksman", "Player Skill Mercantile", "Player Skill Speechcraft", "Player Skill Hand to Hand", "Player Gender", "Player Expelled from Faction", "Player Diseased (Common)", "Player Diseased (Blight)", "Player Clothing Modifier", "Player Crime Level", "Player Same Sex", "Player Same Race", "Player Same Faction", "Faction Rank Difference", "Player Detected", "Alarmed", "Choice Selected", "Player Attribute Intelligence", "Player Attribute Willpower", "Player Attribute Agility", "Player Attribute Speed", "Player Attribute Endurance", "Player Attribute Personality", "Player Attribute Luck", "Player Diseased (Corprus)", "Weather", "Player is a Vampire", "Player Level", "Attacked", "NPC Talked to Player", "Player Health", "Creature Target", "Friend Hit", "Fight", "Hello", "Alarm", "Flee", "Should Attack", "Werewolf" }; return ruleFunctions[idx]; } else return "Invalid"; } // The "unused flag bits" should probably be defined alongside the // defined bits in the ESM component. The names of the flag bits are // very inconsistent. std::string bodyPartFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::BodyPart::BPF_Female) properties += "Female "; if (flags & ESM::BodyPart::BPF_NotPlayable) properties += "NotPlayable "; int unused = (0xFFFFFFFF ^ (ESM::BodyPart::BPF_Female| ESM::BodyPart::BPF_NotPlayable)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string cellFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::Cell::HasWater) properties += "HasWater "; if (flags & ESM::Cell::Interior) properties += "Interior "; if (flags & ESM::Cell::NoSleep) properties += "NoSleep "; if (flags & ESM::Cell::QuasiEx) properties += "QuasiEx "; // This used value is not in the ESM component. if (flags & 0x00000040) properties += "Unknown "; int unused = (0xFFFFFFFF ^ (ESM::Cell::HasWater| ESM::Cell::Interior| ESM::Cell::NoSleep| ESM::Cell::QuasiEx| 0x00000040)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string containerFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::Container::Unknown) properties += "Unknown "; if (flags & ESM::Container::Organic) properties += "Organic "; if (flags & ESM::Container::Respawn) properties += "Respawn "; int unused = (0xFFFFFFFF ^ (ESM::Container::Unknown| ESM::Container::Organic| ESM::Container::Respawn)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string creatureFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::Creature::None) properties += "All "; if (flags & ESM::Creature::Walks) properties += "Walks "; if (flags & ESM::Creature::Swims) properties += "Swims "; if (flags & ESM::Creature::Flies) properties += "Flies "; if (flags & ESM::Creature::Bipedal) properties += "Bipedal "; if (flags & ESM::Creature::Respawn) properties += "Respawn "; if (flags & ESM::Creature::Weapon) properties += "Weapon "; if (flags & ESM::Creature::Skeleton) properties += "Skeleton "; if (flags & ESM::Creature::Metal) properties += "Metal "; if (flags & ESM::Creature::Essential) properties += "Essential "; int unused = (0xFFFFFFFF ^ (ESM::Creature::None| ESM::Creature::Walks| ESM::Creature::Swims| ESM::Creature::Flies| ESM::Creature::Bipedal| ESM::Creature::Respawn| ESM::Creature::Weapon| ESM::Creature::Skeleton| ESM::Creature::Metal| ESM::Creature::Essential)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string landFlags(int flags) { std::string properties = ""; // The ESM component says that this first four bits are used, but // only the first three bits are used as far as I can tell. // There's also no enumeration of the bit in the ESM component. if (flags == 0) properties += "[None] "; if (flags & 0x00000001) properties += "Unknown1 "; if (flags & 0x00000004) properties += "Unknown3 "; if (flags & 0x00000002) properties += "Unknown2 "; if (flags & 0xFFFFFFF8) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string itemListFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::ItemLevList::AllLevels) properties += "AllLevels "; if (flags & ESM::ItemLevList::Each) properties += "Each "; int unused = (0xFFFFFFFF ^ (ESM::ItemLevList::AllLevels| ESM::ItemLevList::Each)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string creatureListFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::CreatureLevList::AllLevels) properties += "AllLevels "; int unused = (0xFFFFFFFF ^ ESM::CreatureLevList::AllLevels); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string lightFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::Light::Dynamic) properties += "Dynamic "; if (flags & ESM::Light::Fire) properties += "Fire "; if (flags & ESM::Light::Carry) properties += "Carry "; if (flags & ESM::Light::Flicker) properties += "Flicker "; if (flags & ESM::Light::FlickerSlow) properties += "FlickerSlow "; if (flags & ESM::Light::Pulse) properties += "Pulse "; if (flags & ESM::Light::PulseSlow) properties += "PulseSlow "; if (flags & ESM::Light::Negative) properties += "Negative "; if (flags & ESM::Light::OffDefault) properties += "OffDefault "; int unused = (0xFFFFFFFF ^ (ESM::Light::Dynamic| ESM::Light::Fire| ESM::Light::Carry| ESM::Light::Flicker| ESM::Light::FlickerSlow| ESM::Light::Pulse| ESM::Light::PulseSlow| ESM::Light::Negative| ESM::Light::OffDefault)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string magicEffectFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::MagicEffect::TargetAttribute) properties += "TargetAttribute "; if (flags & ESM::MagicEffect::TargetSkill) properties += "TargetSkill "; if (flags & ESM::MagicEffect::NoDuration) properties += "NoDuration "; if (flags & ESM::MagicEffect::NoMagnitude) properties += "NoMagnitude "; if (flags & ESM::MagicEffect::Harmful) properties += "Harmful "; if (flags & ESM::MagicEffect::ContinuousVfx) properties += "ContinuousVFX "; if (flags & ESM::MagicEffect::CastSelf) properties += "CastSelf "; if (flags & ESM::MagicEffect::CastTouch) properties += "CastTouch "; if (flags & ESM::MagicEffect::CastTarget) properties += "CastTarget "; if (flags & ESM::MagicEffect::UncappedDamage) properties += "UncappedDamage "; if (flags & ESM::MagicEffect::NonRecastable) properties += "NonRecastable "; if (flags & ESM::MagicEffect::Unreflectable) properties += "Unreflectable "; if (flags & ESM::MagicEffect::CasterLinked) properties += "CasterLinked "; if (flags & ESM::MagicEffect::AllowSpellmaking) properties += "AllowSpellmaking "; if (flags & ESM::MagicEffect::AllowEnchanting) properties += "AllowEnchanting "; if (flags & ESM::MagicEffect::NegativeLight) properties += "NegativeLight "; if (flags & 0xFFFC0000) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string npcFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; // Mythicmods and the ESM component differ. Mythicmods says // 0x8=None and 0x10=AutoCalc, while our code previously defined // 0x8 as AutoCalc. The former seems to be correct. All Bethesda // records have bit 0x8 set. Previously, suspiciously large portion // of females had autocalc turned off. if (flags & 0x00000008) properties += "Unknown "; if (flags & ESM::NPC::Autocalc) properties += "Autocalc "; if (flags & ESM::NPC::Female) properties += "Female "; if (flags & ESM::NPC::Respawn) properties += "Respawn "; if (flags & ESM::NPC::Essential) properties += "Essential "; // These two flags do not appear on any NPCs and may have been // confused with the flags for creatures. if (flags & ESM::NPC::Skeleton) properties += "Skeleton "; if (flags & ESM::NPC::Metal) properties += "Metal "; // Whether corpses persist is a bit that is unaccounted for, // however the only unknown bit occurs on ALL records, and // relatively few NPCs have this bit set. int unused = (0xFFFFFFFF ^ (0x00000008| ESM::NPC::Autocalc| ESM::NPC::Female| ESM::NPC::Respawn| ESM::NPC::Essential| ESM::NPC::Skeleton| ESM::NPC::Metal)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string raceFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; // All races have the playable flag set in Bethesda files. if (flags & ESM::Race::Playable) properties += "Playable "; if (flags & ESM::Race::Beast) properties += "Beast "; int unused = (0xFFFFFFFF ^ (ESM::Race::Playable| ESM::Race::Beast)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string spellFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::Spell::F_Autocalc) properties += "Autocalc "; if (flags & ESM::Spell::F_PCStart) properties += "PCStart "; if (flags & ESM::Spell::F_Always) properties += "Always "; int unused = (0xFFFFFFFF ^ (ESM::Spell::F_Autocalc| ESM::Spell::F_PCStart| ESM::Spell::F_Always)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } std::string weaponFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; // The interpretation of the flags are still unclear to me. // Apparently you can't be Silver without being Magical? Many of // the "Magical" weapons don't have enchantments of any sort. if (flags & ESM::Weapon::Magical) properties += "Magical "; if (flags & ESM::Weapon::Silver) properties += "Silver "; int unused = (0xFFFFFFFF ^ (ESM::Weapon::Magical| ESM::Weapon::Silver)); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; } openmw-openmw-0.38.0/apps/esmtool/labels.hpp000066400000000000000000000043341264522266000210230ustar00rootroot00000000000000#ifndef OPENMW_ESMTOOL_LABELS_H #define OPENMW_ESMTOOL_LABELS_H #include std::string bodyPartLabel(int idx); std::string meshPartLabel(int idx); std::string meshTypeLabel(int idx); std::string clothingTypeLabel(int idx); std::string armorTypeLabel(int idx); std::string dialogTypeLabel(int idx); std::string questStatusLabel(int idx); std::string creatureTypeLabel(int idx); std::string soundTypeLabel(int idx); std::string weaponTypeLabel(int idx); // This function's a bit different because the types are record types, // not consecutive values. std::string aiTypeLabel(int type); // This one's also a bit different, because it enumerates dialog // select rule functions, not types. Structurally, it still converts // indexes to strings for display. std::string ruleFunction(int idx); // The labels below here can all be loaded from GMSTs, but are not // currently because among other things, that requires loading the // GMSTs before dumping any of the records. // If the data format supported ordered lists of GMSTs (post 1.0), the // lists could define the valid values, their localization strings, // and the indexes for referencing the types in other records in the // database. Then a single label function could work for all types. std::string magicEffectLabel(int idx); std::string attributeLabel(int idx); std::string spellTypeLabel(int idx); std::string specializationLabel(int idx); std::string skillLabel(int idx); std::string apparatusTypeLabel(int idx); std::string rangeTypeLabel(int idx); std::string schoolLabel(int idx); std::string enchantTypeLabel(int idx); // The are the flag functions that convert a bitmask into a list of // human readble strings representing the set bits. std::string bodyPartFlags(int flags); std::string cellFlags(int flags); std::string containerFlags(int flags); std::string creatureFlags(int flags); std::string landFlags(int flags); std::string creatureListFlags(int flags); std::string itemListFlags(int flags); std::string lightFlags(int flags); std::string magicEffectFlags(int flags); std::string npcFlags(int flags); std::string raceFlags(int flags); std::string spellFlags(int flags); std::string weaponFlags(int flags); // Missing flags functions: // aiServicesFlags, possibly more #endif openmw-openmw-0.38.0/apps/esmtool/record.cpp000066400000000000000000001525351264522266000210410ustar00rootroot00000000000000#include "record.hpp" #include "labels.hpp" #include #include #include namespace { void printAIPackage(ESM::AIPackage p) { std::cout << " AI Type: " << aiTypeLabel(p.mType) << " (" << boost::format("0x%08X") % p.mType << ")" << std::endl; if (p.mType == ESM::AI_Wander) { std::cout << " Distance: " << p.mWander.mDistance << std::endl; std::cout << " Duration: " << p.mWander.mDuration << std::endl; std::cout << " Time of Day: " << (int)p.mWander.mTimeOfDay << std::endl; if (p.mWander.mShouldRepeat != 1) std::cout << " Should repeat: " << (bool)(p.mWander.mShouldRepeat != 0) << std::endl; std::cout << " Idle: "; for (int i = 0; i != 8; i++) std::cout << (int)p.mWander.mIdle[i] << " "; std::cout << std::endl; } else if (p.mType == ESM::AI_Travel) { std::cout << " Travel Coordinates: (" << p.mTravel.mX << "," << p.mTravel.mY << "," << p.mTravel.mZ << ")" << std::endl; std::cout << " Travel Unknown: " << p.mTravel.mUnk << std::endl; } else if (p.mType == ESM::AI_Follow || p.mType == ESM::AI_Escort) { std::cout << " Follow Coordinates: (" << p.mTarget.mX << "," << p.mTarget.mY << "," << p.mTarget.mZ << ")" << std::endl; std::cout << " Duration: " << p.mTarget.mDuration << std::endl; std::cout << " Target ID: " << p.mTarget.mId.toString() << std::endl; std::cout << " Unknown: " << p.mTarget.mUnk << std::endl; } else if (p.mType == ESM::AI_Activate) { std::cout << " Name: " << p.mActivate.mName.toString() << std::endl; std::cout << " Activate Unknown: " << p.mActivate.mUnk << std::endl; } else { std::cout << " BadPackage: " << boost::format("0x%08x") % p.mType << std::endl; } if (p.mCellName != "") std::cout << " Cell Name: " << p.mCellName << std::endl; } std::string ruleString(ESM::DialInfo::SelectStruct ss) { std::string rule = ss.mSelectRule; if (rule.length() < 5) return "INVALID"; char type = rule[1]; char indicator = rule[2]; std::string type_str = "INVALID"; std::string func_str = str(boost::format("INVALID=%s") % rule.substr(1,3)); int func; std::istringstream iss(rule.substr(2,2)); iss >> func; switch(type) { case '1': type_str = "Function"; func_str = ruleFunction(func); break; case '2': if (indicator == 's') type_str = "Global short"; else if (indicator == 'l') type_str = "Global long"; else if (indicator == 'f') type_str = "Global float"; break; case '3': if (indicator == 's') type_str = "Local short"; else if (indicator == 'l') type_str = "Local long"; else if (indicator == 'f') type_str = "Local float"; break; case '4': if (indicator == 'J') type_str = "Journal"; break; case '5': if (indicator == 'I') type_str = "Item type"; break; case '6': if (indicator == 'D') type_str = "NPC Dead"; break; case '7': if (indicator == 'X') type_str = "Not ID"; break; case '8': if (indicator == 'F') type_str = "Not Faction"; break; case '9': if (indicator == 'C') type_str = "Not Class"; break; case 'A': if (indicator == 'R') type_str = "Not Race"; break; case 'B': if (indicator == 'L') type_str = "Not Cell"; break; case 'C': if (indicator == 's') type_str = "Not Local"; break; default: break; } // Append the variable name to the function string if any. if (type != '1') func_str = rule.substr(5); // In the previous switch, we assumed that the second char was X // for all types not qual to one. If this wasn't true, go back to // the error message. if (type != '1' && rule[3] != 'X') func_str = str(boost::format("INVALID=%s") % rule.substr(1,3)); char oper = rule[4]; std::string oper_str = "??"; switch (oper) { case '0': oper_str = "=="; break; case '1': oper_str = "!="; break; case '2': oper_str = "> "; break; case '3': oper_str = ">="; break; case '4': oper_str = "< "; break; case '5': oper_str = "<="; break; default: break; } std::ostringstream stream; stream << ss.mValue; std::string result = str(boost::format("%-12s %-32s %2s %s") % type_str % func_str % oper_str % stream.str()); return result; } void printEffectList(ESM::EffectList effects) { int i = 0; std::vector::iterator eit; for (eit = effects.mList.begin(); eit != effects.mList.end(); ++eit) { std::cout << " Effect[" << i << "]: " << magicEffectLabel(eit->mEffectID) << " (" << eit->mEffectID << ")" << std::endl; if (eit->mSkill != -1) std::cout << " Skill: " << skillLabel(eit->mSkill) << " (" << (int)eit->mSkill << ")" << std::endl; if (eit->mAttribute != -1) std::cout << " Attribute: " << attributeLabel(eit->mAttribute) << " (" << (int)eit->mAttribute << ")" << std::endl; std::cout << " Range: " << rangeTypeLabel(eit->mRange) << " (" << eit->mRange << ")" << std::endl; // Area is always zero if range type is "Self" if (eit->mRange != ESM::RT_Self) std::cout << " Area: " << eit->mArea << std::endl; std::cout << " Duration: " << eit->mDuration << std::endl; std::cout << " Magnitude: " << eit->mMagnMin << "-" << eit->mMagnMax << std::endl; i++; } } void printTransport(const std::vector& transport) { std::vector::const_iterator dit; for (dit = transport.begin(); dit != transport.end(); ++dit) { std::cout << " Destination Position: " << boost::format("%12.3f") % dit->mPos.pos[0] << "," << boost::format("%12.3f") % dit->mPos.pos[1] << "," << boost::format("%12.3f") % dit->mPos.pos[2] << ")" << std::endl; std::cout << " Destination Rotation: " << boost::format("%9.6f") % dit->mPos.rot[0] << "," << boost::format("%9.6f") % dit->mPos.rot[1] << "," << boost::format("%9.6f") % dit->mPos.rot[2] << ")" << std::endl; if (dit->mCellName != "") std::cout << " Destination Cell: " << dit->mCellName << std::endl; } } } namespace EsmTool { RecordBase * RecordBase::create(ESM::NAME type) { RecordBase *record = 0; switch (type.val) { case ESM::REC_ACTI: { record = new EsmTool::Record; break; } case ESM::REC_ALCH: { record = new EsmTool::Record; break; } case ESM::REC_APPA: { record = new EsmTool::Record; break; } case ESM::REC_ARMO: { record = new EsmTool::Record; break; } case ESM::REC_BODY: { record = new EsmTool::Record; break; } case ESM::REC_BOOK: { record = new EsmTool::Record; break; } case ESM::REC_BSGN: { record = new EsmTool::Record; break; } case ESM::REC_CELL: { record = new EsmTool::Record; break; } case ESM::REC_CLAS: { record = new EsmTool::Record; break; } case ESM::REC_CLOT: { record = new EsmTool::Record; break; } case ESM::REC_CONT: { record = new EsmTool::Record; break; } case ESM::REC_CREA: { record = new EsmTool::Record; break; } case ESM::REC_DIAL: { record = new EsmTool::Record; break; } case ESM::REC_DOOR: { record = new EsmTool::Record; break; } case ESM::REC_ENCH: { record = new EsmTool::Record; break; } case ESM::REC_FACT: { record = new EsmTool::Record; break; } case ESM::REC_GLOB: { record = new EsmTool::Record; break; } case ESM::REC_GMST: { record = new EsmTool::Record; break; } case ESM::REC_INFO: { record = new EsmTool::Record; break; } case ESM::REC_INGR: { record = new EsmTool::Record; break; } case ESM::REC_LAND: { record = new EsmTool::Record; break; } case ESM::REC_LEVI: { record = new EsmTool::Record; break; } case ESM::REC_LEVC: { record = new EsmTool::Record; break; } case ESM::REC_LIGH: { record = new EsmTool::Record; break; } case ESM::REC_LOCK: { record = new EsmTool::Record; break; } case ESM::REC_LTEX: { record = new EsmTool::Record; break; } case ESM::REC_MISC: { record = new EsmTool::Record; break; } case ESM::REC_MGEF: { record = new EsmTool::Record; break; } case ESM::REC_NPC_: { record = new EsmTool::Record; break; } case ESM::REC_PGRD: { record = new EsmTool::Record; break; } case ESM::REC_PROB: { record = new EsmTool::Record; break; } case ESM::REC_RACE: { record = new EsmTool::Record; break; } case ESM::REC_REGN: { record = new EsmTool::Record; break; } case ESM::REC_REPA: { record = new EsmTool::Record; break; } case ESM::REC_SCPT: { record = new EsmTool::Record; break; } case ESM::REC_SKIL: { record = new EsmTool::Record; break; } case ESM::REC_SNDG: { record = new EsmTool::Record; break; } case ESM::REC_SOUN: { record = new EsmTool::Record; break; } case ESM::REC_SPEL: { record = new EsmTool::Record; break; } case ESM::REC_STAT: { record = new EsmTool::Record; break; } case ESM::REC_WEAP: { record = new EsmTool::Record; break; } case ESM::REC_SSCR: { record = new EsmTool::Record; break; } default: record = 0; } if (record) { record->mType = type; } return record; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl; printEffectList(mData.mEffects); std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; if (mData.mEnchant != "") std::cout << " Enchantment: " << mData.mEnchant << std::endl; std::cout << " Type: " << armorTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Health: " << mData.mData.mHealth << std::endl; std::cout << " Armor: " << mData.mData.mArmor << std::endl; std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; std::vector::iterator pit; for (pit = mData.mParts.mParts.begin(); pit != mData.mParts.mParts.end(); ++pit) { std::cout << " Body Part: " << bodyPartLabel(pit->mPart) << " (" << (int)(pit->mPart) << ")" << std::endl; std::cout << " Male Name: " << pit->mMale << std::endl; if (pit->mFemale != "") std::cout << " Female Name: " << pit->mFemale << std::endl; } std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Type: " << apparatusTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Race: " << mData.mRace << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Type: " << meshTypeLabel(mData.mData.mType) << " (" << (int)mData.mData.mType << ")" << std::endl; std::cout << " Flags: " << bodyPartFlags(mData.mData.mFlags) << std::endl; std::cout << " Part: " << meshPartLabel(mData.mData.mPart) << " (" << (int)mData.mData.mPart << ")" << std::endl; std::cout << " Vampire: " << (int)mData.mData.mVampire << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; if (mData.mEnchant != "") std::cout << " Enchantment: " << mData.mEnchant << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl; std::cout << " SkillID: " << mData.mData.mSkillID << std::endl; std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; if (mPrintPlain) { std::cout << " Text:" << std::endl; std::cout << "START--------------------------------------" << std::endl; std::cout << mData.mText << std::endl; std::cout << "END----------------------------------------" << std::endl; } else { std::cout << " Text: [skipped]" << std::endl; } std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Texture: " << mData.mTexture << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::vector::iterator pit; for (pit = mData.mPowers.mList.begin(); pit != mData.mPowers.mList.end(); ++pit) std::cout << " Power: " << *pit << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { // None of the cells have names... if (mData.mName != "") std::cout << " Name: " << mData.mName << std::endl; if (mData.mRegion != "") std::cout << " Region: " << mData.mRegion << std::endl; std::cout << " Flags: " << cellFlags(mData.mData.mFlags) << std::endl; std::cout << " Coordinates: " << " (" << mData.getGridX() << "," << mData.getGridY() << ")" << std::endl; if (mData.mData.mFlags & ESM::Cell::Interior && !(mData.mData.mFlags & ESM::Cell::QuasiEx)) { std::cout << " Ambient Light Color: " << mData.mAmbi.mAmbient << std::endl; std::cout << " Sunlight Color: " << mData.mAmbi.mSunlight << std::endl; std::cout << " Fog Color: " << mData.mAmbi.mFog << std::endl; std::cout << " Fog Density: " << mData.mAmbi.mFogDensity << std::endl; std::cout << " Water Level: " << mData.mWater << std::endl; } else std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl; std::cout << " Water Level Int: " << mData.mWaterInt << std::endl; std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Playable: " << mData.mData.mIsPlayable << std::endl; std::cout << " AutoCalc: " << mData.mData.mCalc << std::endl; std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute[0]) << " (" << mData.mData.mAttribute[0] << ")" << std::endl; std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) << " (" << mData.mData.mAttribute[1] << ")" << std::endl; std::cout << " Specialization: " << specializationLabel(mData.mData.mSpecialization) << " (" << mData.mData.mSpecialization << ")" << std::endl; for (int i = 0; i != 5; i++) std::cout << " Minor Skill: " << skillLabel(mData.mData.mSkills[i][0]) << " (" << mData.mData.mSkills[i][0] << ")" << std::endl; for (int i = 0; i != 5; i++) std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][1]) << " (" << mData.mData.mSkills[i][1] << ")" << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; if (mData.mEnchant != "") std::cout << " Enchantment: " << mData.mEnchant << std::endl; std::cout << " Type: " << clothingTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; std::vector::iterator pit; for (pit = mData.mParts.mParts.begin(); pit != mData.mParts.mParts.end(); ++pit) { std::cout << " Body Part: " << bodyPartLabel(pit->mPart) << " (" << (int)(pit->mPart) << ")" << std::endl; std::cout << " Male Name: " << pit->mMale << std::endl; if (pit->mFemale != "") std::cout << " Female Name: " << pit->mFemale << std::endl; } std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Flags: " << containerFlags(mData.mFlags) << std::endl; std::cout << " Weight: " << mData.mWeight << std::endl; std::vector::iterator cit; for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); ++cit) std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount << " Item: " << cit->mItem.toString() << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Flags: " << creatureFlags(mData.mFlags) << std::endl; std::cout << " Original: " << mData.mOriginal << std::endl; std::cout << " Scale: " << mData.mScale << std::endl; std::cout << " Type: " << creatureTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Level: " << mData.mData.mLevel << std::endl; std::cout << " Attributes:" << std::endl; std::cout << " Strength: " << mData.mData.mStrength << std::endl; std::cout << " Intelligence: " << mData.mData.mIntelligence << std::endl; std::cout << " Willpower: " << mData.mData.mWillpower << std::endl; std::cout << " Agility: " << mData.mData.mAgility << std::endl; std::cout << " Speed: " << mData.mData.mSpeed << std::endl; std::cout << " Endurance: " << mData.mData.mEndurance << std::endl; std::cout << " Personality: " << mData.mData.mPersonality << std::endl; std::cout << " Luck: " << mData.mData.mLuck << std::endl; std::cout << " Health: " << mData.mData.mHealth << std::endl; std::cout << " Magicka: " << mData.mData.mMana << std::endl; std::cout << " Fatigue: " << mData.mData.mFatigue << std::endl; std::cout << " Soul: " << mData.mData.mSoul << std::endl; std::cout << " Combat: " << mData.mData.mCombat << std::endl; std::cout << " Magic: " << mData.mData.mMagic << std::endl; std::cout << " Stealth: " << mData.mData.mStealth << std::endl; std::cout << " Attack1: " << mData.mData.mAttack[0] << "-" << mData.mData.mAttack[1] << std::endl; std::cout << " Attack2: " << mData.mData.mAttack[2] << "-" << mData.mData.mAttack[3] << std::endl; std::cout << " Attack3: " << mData.mData.mAttack[4] << "-" << mData.mData.mAttack[5] << std::endl; std::cout << " Gold: " << mData.mData.mGold << std::endl; std::vector::iterator cit; for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); ++cit) std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount << " Item: " << cit->mItem.toString() << std::endl; std::vector::iterator sit; for (sit = mData.mSpells.mList.begin(); sit != mData.mSpells.mList.end(); ++sit) std::cout << " Spell: " << *sit << std::endl; printTransport(mData.getTransport()); std::cout << " Artifical Intelligence: " << mData.mHasAI << std::endl; std::cout << " AI Hello:" << (int)mData.mAiData.mHello << std::endl; std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl; std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl; std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl; std::cout << " AI U1:" << (int)mData.mAiData.mU1 << std::endl; std::cout << " AI U2:" << (int)mData.mAiData.mU2 << std::endl; std::cout << " AI U3:" << (int)mData.mAiData.mU3 << std::endl; std::cout << " AI U4:" << (int)mData.mAiData.mU4 << std::endl; std::cout << " AI Services:" << boost::format("0x%08X") % mData.mAiData.mServices << std::endl; std::vector::iterator pit; for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit) printAIPackage(*pit); std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Type: " << dialogTypeLabel(mData.mType) << " (" << (int)mData.mType << ")" << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; // Sadly, there are no DialInfos, because the loader dumps as it // loads, rather than loading and then dumping. :-( Anyone mind if // I change this? ESM::Dialogue::InfoContainer::iterator iit; for (iit = mData.mInfo.begin(); iit != mData.mInfo.end(); iit++) std::cout << "INFO!" << iit->mId << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Script: " << mData.mScript << std::endl; std::cout << " OpenSound: " << mData.mOpenSound << std::endl; std::cout << " CloseSound: " << mData.mCloseSound << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Type: " << enchantTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Cost: " << mData.mData.mCost << std::endl; std::cout << " Charge: " << mData.mData.mCharge << std::endl; std::cout << " AutoCalc: " << mData.mData.mAutocalc << std::endl; printEffectList(mData.mEffects); std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Hidden: " << mData.mData.mIsHidden << std::endl; std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute[0]) << " (" << mData.mData.mAttribute[0] << ")" << std::endl; std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) << " (" << mData.mData.mAttribute[1] << ")" << std::endl; for (int i = 0; i < 7; i++) if (mData.mData.mSkills[i] != -1) std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) << " (" << mData.mData.mSkills[i] << ")" << std::endl; for (int i = 0; i != 10; i++) if (mData.mRanks[i] != "") { std::cout << " Rank: " << mData.mRanks[i] << std::endl; std::cout << " Attribute1 Requirement: " << mData.mData.mRankData[i].mAttribute1 << std::endl; std::cout << " Attribute2 Requirement: " << mData.mData.mRankData[i].mAttribute2 << std::endl; std::cout << " One Skill at Level: " << mData.mData.mRankData[i].mSkill1 << std::endl; std::cout << " Two Skills at Level: " << mData.mData.mRankData[i].mSkill2 << std::endl; std::cout << " Faction Reaction: " << mData.mData.mRankData[i].mFactReaction << std::endl; } std::map::iterator rit; for (rit = mData.mReactions.begin(); rit != mData.mReactions.end(); ++rit) std::cout << " Reaction: " << rit->second << " = " << rit->first << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " " << mData.mValue << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " " << mData.mValue << std::endl; } template<> void Record::print() { std::cout << " Id: " << mData.mId << std::endl; if (mData.mPrev != "") std::cout << " Previous ID: " << mData.mPrev << std::endl; if (mData.mNext != "") std::cout << " Next ID: " << mData.mNext << std::endl; std::cout << " Text: " << mData.mResponse << std::endl; if (mData.mActor != "") std::cout << " Actor: " << mData.mActor << std::endl; if (mData.mRace != "") std::cout << " Race: " << mData.mRace << std::endl; if (mData.mClass != "") std::cout << " Class: " << mData.mClass << std::endl; std::cout << " Factionless: " << mData.mFactionLess << std::endl; if (mData.mFaction != "") std::cout << " NPC Faction: " << mData.mFaction << std::endl; if (mData.mData.mRank != -1) std::cout << " NPC Rank: " << (int)mData.mData.mRank << std::endl; if (mData.mPcFaction != "") std::cout << " PC Faction: " << mData.mPcFaction << std::endl; // CHANGE? non-standard capitalization mPCrank -> mPCRank (mPcRank?) if (mData.mData.mPCrank != -1) std::cout << " PC Rank: " << (int)mData.mData.mPCrank << std::endl; if (mData.mCell != "") std::cout << " Cell: " << mData.mCell << std::endl; if (mData.mData.mDisposition > 0) std::cout << " Disposition/Journal index: " << mData.mData.mDisposition << std::endl; if (mData.mData.mGender != ESM::DialInfo::NA) std::cout << " Gender: " << mData.mData.mGender << std::endl; if (mData.mSound != "") std::cout << " Sound File: " << mData.mSound << std::endl; std::cout << " Quest Status: " << questStatusLabel(mData.mQuestStatus) << " (" << mData.mQuestStatus << ")" << std::endl; std::cout << " Unknown1: " << mData.mData.mUnknown1 << std::endl; std::cout << " Unknown2: " << (int)mData.mData.mUnknown2 << std::endl; std::vector::iterator sit; for (sit = mData.mSelects.begin(); sit != mData.mSelects.end(); ++sit) std::cout << " Select Rule: " << ruleString(*sit) << std::endl; if (mData.mResultScript != "") { if (mPrintPlain) { std::cout << " Result Script:" << std::endl; std::cout << "START--------------------------------------" << std::endl; std::cout << mData.mResultScript << std::endl; std::cout << "END----------------------------------------" << std::endl; } else { std::cout << " Result Script: [skipped]" << std::endl; } } std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; for (int i = 0; i !=4; i++) { // A value of -1 means no effect if (mData.mData.mEffectID[i] == -1) continue; std::cout << " Effect: " << magicEffectLabel(mData.mData.mEffectID[i]) << " (" << mData.mData.mEffectID[i] << ")" << std::endl; std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) << " (" << mData.mData.mSkills[i] << ")" << std::endl; std::cout << " Attribute: " << attributeLabel(mData.mData.mAttributes[i]) << " (" << mData.mData.mAttributes[i] << ")" << std::endl; } std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl; std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl; std::cout << " DataTypes: " << mData.mDataTypes << std::endl; if (const ESM::Land::LandData *data = mData.getLandData (mData.mDataTypes)) { std::cout << " Height Offset: " << data->mHeightOffset << std::endl; // Lots of missing members. std::cout << " Unknown1: " << data->mUnk1 << std::endl; std::cout << " Unknown2: " << data->mUnk2 << std::endl; } mData.unloadData(); std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit) std::cout << " Creature: Level: " << iit->mLevel << " Creature: " << iit->mId << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit) std::cout << " Inventory: Level: " << iit->mLevel << " Item: " << iit->mId << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { if (mData.mName != "") std::cout << " Name: " << mData.mName << std::endl; if (mData.mModel != "") std::cout << " Model: " << mData.mModel << std::endl; if (mData.mIcon != "") std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Flags: " << lightFlags(mData.mData.mFlags) << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Sound: " << mData.mSound << std::endl; std::cout << " Duration: " << mData.mData.mTime << std::endl; std::cout << " Radius: " << mData.mData.mRadius << std::endl; std::cout << " Color: " << mData.mData.mColor << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Uses: " << mData.mData.mUses << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Uses: " << mData.mData.mUses << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; std::cout << " Uses: " << mData.mData.mUses << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Id: " << mData.mId << std::endl; std::cout << " Index: " << mData.mIndex << std::endl; std::cout << " Texture: " << mData.mTexture << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Index: " << magicEffectLabel(mData.mIndex) << " (" << mData.mIndex << ")" << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; std::cout << " Flags: " << magicEffectFlags(mData.mData.mFlags) << std::endl; std::cout << " Particle Texture: " << mData.mParticle << std::endl; if (mData.mCasting != "") std::cout << " Casting Static: " << mData.mCasting << std::endl; if (mData.mCastSound != "") std::cout << " Casting Sound: " << mData.mCastSound << std::endl; if (mData.mBolt != "") std::cout << " Bolt Static: " << mData.mBolt << std::endl; if (mData.mBoltSound != "") std::cout << " Bolt Sound: " << mData.mBoltSound << std::endl; if (mData.mHit != "") std::cout << " Hit Static: " << mData.mHit << std::endl; if (mData.mHitSound != "") std::cout << " Hit Sound: " << mData.mHitSound << std::endl; if (mData.mArea != "") std::cout << " Area Static: " << mData.mArea << std::endl; if (mData.mAreaSound != "") std::cout << " Area Sound: " << mData.mAreaSound << std::endl; std::cout << " School: " << schoolLabel(mData.mData.mSchool) << " (" << mData.mData.mSchool << ")" << std::endl; std::cout << " Base Cost: " << mData.mData.mBaseCost << std::endl; std::cout << " Unknown 1: " << mData.mData.mUnknown1 << std::endl; std::cout << " Speed: " << mData.mData.mSpeed << std::endl; std::cout << " Unknown 2: " << mData.mData.mUnknown2 << std::endl; std::cout << " RGB Color: " << "(" << mData.mData.mRed << "," << mData.mData.mGreen << "," << mData.mData.mBlue << ")" << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Is Key: " << mData.mData.mIsKey << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Animation: " << mData.mModel << std::endl; std::cout << " Hair Model: " << mData.mHair << std::endl; std::cout << " Head Model: " << mData.mHead << std::endl; std::cout << " Race: " << mData.mRace << std::endl; std::cout << " Class: " << mData.mClass << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; if (mData.mFaction != "") std::cout << " Faction: " << mData.mFaction << std::endl; std::cout << " Flags: " << npcFlags(mData.mFlags) << std::endl; if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { std::cout << " Level: " << mData.mNpdt12.mLevel << std::endl; std::cout << " Reputation: " << (int)mData.mNpdt12.mReputation << std::endl; std::cout << " Disposition: " << (int)mData.mNpdt12.mDisposition << std::endl; std::cout << " Rank: " << (int)mData.mNpdt12.mRank << std::endl; std::cout << " Unknown1: " << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown1) << std::endl; std::cout << " Unknown2: " << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown2) << std::endl; std::cout << " Unknown3: " << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown3) << std::endl; std::cout << " Gold: " << mData.mNpdt12.mGold << std::endl; } else { std::cout << " Level: " << mData.mNpdt52.mLevel << std::endl; std::cout << " Reputation: " << (int)mData.mNpdt52.mReputation << std::endl; std::cout << " Disposition: " << (int)mData.mNpdt52.mDisposition << std::endl; std::cout << " Rank: " << (int)mData.mNpdt52.mRank << std::endl; std::cout << " FactionID: " << (int)mData.mNpdt52.mFactionID << std::endl; std::cout << " Attributes:" << std::endl; std::cout << " Strength: " << (int)mData.mNpdt52.mStrength << std::endl; std::cout << " Intelligence: " << (int)mData.mNpdt52.mIntelligence << std::endl; std::cout << " Willpower: " << (int)mData.mNpdt52.mWillpower << std::endl; std::cout << " Agility: " << (int)mData.mNpdt52.mAgility << std::endl; std::cout << " Speed: " << (int)mData.mNpdt52.mSpeed << std::endl; std::cout << " Endurance: " << (int)mData.mNpdt52.mEndurance << std::endl; std::cout << " Personality: " << (int)mData.mNpdt52.mPersonality << std::endl; std::cout << " Luck: " << (int)mData.mNpdt52.mLuck << std::endl; std::cout << " Skills:" << std::endl; for (int i = 0; i != ESM::Skill::Length; i++) std::cout << " " << skillLabel(i) << ": " << (int)(mData.mNpdt52.mSkills[i]) << std::endl; std::cout << " Health: " << mData.mNpdt52.mHealth << std::endl; std::cout << " Magicka: " << mData.mNpdt52.mMana << std::endl; std::cout << " Fatigue: " << mData.mNpdt52.mFatigue << std::endl; std::cout << " Unknown: " << (int)mData.mNpdt52.mUnknown << std::endl; std::cout << " Gold: " << mData.mNpdt52.mGold << std::endl; } std::vector::iterator cit; for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); ++cit) std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount << " Item: " << cit->mItem.toString() << std::endl; std::vector::iterator sit; for (sit = mData.mSpells.mList.begin(); sit != mData.mSpells.mList.end(); ++sit) std::cout << " Spell: " << *sit << std::endl; printTransport(mData.getTransport()); std::cout << " Artifical Intelligence: " << mData.mHasAI << std::endl; std::cout << " AI Hello:" << (int)mData.mAiData.mHello << std::endl; std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl; std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl; std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl; std::cout << " AI U1:" << (int)mData.mAiData.mU1 << std::endl; std::cout << " AI U2:" << (int)mData.mAiData.mU2 << std::endl; std::cout << " AI U3:" << (int)mData.mAiData.mU3 << std::endl; std::cout << " AI U4:" << (int)mData.mAiData.mU4 << std::endl; std::cout << " AI Services:" << boost::format("0x%08X") % mData.mAiData.mServices << std::endl; std::vector::iterator pit; for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit) printAIPackage(*pit); std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Cell: " << mData.mCell << std::endl; std::cout << " Coordinates: (" << mData.mData.mX << "," << mData.mData.mY << ")" << std::endl; std::cout << " Unknown S1: " << mData.mData.mS1 << std::endl; if ((unsigned int)mData.mData.mS2 != mData.mPoints.size()) std::cout << " Reported Point Count: " << mData.mData.mS2 << std::endl; std::cout << " Point Count: " << mData.mPoints.size() << std::endl; std::cout << " Edge Count: " << mData.mEdges.size() << std::endl; int i = 0; ESM::Pathgrid::PointList::iterator pit; for (pit = mData.mPoints.begin(); pit != mData.mPoints.end(); pit++) { std::cout << " Point[" << i << "]:" << std::endl; std::cout << " Coordinates: (" << pit->mX << "," << pit->mY << "," << pit->mZ << ")" << std::endl; std::cout << " Auto-Generated: " << (int)pit->mAutogenerated << std::endl; std::cout << " Connections: " << (int)pit->mConnectionNum << std::endl; std::cout << " Unknown: " << pit->mUnknown << std::endl; i++; } i = 0; ESM::Pathgrid::EdgeList::iterator eit; for (eit = mData.mEdges.begin(); eit != mData.mEdges.end(); eit++) { std::cout << " Edge[" << i << "]: " << eit->mV0 << " -> " << eit->mV1 << std::endl; if (eit->mV0 >= mData.mData.mS2 || eit->mV1 >= mData.mData.mS2) std::cout << " BAD POINT IN EDGE!" << std::endl; i++; } std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { static const char *sAttributeNames[8] = { "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", "Luck" }; std::cout << " Name: " << mData.mName << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl; for (int i=0; i<2; ++i) { bool male = i==0; std::cout << (male ? " Male:" : " Female:") << std::endl; for (int j=0; j<8; ++j) std::cout << " " << sAttributeNames[j] << ": " << mData.mData.mAttributeValues[j].getValue (male) << std::endl; std::cout << " Height: " << mData.mData.mHeight.getValue (male) << std::endl; std::cout << " Weight: " << mData.mData.mWeight.getValue (male) << std::endl; } for (int i = 0; i != 7; i++) // Not all races have 7 skills. if (mData.mData.mBonus[i].mSkill != -1) std::cout << " Skill: " << skillLabel(mData.mData.mBonus[i].mSkill) << " (" << mData.mData.mBonus[i].mSkill << ") = " << mData.mData.mBonus[i].mBonus << std::endl; std::vector::iterator sit; for (sit = mData.mPowers.mList.begin(); sit != mData.mPowers.mList.end(); ++sit) std::cout << " Power: " << *sit << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Weather:" << std::endl; std::cout << " Clear: " << (int)mData.mData.mClear << std::endl; std::cout << " Cloudy: " << (int)mData.mData.mCloudy << std::endl; std::cout << " Foggy: " << (int)mData.mData.mFoggy << std::endl; std::cout << " Overcast: " << (int)mData.mData.mOvercast << std::endl; std::cout << " Rain: " << (int)mData.mData.mOvercast << std::endl; std::cout << " Thunder: " << (int)mData.mData.mThunder << std::endl; std::cout << " Ash: " << (int)mData.mData.mAsh << std::endl; std::cout << " Blight: " << (int)mData.mData.mBlight << std::endl; std::cout << " UnknownA: " << (int)mData.mData.mA << std::endl; std::cout << " UnknownB: " << (int)mData.mData.mB << std::endl; std::cout << " Map Color: " << mData.mMapColor << std::endl; if (mData.mSleepList != "") std::cout << " Sleep List: " << mData.mSleepList << std::endl; std::vector::iterator sit; for (sit = mData.mSoundList.begin(); sit != mData.mSoundList.end(); ++sit) std::cout << " Sound: " << (int)sit->mChance << " = " << sit->mSound.toString() << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mId << std::endl; std::cout << " Num Shorts: " << mData.mData.mNumShorts << std::endl; std::cout << " Num Longs: " << mData.mData.mNumLongs << std::endl; std::cout << " Num Floats: " << mData.mData.mNumFloats << std::endl; std::cout << " Script Data Size: " << mData.mData.mScriptDataSize << std::endl; std::cout << " Table Size: " << mData.mData.mStringTableSize << std::endl; std::vector::iterator vit; for (vit = mData.mVarNames.begin(); vit != mData.mVarNames.end(); ++vit) std::cout << " Variable: " << *vit << std::endl; std::cout << " ByteCode: "; std::vector::iterator cit; for (cit = mData.mScriptData.begin(); cit != mData.mScriptData.end(); ++cit) std::cout << boost::format("%02X") % (int)(*cit); std::cout << std::endl; if (mPrintPlain) { std::cout << " Script:" << std::endl; std::cout << "START--------------------------------------" << std::endl; std::cout << mData.mScriptText << std::endl; std::cout << "END----------------------------------------" << std::endl; } else { std::cout << " Script: [skipped]" << std::endl; } std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " ID: " << skillLabel(mData.mIndex) << " (" << mData.mIndex << ")" << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Governing Attribute: " << attributeLabel(mData.mData.mAttribute) << " (" << mData.mData.mAttribute << ")" << std::endl; std::cout << " Specialization: " << specializationLabel(mData.mData.mSpecialization) << " (" << mData.mData.mSpecialization << ")" << std::endl; for (int i = 0; i != 4; i++) std::cout << " UseValue[" << i << "]:" << mData.mData.mUseValue[i] << std::endl; } template<> void Record::print() { std::cout << " Creature: " << mData.mCreature << std::endl; std::cout << " Sound: " << mData.mSound << std::endl; std::cout << " Type: " << soundTypeLabel(mData.mType) << " (" << mData.mType << ")" << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Sound: " << mData.mSound << std::endl; std::cout << " Volume: " << (int)mData.mData.mVolume << std::endl; if (mData.mData.mMinRange != 0 && mData.mData.mMaxRange != 0) std::cout << " Range: " << (int)mData.mData.mMinRange << " - " << (int)mData.mData.mMaxRange << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Type: " << spellTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Flags: " << spellFlags(mData.mData.mFlags) << std::endl; std::cout << " Cost: " << mData.mData.mCost << std::endl; printEffectList(mData.mEffects); std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Start Script: " << mData.mId << std::endl; std::cout << " Start Data: " << mData.mData << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> void Record::print() { std::cout << " Model: " << mData.mModel << std::endl; } template<> void Record::print() { // No names on VFX bolts if (mData.mName != "") std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; // No icons on VFX bolts or magic bolts if (mData.mIcon != "") std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; if (mData.mEnchant != "") std::cout << " Enchantment: " << mData.mEnchant << std::endl; std::cout << " Type: " << weaponTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")" << std::endl; std::cout << " Flags: " << weaponFlags(mData.mData.mFlags) << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Health: " << mData.mData.mHealth << std::endl; std::cout << " Speed: " << mData.mData.mSpeed << std::endl; std::cout << " Reach: " << mData.mData.mReach << std::endl; std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; if (mData.mData.mChop[0] != 0 && mData.mData.mChop[1] != 0) std::cout << " Chop: " << (int)mData.mData.mChop[0] << "-" << (int)mData.mData.mChop[1] << std::endl; if (mData.mData.mSlash[0] != 0 && mData.mData.mSlash[1] != 0) std::cout << " Slash: " << (int)mData.mData.mSlash[0] << "-" << (int)mData.mData.mSlash[1] << std::endl; if (mData.mData.mThrust[0] != 0 && mData.mData.mThrust[1] != 0) std::cout << " Thrust: " << (int)mData.mData.mThrust[0] << "-" << (int)mData.mData.mThrust[1] << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl; } template<> std::string Record::getId() const { return mData.mName; } template<> std::string Record::getId() const { return ""; // No ID for Land record } template<> std::string Record::getId() const { return ""; // No ID for MagicEffect record } template<> std::string Record::getId() const { return ""; // No ID for Pathgrid record } template<> std::string Record::getId() const { return ""; // No ID for Skill record } } // end namespace openmw-openmw-0.38.0/apps/esmtool/record.hpp000066400000000000000000000102541264522266000210350ustar00rootroot00000000000000#ifndef OPENMW_ESMTOOL_RECORD_H #define OPENMW_ESMTOOL_RECORD_H #include #include namespace ESM { class ESMReader; class ESMWriter; } namespace EsmTool { template class Record; class RecordBase { protected: std::string mId; uint32_t mFlags; ESM::NAME mType; bool mPrintPlain; public: RecordBase () : mFlags(0) , mPrintPlain(false) { } virtual ~RecordBase() {} virtual std::string getId() const = 0; uint32_t getFlags() const { return mFlags; } void setFlags(uint32_t flags) { mFlags = flags; } ESM::NAME getType() const { return mType; } void setPrintPlain(bool plain) { mPrintPlain = plain; } virtual void load(ESM::ESMReader &esm) = 0; virtual void save(ESM::ESMWriter &esm) = 0; virtual void print() = 0; static RecordBase *create(ESM::NAME type); // just make it a bit shorter template Record *cast() { return static_cast *>(this); } }; template class Record : public RecordBase { T mData; bool mIsDeleted; public: Record() : mIsDeleted(false) {} std::string getId() const { return mData.mId; } T &get() { return mData; } void save(ESM::ESMWriter &esm) { mData.save(esm, mIsDeleted); } void load(ESM::ESMReader &esm) { mData.load(esm, mIsDeleted); } void print(); }; template<> std::string Record::getId() const; template<> std::string Record::getId() const; template<> std::string Record::getId() const; template<> std::string Record::getId() const; template<> std::string Record::getId() const; template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); } #endif openmw-openmw-0.38.0/apps/essimporter/000077500000000000000000000000001264522266000177365ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/essimporter/CMakeLists.txt000066400000000000000000000015431264522266000225010ustar00rootroot00000000000000set(ESSIMPORTER_FILES main.cpp importer.cpp importplayer.cpp importnpcc.cpp importcrec.cpp importcellref.cpp importacdt.cpp importinventory.cpp importklst.cpp importcntc.cpp importgame.cpp importinfo.cpp importdial.cpp importques.cpp importjour.cpp importscri.cpp importscpt.cpp importercontext.cpp converter.cpp convertacdt.cpp convertnpcc.cpp convertinventory.cpp convertcrec.cpp convertcntc.cpp convertscri.cpp convertscpt.cpp convertplayer.cpp ) add_executable(openmw-essimporter ${ESSIMPORTER_FILES} ) target_link_libraries(openmw-essimporter ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} components ) if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw-essimporter gcov) endif() openmw-openmw-0.38.0/apps/essimporter/convertacdt.cpp000066400000000000000000000031751264522266000227640ustar00rootroot00000000000000#include "convertacdt.hpp" namespace ESSImport { int translateDynamicIndex(int mwIndex) { if (mwIndex == 1) return 2; else if (mwIndex == 2) return 1; return mwIndex; } void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats) { for (int i=0; i<3; ++i) { int writeIndex = translateDynamicIndex(i); cStats.mDynamic[writeIndex].mBase = acdt.mDynamic[i][1]; cStats.mDynamic[writeIndex].mMod = acdt.mDynamic[i][1]; cStats.mDynamic[writeIndex].mCurrent = acdt.mDynamic[i][0]; } for (int i=0; i<8; ++i) { cStats.mAttributes[i].mBase = static_cast(acdt.mAttributes[i][1]); cStats.mAttributes[i].mMod = static_cast(acdt.mAttributes[i][0]); cStats.mAttributes[i].mCurrent = static_cast(acdt.mAttributes[i][0]); } cStats.mGoldPool = acdt.mGoldPool; cStats.mTalkedTo = (acdt.mFlags & TalkedToPlayer) != 0; cStats.mAttacked = (acdt.mFlags & Attacked) != 0; } void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats) { cStats.mDead = (acsc.mFlags & Dead) != 0; } void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats) { for (int i=0; i #include #include #include "importacdt.hpp" namespace ESSImport { // OpenMW uses Health,Magicka,Fatigue, MW uses Health,Fatigue,Magicka int translateDynamicIndex(int mwIndex); void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats); void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats); void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats); } #endif openmw-openmw-0.38.0/apps/essimporter/convertcntc.cpp000066400000000000000000000003431264522266000227720ustar00rootroot00000000000000#include "convertcntc.hpp" #include "convertinventory.hpp" namespace ESSImport { void convertCNTC(const CNTC &cntc, ESM::ContainerState &state) { convertInventory(cntc.mInventory, state.mInventory); } } openmw-openmw-0.38.0/apps/essimporter/convertcntc.hpp000066400000000000000000000003761264522266000230050ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CONVERTCNTC_H #define OPENMW_ESSIMPORT_CONVERTCNTC_H #include "importcntc.hpp" #include namespace ESSImport { void convertCNTC(const CNTC& cntc, ESM::ContainerState& state); } #endif openmw-openmw-0.38.0/apps/essimporter/convertcrec.cpp000066400000000000000000000003421264522266000227560ustar00rootroot00000000000000#include "convertcrec.hpp" #include "convertinventory.hpp" namespace ESSImport { void convertCREC(const CREC &crec, ESM::CreatureState &state) { convertInventory(crec.mInventory, state.mInventory); } } openmw-openmw-0.38.0/apps/essimporter/convertcrec.hpp000066400000000000000000000003741264522266000227700ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CONVERTCREC_H #define OPENMW_ESSIMPORT_CONVERTCREC_H #include "importcrec.hpp" #include namespace ESSImport { void convertCREC(const CREC& crec, ESM::CreatureState& state); } #endif openmw-openmw-0.38.0/apps/essimporter/converter.cpp000066400000000000000000000370171264522266000224610ustar00rootroot00000000000000#include "converter.hpp" #include #include #include #include #include "convertcrec.hpp" #include "convertcntc.hpp" #include "convertscri.hpp" namespace { void convertImage(char* data, int size, int width, int height, GLenum pf, const std::string& out) { osg::ref_ptr image (new osg::Image); image->allocateImage(width, height, 1, pf, GL_UNSIGNED_BYTE); memcpy(image->data(), data, size); image->flipVertical(); osgDB::writeImageFile(*image, out); } void convertCellRef(const ESSImport::CellRef& cellref, ESM::ObjectState& objstate) { objstate.mEnabled = cellref.mEnabled; objstate.mPosition = cellref.mPos; objstate.mRef.mRefNum = cellref.mRefNum; if (cellref.mDeleted) objstate.mCount = 0; convertSCRI(cellref.mSCRI, objstate.mLocals); objstate.mHasLocals = !objstate.mLocals.mVariables.empty(); } bool isIndexedRefId(const std::string& indexedRefId) { if (indexedRefId.size() <= 8) return false; if (indexedRefId.find_first_not_of("0123456789") == std::string::npos) return false; // entirely numeric refid, this is a reference to // a dynamically created record e.g. player-enchanted weapon std::string index = indexedRefId.substr(indexedRefId.size()-8); if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos ) return true; return false; } } namespace ESSImport { struct MAPH { unsigned int size; unsigned int value; }; void ConvertFMAP::read(ESM::ESMReader &esm) { MAPH maph; esm.getHNT(maph, "MAPH"); std::vector data; esm.getSubNameIs("MAPD"); esm.getSubHeader(); data.resize(esm.getSubSize()); esm.getExact(&data[0], data.size()); mGlobalMapImage = new osg::Image; mGlobalMapImage->allocateImage(maph.size, maph.size, 1, GL_RGB, GL_UNSIGNED_BYTE); memcpy(mGlobalMapImage->data(), &data[0], data.size()); // to match openmw size // FIXME: filtering? mGlobalMapImage->scaleImage(maph.size*2, maph.size*2, 1, GL_UNSIGNED_BYTE); } void ConvertFMAP::write(ESM::ESMWriter &esm) { int numcells = mGlobalMapImage->s() / 18; // NB truncating, doesn't divide perfectly // with the 512x512 map the game has by default int cellSize = mGlobalMapImage->s()/numcells; // Note the upper left corner of the (0,0) cell should be at (width/2, height/2) mContext->mGlobalMapState.mBounds.mMinX = -numcells/2; mContext->mGlobalMapState.mBounds.mMaxX = (numcells-1)/2; mContext->mGlobalMapState.mBounds.mMinY = -(numcells-1)/2; mContext->mGlobalMapState.mBounds.mMaxY = numcells/2; osg::ref_ptr image2 (new osg::Image); int width = cellSize*numcells; int height = cellSize*numcells; std::vector data; data.resize(width*height*4, 0); image2->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE); memcpy(image2->data(), &data[0], data.size()); for (std::set >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it) { if (it->first > mContext->mGlobalMapState.mBounds.mMaxX || it->first < mContext->mGlobalMapState.mBounds.mMinX || it->second > mContext->mGlobalMapState.mBounds.mMaxY || it->second < mContext->mGlobalMapState.mBounds.mMinY) { // out of bounds, I think this could happen, since the original engine had a fixed-size map continue; } int imageLeftSrc = mGlobalMapImage->s()/2; int imageTopSrc = mGlobalMapImage->t()/2; imageLeftSrc += it->first * cellSize; imageTopSrc -= it->second * cellSize; int imageLeftDst = width/2; int imageTopDst = height/2; imageLeftDst += it->first * cellSize; imageTopDst -= it->second * cellSize; for (int x=0; xdata(imageLeftSrc+x, imageTopSrc+y, 0); *(unsigned int*)image2->data(imageLeftDst+x, imageTopDst+y, 0) = col; } } std::stringstream ostream; osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) { std::cerr << "can't write global map image, no png readerwriter found" << std::endl; return; } image2->flipVertical(); osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image2, ostream); if (!result.success()) { std::cerr << "can't write global map image: " << result.message() << " code " << result.status() << std::endl; return; } std::string outData = ostream.str(); mContext->mGlobalMapState.mImageData = std::vector(outData.begin(), outData.end()); esm.startRecord(ESM::REC_GMAP); mContext->mGlobalMapState.save(esm); esm.endRecord(ESM::REC_GMAP); } void ConvertCell::read(ESM::ESMReader &esm) { ESM::Cell cell; bool isDeleted = false; cell.load(esm, isDeleted, false); // I wonder what 0x40 does? if (cell.isExterior() && cell.mData.mFlags & 0x20) { mContext->mGlobalMapState.mMarkers.insert(std::make_pair(cell.mData.mX, cell.mData.mY)); } // note if the player is in a nameless exterior cell, we will assign the cellId later based on player position if (cell.mName == mContext->mPlayerCellName) { mContext->mPlayer.mCellId = cell.getCellId(); } Cell newcell; newcell.mCell = cell; // fog of war // seems to be a 1-bit pixel format, 16*16 pixels // TODO: add bleeding of FOW into neighbouring cells (openmw handles this by writing to the textures, // MW handles it when rendering only) unsigned char nam8[32]; // exterior has 1 NAM8, interior can have multiple ones, and have an extra 4 byte flag at the start // (probably offset of that specific fog texture?) while (esm.isNextSub("NAM8")) { if (cell.isExterior()) // TODO: NAM8 occasionally exists for cells that haven't been explored. // are there any flags marking explored cells? mContext->mExploredCells.insert(std::make_pair(cell.mData.mX, cell.mData.mY)); esm.getSubHeader(); if (esm.getSubSize() == 36) { // flag on interiors esm.skip(4); } esm.getExact(nam8, 32); newcell.mFogOfWar.reserve(16*16); for (int x=0; x<16; ++x) { for (int y=0; y<16; ++y) { size_t pos = x*16+y; size_t bytepos = pos/8; assert(bytepos<32); int bit = pos%8; newcell.mFogOfWar.push_back(((nam8[bytepos] >> bit) & (0x1)) ? 0xffffffff : 0x000000ff); } } if (cell.isExterior()) { std::ostringstream filename; filename << "fog_" << cell.mData.mX << "_" << cell.mData.mY << ".tga"; convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, GL_RGBA, filename.str()); } } // moved reference, not handled yet // NOTE: MVRF can also occur in within normal references (importcellref.cpp)? // this does not match the ESM file implementation, // verify if that can happen with ESM files too while (esm.isNextSub("MVRF")) { esm.skipHSub(); // skip MVRF esm.getSubName(); esm.skipHSub(); // skip CNDT } std::vector cellrefs; while (esm.hasMoreSubs() && esm.isNextSub("FRMR")) { CellRef ref; ref.load (esm); cellrefs.push_back(ref); } while (esm.isNextSub("MPCD")) { float notepos[3]; esm.getHT(notepos, 3*sizeof(float)); // Markers seem to be arranged in a 32*32 grid, notepos has grid-indices. // This seems to be the reason markers can't be placed everywhere in interior cells, // i.e. when the grid is exceeded. // Converting the interior markers correctly could be rather tricky, but is probably similar logic // as used for the FoW texture placement, which we need to figure out anyway notepos[1] += 31.f; notepos[0] += 0.5; notepos[1] += 0.5; notepos[0] = 8192 * notepos[0] / 32.f; notepos[1] = 8192 * notepos[1] / 32.f; if (cell.isExterior()) { notepos[0] += 8192 * cell.mData.mX; notepos[1] += 8192 * cell.mData.mY; } // TODO: what encoding is this in? std::string note = esm.getHNString("MPNT"); ESM::CustomMarker marker; marker.mWorldX = notepos[0]; marker.mWorldY = notepos[1]; marker.mNote = note; marker.mCell = cell.getCellId(); mMarkers.push_back(marker); } newcell.mRefs = cellrefs; if (cell.isExterior()) mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell; else mIntCells[cell.mName] = newcell; } void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm) { ESM::Cell esmcell = cell.mCell; esm.startRecord(ESM::REC_CSTA); ESM::CellState csta; csta.mHasFogOfWar = 0; csta.mId = esmcell.getCellId(); csta.mId.save(esm); // TODO csta.mLastRespawn; // shouldn't be needed if we respawn on global schedule like in original MW csta.mWaterLevel = esmcell.mWater; csta.save(esm); for (std::vector::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt) { const CellRef& cellref = *refIt; ESM::CellRef out (cellref); // TODO: use mContext->mCreatures/mNpcs if (!isIndexedRefId(cellref.mIndexedRefId)) { // non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it // this could be any type of object really (even creatures/npcs too) out.mRefID = cellref.mIndexedRefId; std::string idLower = Misc::StringUtils::lowerCase(out.mRefID); ESM::ObjectState objstate; objstate.blank(); objstate.mRef = out; objstate.mRef.mRefID = idLower; objstate.mHasCustomState = false; convertCellRef(cellref, objstate); esm.writeHNT ("OBJE", 0); objstate.save(esm); continue; } else { std::stringstream stream; stream << std::hex << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); int refIndex; stream >> refIndex; out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); std::string idLower = Misc::StringUtils::lowerCase(out.mRefID); std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( std::make_pair(refIndex, out.mRefID)); if (npccIt != mContext->mNpcChanges.end()) { ESM::NpcState objstate; objstate.blank(); objstate.mRef = out; objstate.mRef.mRefID = idLower; // TODO: need more micromanagement here so we don't overwrite values // from the ESM with default values if (cellref.mHasACDT) convertACDT(cellref.mACDT, objstate.mCreatureStats); if (cellref.mHasACSC) convertACSC(cellref.mACSC, objstate.mCreatureStats); convertNpcData(cellref, objstate.mNpcStats); convertNPCC(npccIt->second, objstate); convertCellRef(cellref, objstate); esm.writeHNT ("OBJE", ESM::REC_NPC_); objstate.save(esm); continue; } std::map, CNTC>::const_iterator cntcIt = mContext->mContainerChanges.find( std::make_pair(refIndex, out.mRefID)); if (cntcIt != mContext->mContainerChanges.end()) { ESM::ContainerState objstate; objstate.blank(); objstate.mRef = out; objstate.mRef.mRefID = idLower; convertCNTC(cntcIt->second, objstate); convertCellRef(cellref, objstate); esm.writeHNT ("OBJE", ESM::REC_CONT); objstate.save(esm); continue; } std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( std::make_pair(refIndex, out.mRefID)); if (crecIt != mContext->mCreatureChanges.end()) { ESM::CreatureState objstate; objstate.blank(); objstate.mRef = out; objstate.mRef.mRefID = idLower; // TODO: need more micromanagement here so we don't overwrite values // from the ESM with default values if (cellref.mHasACDT) convertACDT(cellref.mACDT, objstate.mCreatureStats); if (cellref.mHasACSC) convertACSC(cellref.mACSC, objstate.mCreatureStats); convertCREC(crecIt->second, objstate); convertCellRef(cellref, objstate); esm.writeHNT ("OBJE", ESM::REC_CREA); objstate.save(esm); continue; } std::stringstream error; error << "Can't find type for " << cellref.mIndexedRefId << std::endl; throw std::runtime_error(error.str()); } } esm.endRecord(ESM::REC_CSTA); } void ConvertCell::write(ESM::ESMWriter &esm) { for (std::map::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it) writeCell(it->second, esm); for (std::map, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it) writeCell(it->second, esm); for (std::vector::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) { esm.startRecord(ESM::REC_MARK); it->save(esm); esm.endRecord(ESM::REC_MARK); } } } openmw-openmw-0.38.0/apps/essimporter/converter.hpp000066400000000000000000000414551264522266000224670ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CONVERTER_H #define OPENMW_ESSIMPORT_CONVERTER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "importcrec.hpp" #include "importcntc.hpp" #include "importercontext.hpp" #include "importcellref.hpp" #include "importklst.hpp" #include "importgame.hpp" #include "importinfo.hpp" #include "importdial.hpp" #include "importques.hpp" #include "importjour.hpp" #include "importscpt.hpp" #include "convertacdt.hpp" #include "convertnpcc.hpp" #include "convertscpt.hpp" #include "convertplayer.hpp" namespace ESSImport { class Converter { public: /// @return the order for writing this converter's records to the output file, in relation to other converters virtual int getStage() { return 1; } virtual ~Converter() {} void setContext(Context& context) { mContext = &context; } /// @note The load method of ESM records accept the deleted flag as a parameter. /// I don't know can the DELE sub-record appear in saved games, so the deleted flag will be ignored. virtual void read(ESM::ESMReader& esm) { } /// Called after the input file has been read in completely, which may be necessary /// if the conversion process relies on information in other records virtual void write(ESM::ESMWriter& esm) { } protected: Context* mContext; }; /// Default converter: simply reads the record and writes it unmodified to the output template class DefaultConverter : public Converter { public: virtual int getStage() { return 0; } virtual void read(ESM::ESMReader& esm) { T record; bool isDeleted = false; record.load(esm, isDeleted); mRecords[record.mId] = record; } virtual void write(ESM::ESMWriter& esm) { for (typename std::map::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it) { esm.startRecord(T::sRecordId); it->second.save(esm); esm.endRecord(T::sRecordId); } } protected: std::map mRecords; }; class ConvertNPC : public Converter { public: virtual void read(ESM::ESMReader &esm) { ESM::NPC npc; bool isDeleted = false; npc.load(esm, isDeleted); if (npc.mId != "player") { // Handles changes to the NPC struct, but since there is no index here // it will apply to ALL instances of the class. seems to be the reason for the // "feature" in MW where changing AI settings of one guard will change it for all guards of that refID. mContext->mNpcs[Misc::StringUtils::lowerCase(npc.mId)] = npc; } else { mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; mContext->mPlayerBase = npc; std::map empty; // FIXME: player start spells and birthsign spells aren't listed here, // need to fix openmw to account for this for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; // Clear the list now that we've written it, this prevents issues cropping up with // ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal. mContext->mPlayerBase.mSpells.mList.clear(); // Same with inventory. Actually it's strange this would contain something, since there's already an // inventory list in NPCC. There seems to be a fair amount of redundancy in this format. mContext->mPlayerBase.mInventory.mList.clear(); } } }; class ConvertCREA : public Converter { public: virtual void read(ESM::ESMReader &esm) { // See comment in ConvertNPC ESM::Creature creature; bool isDeleted = false; creature.load(esm, isDeleted); mContext->mCreatures[Misc::StringUtils::lowerCase(creature.mId)] = creature; } }; // Do we need ConvertCONT? // I've seen a CONT record in a certain save file, but the container contents in it // were identical to a corresponding CNTC record. See previous comment about redundancy... class ConvertGlobal : public DefaultConverter { public: virtual void read(ESM::ESMReader &esm) { ESM::Global global; bool isDeleted = false; global.load(esm, isDeleted); if (Misc::StringUtils::ciEqual(global.mId, "gamehour")) mContext->mHour = global.mValue.getFloat(); if (Misc::StringUtils::ciEqual(global.mId, "day")) mContext->mDay = global.mValue.getInteger(); if (Misc::StringUtils::ciEqual(global.mId, "month")) mContext->mMonth = global.mValue.getInteger(); if (Misc::StringUtils::ciEqual(global.mId, "year")) mContext->mYear = global.mValue.getInteger(); mRecords[global.mId] = global; } }; class ConvertClass : public DefaultConverter { public: virtual void read(ESM::ESMReader &esm) { ESM::Class class_; bool isDeleted = false; class_.load(esm, isDeleted); if (class_.mId == "NEWCLASSID_CHARGEN") mContext->mCustomPlayerClassName = class_.mName; mRecords[class_.mId] = class_; } }; class ConvertBook : public DefaultConverter { public: virtual void read(ESM::ESMReader &esm) { ESM::Book book; bool isDeleted = false; book.load(esm, isDeleted); if (book.mData.mSkillID == -1) mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId)); mRecords[book.mId] = book; } }; class ConvertNPCC : public Converter { public: virtual void read(ESM::ESMReader &esm) { std::string id = esm.getHNString("NAME"); NPCC npcc; npcc.load(esm); if (id == "PlayerSaveGame") { convertNPCC(npcc, mContext->mPlayer.mObject); } else { int index = npcc.mNPDT.mIndex; mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)); } } }; class ConvertREFR : public Converter { public: virtual void read(ESM::ESMReader &esm) { REFR refr; refr.load(esm); assert(refr.mRefID == "PlayerSaveGame"); mContext->mPlayer.mObject.mPosition = refr.mPos; ESM::CreatureStats& cStats = mContext->mPlayer.mObject.mCreatureStats; convertACDT(refr.mActorData.mACDT, cStats); ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats; convertNpcData(refr.mActorData, npcStats); mSelectedSpell = refr.mActorData.mSelectedSpell; if (!refr.mActorData.mSelectedEnchantItem.empty()) { ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory; for (unsigned int i=0; imPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam); } virtual void write(ESM::ESMWriter &esm) { esm.startRecord(ESM::REC_CAM_); esm.writeHNT("FIRS", mFirstPersonCam); esm.endRecord(ESM::REC_CAM_); } private: bool mFirstPersonCam; }; class ConvertCNTC : public Converter { virtual void read(ESM::ESMReader &esm) { std::string id = esm.getHNString("NAME"); CNTC cntc; cntc.load(esm); mContext->mContainerChanges.insert(std::make_pair(std::make_pair(cntc.mIndex,id), cntc)); } }; class ConvertCREC : public Converter { public: virtual void read(ESM::ESMReader &esm) { std::string id = esm.getHNString("NAME"); CREC crec; crec.load(esm); mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec)); } }; class ConvertFMAP : public Converter { public: virtual void read(ESM::ESMReader &esm); virtual void write(ESM::ESMWriter &esm); private: osg::ref_ptr mGlobalMapImage; }; class ConvertCell : public Converter { public: virtual void read(ESM::ESMReader& esm); virtual void write(ESM::ESMWriter& esm); private: struct Cell { ESM::Cell mCell; std::vector mRefs; std::vector mFogOfWar; }; std::map mIntCells; std::map, Cell> mExtCells; std::vector mMarkers; void writeCell(const Cell& cell, ESM::ESMWriter &esm); }; class ConvertKLST : public Converter { public: virtual void read(ESM::ESMReader& esm) { KLST klst; klst.load(esm); mKillCounter = klst.mKillCounter; mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills; } virtual void write(ESM::ESMWriter &esm) { esm.startRecord(ESM::REC_DCOU); for (std::map::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it) { esm.writeHNString("ID__", it->first); esm.writeHNT ("COUN", it->second); } esm.endRecord(ESM::REC_DCOU); } private: std::map mKillCounter; }; class ConvertFACT : public Converter { public: virtual void read(ESM::ESMReader& esm) { ESM::Faction faction; bool isDeleted = false; faction.load(esm, isDeleted); std::string id = Misc::StringUtils::lowerCase(faction.mId); for (std::map::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it) { std::string faction2 = Misc::StringUtils::lowerCase(it->first); mContext->mDialogueState.mChangedFactionReaction[id].insert(std::make_pair(faction2, it->second)); } } }; /// Stolen items class ConvertSTLN : public Converter { public: virtual void read(ESM::ESMReader &esm) { std::string itemid = esm.getHNString("NAME"); Misc::StringUtils::lowerCaseInPlace(itemid); while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM")) { if (esm.retSubName().toString() == "FNAM") { std::string factionid = esm.getHString(); mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(factionid), true)); } else { std::string ownerid = esm.getHString(); mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false)); } } } virtual void write(ESM::ESMWriter &esm) { ESM::StolenItems items; for (std::map >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it) { std::map, int> owners; for (std::set::const_iterator ownerIt = it->second.begin(); ownerIt != it->second.end(); ++ownerIt) { owners.insert(std::make_pair(std::make_pair(ownerIt->first, ownerIt->second) // Since OpenMW doesn't suffer from the owner contamination bug, // it needs a count argument. But for legacy savegames, we don't know // this count, so must assume all items of that ID are stolen, // like vanilla MW did. ,std::numeric_limits::max())); } items.mStolenItems.insert(std::make_pair(it->first, owners)); } esm.startRecord(ESM::REC_STLN); items.write(esm); esm.endRecord(ESM::REC_STLN); } private: typedef std::pair Owner; // std::map > mStolenItems; }; /// Seen responses for a dialogue topic? /// Each DIAL record is followed by a number of INFO records, I believe, just like in ESMs /// Dialogue conversion problems: /// - Journal is stored in one continuous HTML markup rather than each entry separately with associated info ID. /// - Seen dialogue responses only store the INFO id, rather than the fulltext. /// - Quest stages only store the INFO id, rather than the journal entry fulltext. class ConvertINFO : public Converter { public: virtual void read(ESM::ESMReader& esm) { INFO info; info.load(esm); } }; class ConvertDIAL : public Converter { public: virtual void read(ESM::ESMReader& esm) { std::string id = esm.getHNString("NAME"); DIAL dial; dial.load(esm); if (dial.mIndex > 0) mDials[id] = dial; } virtual void write(ESM::ESMWriter &esm) { for (std::map::const_iterator it = mDials.begin(); it != mDials.end(); ++it) { esm.startRecord(ESM::REC_QUES); ESM::QuestState state; state.mFinished = 0; state.mState = it->second.mIndex; state.mTopic = Misc::StringUtils::lowerCase(it->first); state.save(esm); esm.endRecord(ESM::REC_QUES); } } private: std::map mDials; }; class ConvertQUES : public Converter { public: virtual void read(ESM::ESMReader& esm) { std::string id = esm.getHNString("NAME"); QUES quest; quest.load(esm); } }; class ConvertJOUR : public Converter { public: virtual void read(ESM::ESMReader& esm) { JOUR journal; journal.load(esm); } }; class ConvertGAME : public Converter { public: ConvertGAME() : mHasGame(false) {} virtual void read(ESM::ESMReader &esm) { mGame.load(esm); mHasGame = true; } int validateWeatherID(int weatherID) { if(weatherID >= -1 && weatherID < 10) { return weatherID; } else { std::stringstream error; error << "Invalid weather ID:" << weatherID << std::endl; throw std::runtime_error(error.str()); } } virtual void write(ESM::ESMWriter &esm) { if (!mHasGame) return; esm.startRecord(ESM::REC_WTHR); ESM::WeatherState weather; weather.mTimePassed = 0.0f; weather.mFastForward = false; weather.mWeatherUpdateTime = mGame.mGMDT.mTimeOfNextTransition - mContext->mHour; weather.mTransitionFactor = 1 - (mGame.mGMDT.mWeatherTransition / 100.0f); weather.mCurrentWeather = validateWeatherID(mGame.mGMDT.mCurrentWeather); weather.mNextWeather = validateWeatherID(mGame.mGMDT.mNextWeather); weather.mQueuedWeather = -1; // TODO: Determine how ModRegion modifiers are saved in Morrowind. weather.save(esm); esm.endRecord(ESM::REC_WTHR); } private: bool mHasGame; GAME mGame; }; /// Running global script class ConvertSCPT : public Converter { public: virtual void read(ESM::ESMReader &esm) { SCPT script; script.load(esm); ESM::GlobalScript out; convertSCPT(script, out); mScripts.push_back(out); } virtual void write(ESM::ESMWriter &esm) { for (std::vector::const_iterator it = mScripts.begin(); it != mScripts.end(); ++it) { esm.startRecord(ESM::REC_GSCR); it->save(esm); esm.endRecord(ESM::REC_GSCR); } } private: std::vector mScripts; }; } #endif openmw-openmw-0.38.0/apps/essimporter/convertinventory.cpp000066400000000000000000000023671264522266000241100ustar00rootroot00000000000000#include "convertinventory.hpp" #include namespace ESSImport { void convertInventory(const Inventory &inventory, ESM::InventoryState &state) { int index = 0; for (std::vector::const_iterator it = inventory.mItems.begin(); it != inventory.mItems.end(); ++it) { ESM::ObjectState objstate; objstate.blank(); objstate.mRef = *it; objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags state.mItems.push_back(objstate); if (it->mRelativeEquipmentSlot != -1) // Note we should really write the absolute slot here, which we do not know about // Not a big deal, OpenMW will auto-correct to a valid slot, the only problem is when // an item could be equipped in two different slots (e.g. equipped two rings) state.mEquipmentSlots[index] = it->mRelativeEquipmentSlot; ++index; } } } openmw-openmw-0.38.0/apps/essimporter/convertinventory.hpp000066400000000000000000000004351264522266000241070ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CONVERTINVENTORY_H #define OPENMW_ESSIMPORT_CONVERTINVENTORY_H #include "importinventory.hpp" #include namespace ESSImport { void convertInventory (const Inventory& inventory, ESM::InventoryState& state); } #endif openmw-openmw-0.38.0/apps/essimporter/convertnpcc.cpp000066400000000000000000000005471264522266000227740ustar00rootroot00000000000000#include "convertnpcc.hpp" #include "convertinventory.hpp" namespace ESSImport { void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) { npcState.mNpcStats.mDisposition = npcc.mNPDT.mDisposition; npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; convertInventory(npcc.mInventory, npcState.mInventory); } } openmw-openmw-0.38.0/apps/essimporter/convertnpcc.hpp000066400000000000000000000003661264522266000230000ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CONVERTNPCC_H #define OPENMW_ESSIMPORT_CONVERTNPCC_H #include "importnpcc.hpp" #include namespace ESSImport { void convertNPCC (const NPCC& npcc, ESM::NpcState& npcState); } #endif openmw-openmw-0.38.0/apps/essimporter/convertplayer.cpp000066400000000000000000000031571264522266000233450ustar00rootroot00000000000000#include "convertplayer.hpp" #include namespace ESSImport { void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector& outDialogueTopics, bool& firstPersonCam) { out.mBirthsign = pcdt.mBirthsign; out.mObject.mNpcStats.mBounty = pcdt.mBounty; for (std::vector::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it) { ESM::NpcStats::Faction faction; faction.mExpelled = (it->mFlags & 0x2) != 0; faction.mRank = it->mRank; faction.mReputation = it->mReputation; out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction; } for (int i=0; i<8; ++i) out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i]; for (int i=0; i<27; ++i) out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i]; out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress; if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Weapon) out.mObject.mCreatureStats.mDrawState = 1; if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell) out.mObject.mCreatureStats.mDrawState = 2; firstPersonCam = (pcdt.mPNAM.mCameraState == PCDT::CameraState_FirstPerson); for (std::vector::const_iterator it = pcdt.mKnownDialogueTopics.begin(); it != pcdt.mKnownDialogueTopics.end(); ++it) { outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it)); } } } openmw-openmw-0.38.0/apps/essimporter/convertplayer.hpp000066400000000000000000000004651264522266000233510ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CONVERTPLAYER_H #define OPENMW_ESSIMPORT_CONVERTPLAYER_H #include "importplayer.hpp" #include namespace ESSImport { void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector& outDialogueTopics, bool& firstPersonCam); } #endif openmw-openmw-0.38.0/apps/essimporter/convertscpt.cpp000066400000000000000000000005501264522266000230140ustar00rootroot00000000000000#include "convertscpt.hpp" #include #include "convertscri.hpp" namespace ESSImport { void convertSCPT(const SCPT &scpt, ESM::GlobalScript &out) { out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString()); out.mRunning = scpt.mRunning; convertSCRI(scpt.mSCRI, out.mLocals); } } openmw-openmw-0.38.0/apps/essimporter/convertscpt.hpp000066400000000000000000000003641264522266000230240ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CONVERTSCPT_H #define OPENMW_ESSIMPORT_CONVERTSCPT_H #include #include "importscpt.hpp" namespace ESSImport { void convertSCPT(const SCPT& scpt, ESM::GlobalScript& out); } #endif openmw-openmw-0.38.0/apps/essimporter/convertscri.cpp000066400000000000000000000016641264522266000230120ustar00rootroot00000000000000#include "convertscri.hpp" #include namespace { template void storeVariables(const std::vector& variables, ESM::Locals& locals, const std::string& scriptname) { for (typename std::vector::const_iterator it = variables.begin(); it != variables.end(); ++it) { ESM::Variant val(*it); val.setType(VariantType); locals.mVariables.push_back(std::make_pair(std::string(), val)); } } } namespace ESSImport { void convertSCRI(const SCRI &scri, ESM::Locals &locals) { // order *is* important, as we do not have variable names available in this format storeVariables (scri.mShorts, locals, scri.mScript); storeVariables (scri.mLongs, locals, scri.mScript); storeVariables (scri.mFloats, locals, scri.mScript); } } openmw-openmw-0.38.0/apps/essimporter/convertscri.hpp000066400000000000000000000004341264522266000230110ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CONVERTSCRI_H #define OPENMW_ESSIMPORT_CONVERTSCRI_H #include "importscri.hpp" #include namespace ESSImport { /// Convert script variable assignments void convertSCRI (const SCRI& scri, ESM::Locals& locals); } #endif openmw-openmw-0.38.0/apps/essimporter/importacdt.cpp000066400000000000000000000073011264522266000226110ustar00rootroot00000000000000#include "importacdt.hpp" #include #include namespace ESSImport { void ActorData::load(ESM::ESMReader &esm) { if (esm.isNextSub("ACTN")) { /* Activation flags: ActivationFlag_UseEnabled = 1 ActivationFlag_OnActivate = 2 ActivationFlag_OnDeath = 10h ActivationFlag_OnKnockout = 20h ActivationFlag_OnMurder = 40h ActivationFlag_DoorOpening = 100h ActivationFlag_DoorClosing = 200h ActivationFlag_DoorJammedOpening = 400h ActivationFlag_DoorJammedClosing = 800h */ esm.skipHSub(); } if (esm.isNextSub("STPR")) esm.skipHSub(); if (esm.isNextSub("MNAM")) esm.skipHSub(); bool isDeleted = false; ESM::CellRef::loadData(esm, isDeleted); mHasACDT = false; if (esm.isNextSub("ACDT")) { mHasACDT = true; esm.getHT(mACDT); } mHasACSC = false; if (esm.isNextSub("ACSC")) { mHasACSC = true; esm.getHT(mACSC); } if (esm.isNextSub("ACSL")) esm.skipHSubSize(112); if (esm.isNextSub("CSTN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? if (esm.isNextSub("LSTN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? // unsure at which point between LSTN and TGTN if (esm.isNextSub("CSHN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? // unsure if before or after CSTN/LSTN if (esm.isNextSub("LSHN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? while (esm.isNextSub("TGTN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? while (esm.isNextSub("FGTN")) esm.getHString(); // fight target? // unsure at which point between TGTN and CRED if (esm.isNextSub("AADT")) { // occured when a creature was in the middle of its attack, 44 bytes esm.skipHSub(); } // unsure at which point between FGTN and CHRD if (esm.isNextSub("PWPC")) esm.skipHSub(); if (esm.isNextSub("PWPS")) esm.skipHSub(); if (esm.isNextSub("WNAM")) { std::string id = esm.getHString(); if (esm.isNextSub("XNAM")) mSelectedEnchantItem = esm.getHString(); else mSelectedSpell = id; if (esm.isNextSub("YNAM")) esm.skipHSub(); // 4 byte, 0 } while (esm.isNextSub("APUD")) { // used power esm.getSubHeader(); std::string id = esm.getString(32); (void)id; // timestamp can't be used: this is the total hours passed, calculated by // timestamp = 24 * (365 * year + cumulativeDays[month] + day) // unfortunately cumulativeDays[month] is not clearly defined, // in the (non-MCP) vanilla version the first month was missing, but MCP added it. double timestamp; esm.getT(timestamp); } // FIXME: not all actors have this, add flag if (esm.isNextSub("CHRD")) // npc only esm.getHExact(mSkills, 27*2*sizeof(int)); if (esm.isNextSub("CRED")) // creature only esm.getHExact(mCombatStats, 3*2*sizeof(int)); mSCRI.load(esm); if (esm.isNextSub("ND3D")) esm.skipHSub(); if (esm.isNextSub("ANIS")) esm.skipHSub(); } } openmw-openmw-0.38.0/apps/essimporter/importacdt.hpp000066400000000000000000000040741264522266000226220ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_ACDT_H #define OPENMW_ESSIMPORT_ACDT_H #include #include #include "importscri.hpp" namespace ESM { class ESMReader; } namespace ESSImport { enum ACDTFlags { TalkedToPlayer = 0x4, Attacked = 0x100, Unknown = 0x200 }; enum ACSCFlags { Dead = 0x2 }; /// Actor data, shared by (at least) REFR and CellRef #pragma pack(push) #pragma pack(1) struct ACDT { // Note, not stored at *all*: // - Level changes are lost on reload, except for the player (there it's in the NPC record). unsigned char mUnknown[12]; unsigned int mFlags; float mBreathMeter; // Seconds left before drowning unsigned char mUnknown2[20]; float mDynamic[3][2]; unsigned char mUnknown3[16]; float mAttributes[8][2]; float mMagicEffects[27]; // Effect attributes: https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attributes unsigned char mUnknown4[4]; unsigned int mGoldPool; unsigned char mCountDown; // seen the same value as in ACSC.mCorpseClearCountdown, maybe // this one is for respawning? unsigned char mUnknown5[3]; }; struct ACSC { unsigned char mUnknown1[17]; unsigned char mFlags; // ACSCFlags unsigned char mUnknown2[22]; unsigned char mCorpseClearCountdown; // hours? unsigned char mUnknown3[71]; }; #pragma pack(pop) struct ActorData : public ESM::CellRef { bool mHasACDT; ACDT mACDT; bool mHasACSC; ACSC mACSC; int mSkills[27][2]; // skills, base and modified // creature combat stats, base and modified // I think these can be ignored in the conversion, because it is not possible // to change them ingame int mCombatStats[3][2]; std::string mSelectedSpell; std::string mSelectedEnchantItem; SCRI mSCRI; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importcellref.cpp000066400000000000000000000032011264522266000233050ustar00rootroot00000000000000#include "importcellref.hpp" #include namespace ESSImport { void CellRef::load(ESM::ESMReader &esm) { blank(); // (FRMR subrecord name is already read by the loop in ConvertCell) esm.getHT(mRefNum.mIndex); // FRMR // this is required since openmw supports more than 255 content files int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24; mRefNum.mContentFile = pluginIndex-1; mRefNum.mIndex &= 0x00ffffff; mIndexedRefId = esm.getHNString("NAME"); ActorData::load(esm); if (esm.isNextSub("LVCR")) { // occurs on levelled creature spawner references // probably some identifier for the creature that has been spawned? unsigned char lvcr; esm.getHT(lvcr); //std::cout << "LVCR: " << (int)lvcr << std::endl; } mEnabled = true; esm.getHNOT(mEnabled, "ZNAM"); // DATA should occur for all references, except levelled creature spawners // I've seen DATA *twice* on a creature record, and with the exact same content too! weird // alarmvoi0000.ess esm.getHNOT(mPos, "DATA", 24); esm.getHNOT(mPos, "DATA", 24); mDeleted = 0; if (esm.isNextSub("DELE")) { unsigned int deleted; esm.getHT(deleted); mDeleted = ((deleted >> 24) & 0x2) != 0; // the other 3 bytes seem to be uninitialized garbage } if (esm.isNextSub("MVRF")) { esm.skipHSub(); esm.getSubName(); esm.skipHSub(); } } } openmw-openmw-0.38.0/apps/essimporter/importcellref.hpp000066400000000000000000000006631264522266000233230ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CELLREF_H #define OPENMW_ESSIMPORT_CELLREF_H #include #include #include "importacdt.hpp" namespace ESM { class ESMReader; } namespace ESSImport { struct CellRef : public ActorData { std::string mIndexedRefId; std::string mScript; bool mEnabled; bool mDeleted; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importcntc.cpp000066400000000000000000000003521264522266000226240ustar00rootroot00000000000000#include "importcntc.hpp" #include namespace ESSImport { void CNTC::load(ESM::ESMReader &esm) { mIndex = 0; esm.getHNT(mIndex, "INDX"); mInventory.load(esm); } } openmw-openmw-0.38.0/apps/essimporter/importcntc.hpp000066400000000000000000000005221264522266000226300ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_IMPORTCNTC_H #define OPENMW_ESSIMPORT_IMPORTCNTC_H #include "importinventory.hpp" namespace ESM { class ESMReader; } namespace ESSImport { /// Changed container contents struct CNTC { int mIndex; Inventory mInventory; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importcrec.cpp000066400000000000000000000011131264522266000226050ustar00rootroot00000000000000#include "importcrec.hpp" #include namespace ESSImport { void CREC::load(ESM::ESMReader &esm) { esm.getHNT(mIndex, "INDX"); // equivalent of ESM::Creature XSCL? probably don't have to convert this, // since the value can't be changed float scale; esm.getHNOT(scale, "XSCL"); while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") || esm.isNextSub("AI_A")) mAiPackages.add(esm); mInventory.load(esm); } } openmw-openmw-0.38.0/apps/essimporter/importcrec.hpp000066400000000000000000000006151264522266000226200ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CREC_H #define OPENMW_ESSIMPORT_CREC_H #include "importinventory.hpp" #include namespace ESM { class ESMReader; } namespace ESSImport { /// Creature changes struct CREC { int mIndex; Inventory mInventory; ESM::AIPackageList mAiPackages; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importdial.cpp000066400000000000000000000011171264522266000226060ustar00rootroot00000000000000#include "importdial.hpp" #include namespace ESSImport { void DIAL::load(ESM::ESMReader &esm) { // See ESM::Dialogue::Type enum, not sure why we would need this here though int type = 0; esm.getHNOT(type, "DATA"); // Deleted dialogue in a savefile. No clue what this means... int deleted = 0; esm.getHNOT(deleted, "DELE"); mIndex = 0; // *should* always occur except when the dialogue is deleted, but leaving it optional just in case... esm.getHNOT(mIndex, "XIDX"); } } openmw-openmw-0.38.0/apps/essimporter/importdial.hpp000066400000000000000000000004011264522266000226060ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_IMPORTDIAL_H #define OPENMW_ESSIMPORT_IMPORTDIAL_H namespace ESM { class ESMReader; } namespace ESSImport { struct DIAL { int mIndex; // Journal index void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importer.cpp000066400000000000000000000410301264522266000223010ustar00rootroot00000000000000#include "importer.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "importercontext.hpp" #include "converter.hpp" namespace { void writeScreenshot(const ESM::Header& fileHeader, ESM::SavedGame& out) { if (fileHeader.mSCRS.size() != 128*128*4) { std::cerr << "unexpected screenshot size " << std::endl; return; } osg::ref_ptr image (new osg::Image); image->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE); // need to convert pixel format from BGRA to RGB as the jpg readerwriter doesn't support it otherwise std::vector::const_iterator it = fileHeader.mSCRS.begin(); for (int y=0; y<128; ++y) { for (int x=0; x<128; ++x) { *(image->data(x,y)+2) = *it++; *(image->data(x,y)+1) = *it++; *image->data(x,y) = *it++; ++it; // skip alpha } } image->flipVertical(); std::stringstream ostream; osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg"); if (!readerwriter) { std::cerr << "can't write screenshot: no jpg readerwriter found" << std::endl; return; } osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image, ostream); if (!result.success()) { std::cerr << "can't write screenshot: " << result.message() << " code " << result.status() << std::endl; return; } std::string data = ostream.str(); out.mScreenshot = std::vector(data.begin(), data.end()); } } namespace ESSImport { Importer::Importer(const std::string &essfile, const std::string &outfile, const std::string &encoding) : mEssFile(essfile) , mOutFile(outfile) , mEncoding(encoding) { } struct File { struct Subrecord { std::string mName; size_t mFileOffset; std::vector mData; }; struct Record { std::string mName; size_t mFileOffset; std::vector mSubrecords; }; std::vector mRecords; }; void read(const std::string& filename, File& file) { ESM::ESMReader esm; esm.open(filename); while (esm.hasMoreRecs()) { ESM::NAME n = esm.getRecName(); esm.getRecHeader(); File::Record rec; rec.mName = n.toString(); rec.mFileOffset = esm.getFileOffset(); while (esm.hasMoreSubs()) { File::Subrecord sub; esm.getSubName(); esm.getSubHeader(); sub.mFileOffset = esm.getFileOffset(); sub.mName = esm.retSubName().toString(); sub.mData.resize(esm.getSubSize()); esm.getExact(&sub.mData[0], sub.mData.size()); rec.mSubrecords.push_back(sub); } file.mRecords.push_back(rec); } } void Importer::compare() { // data that always changes (and/or is already fully decoded) should be blacklisted std::set > blacklist; blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour blacklist.insert(std::make_pair("REFR", "DATA")); // player position blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war blacklist.insert(std::make_pair("GAME", "GMDT")); // weather data, current time always changes blacklist.insert(std::make_pair("CELL", "DELE")); // first 3 bytes are uninitialized // this changes way too often, name suggests some renderer internal data? blacklist.insert(std::make_pair("CELL", "ND3D")); blacklist.insert(std::make_pair("REFR", "ND3D")); File file1; read(mEssFile, file1); File file2; read(mOutFile, file2); // todo rename variable // FIXME: use max(size1, size2) for (unsigned int i=0; i= file2.mRecords.size()) { std::ios::fmtflags f(std::cout.flags()); std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset << std::endl; std::cout.flags(f); return; } File::Record rec2 = file2.mRecords[i]; if (rec.mName != rec2.mName) { std::ios::fmtflags f(std::cout.flags()); std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl; std::cout.flags(f); return; // TODO: try to recover } // FIXME: use max(size1, size2) for (unsigned int j=0; j= rec2.mSubrecords.size()) { std::ios::fmtflags f(std::cout.flags()); std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset << std::endl; std::cout.flags(f); return; } File::Subrecord sub2 = rec2.mSubrecords[j]; if (sub.mName != sub2.mName) { std::ios::fmtflags f(std::cout.flags()); std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset << " (2) 0x" << sub2.mFileOffset << std::endl; std::cout.flags(f); break; // TODO: try to recover } if (sub.mData != sub2.mData) { if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end()) continue; std::ios::fmtflags f(std::cout.flags()); std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset << " (2) 0x" << sub2.mFileOffset << std::endl; std::cout << "Data 1:" << std::endl; for (unsigned int k=0; k= sub2.mData.size() || sub2.mData[k] != sub.mData[k]) different = true; if (different) std::cout << "\033[033m"; std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub.mData[k] << " "; if (different) std::cout << "\033[0m"; } std::cout << std::endl; std::cout << "Data 2:" << std::endl; for (unsigned int k=0; k= sub.mData.size() || sub.mData[k] != sub2.mData[k]) different = true; if (different) std::cout << "\033[033m"; std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub2.mData[k] << " "; if (different) std::cout << "\033[0m"; } std::cout << std::endl; std::cout.flags(f); } } } } void Importer::run() { ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncoding)); ESM::ESMReader esm; esm.open(mEssFile); esm.setEncoder(&encoder); Context context; const ESM::Header& header = esm.getHeader(); context.mPlayerCellName = header.mGameData.mCurrentCell.toString(); const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value; const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value; const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value; const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value; const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value; std::map > converters; converters[ESM::REC_GLOB] = boost::shared_ptr(new ConvertGlobal()); converters[ESM::REC_BOOK] = boost::shared_ptr(new ConvertBook()); converters[ESM::REC_NPC_] = boost::shared_ptr(new ConvertNPC()); converters[ESM::REC_CREA] = boost::shared_ptr(new ConvertCREA()); converters[ESM::REC_NPCC] = boost::shared_ptr(new ConvertNPCC()); converters[ESM::REC_CREC] = boost::shared_ptr(new ConvertCREC()); converters[recREFR ] = boost::shared_ptr(new ConvertREFR()); converters[recPCDT ] = boost::shared_ptr(new ConvertPCDT()); converters[recFMAP ] = boost::shared_ptr(new ConvertFMAP()); converters[recKLST ] = boost::shared_ptr(new ConvertKLST()); converters[recSTLN ] = boost::shared_ptr(new ConvertSTLN()); converters[recGAME ] = boost::shared_ptr(new ConvertGAME()); converters[ESM::REC_CELL] = boost::shared_ptr(new ConvertCell()); converters[ESM::REC_ALCH] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CLAS] = boost::shared_ptr(new ConvertClass()); converters[ESM::REC_SPEL] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_ARMO] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_WEAP] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CLOT] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_ENCH] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_WEAP] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVC] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CNTC] = boost::shared_ptr(new ConvertCNTC()); converters[ESM::REC_FACT] = boost::shared_ptr(new ConvertFACT()); converters[ESM::REC_INFO] = boost::shared_ptr(new ConvertINFO()); converters[ESM::REC_DIAL] = boost::shared_ptr(new ConvertDIAL()); converters[ESM::REC_QUES] = boost::shared_ptr(new ConvertQUES()); converters[recJOUR ] = boost::shared_ptr(new ConvertJOUR()); converters[ESM::REC_SCPT] = boost::shared_ptr(new ConvertSCPT()); // TODO: // - REGN (weather in certain regions?) // - VFXM // - SPLM (active spell effects) // - PROJ (magic projectiles in air) std::set unknownRecords; for (std::map >::const_iterator it = converters.begin(); it != converters.end(); ++it) { it->second->setContext(context); } while (esm.hasMoreRecs()) { ESM::NAME n = esm.getRecName(); esm.getRecHeader(); std::map >::iterator it = converters.find(n.val); if (it != converters.end()) { it->second->read(esm); } else { if (unknownRecords.insert(n.val).second) { std::ios::fmtflags f(std::cerr.flags()); std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl; std::cerr.flags(f); } esm.skipRecord(); } } ESM::ESMWriter writer; writer.setFormat (ESM::SavedGame::sCurrentFormat); std::ofstream stream(mOutFile.c_str(), std::ios::binary); // all unused writer.setVersion(0); writer.setType(0); writer.setAuthor(""); writer.setDescription(""); writer.setRecordCount (0); for (std::vector::const_iterator it = header.mMaster.begin(); it != header.mMaster.end(); ++it) writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0 writer.save (stream); ESM::SavedGame profile; for (std::vector::const_iterator it = header.mMaster.begin(); it != header.mMaster.end(); ++it) { profile.mContentFiles.push_back(it->name); } profile.mDescription = esm.getDesc(); profile.mInGameTime.mDay = context.mDay; profile.mInGameTime.mGameHour = context.mHour; profile.mInGameTime.mMonth = context.mMonth; profile.mInGameTime.mYear = context.mYear; profile.mPlayerCell = header.mGameData.mCurrentCell.toString(); if (context.mPlayerBase.mClass == "NEWCLASSID_CHARGEN") profile.mPlayerClassName = context.mCustomPlayerClassName; else profile.mPlayerClassId = context.mPlayerBase.mClass; profile.mPlayerLevel = context.mPlayerBase.mNpdt52.mLevel; profile.mPlayerName = header.mGameData.mPlayerName.toString(); writeScreenshot(header, profile); writer.startRecord (ESM::REC_SAVE); profile.save (writer); writer.endRecord (ESM::REC_SAVE); // Writing order should be Dynamic Store -> Cells -> Player, // so that references to dynamic records can be recognized when loading for (std::map >::const_iterator it = converters.begin(); it != converters.end(); ++it) { if (it->second->getStage() != 0) continue; it->second->write(writer); } writer.startRecord(ESM::REC_NPC_); context.mPlayerBase.mId = "player"; context.mPlayerBase.save(writer); writer.endRecord(ESM::REC_NPC_); for (std::map >::const_iterator it = converters.begin(); it != converters.end(); ++it) { if (it->second->getStage() != 1) continue; it->second->write(writer); } writer.startRecord(ESM::REC_PLAY); if (context.mPlayer.mCellId.mPaged) { // exterior cell -> determine cell coordinates based on position const int cellSize = 8192; int cellX = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize)); int cellY = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[1] / cellSize)); context.mPlayer.mCellId.mIndex.mX = cellX; context.mPlayer.mCellId.mIndex.mY = cellY; } context.mPlayer.save(writer); writer.endRecord(ESM::REC_PLAY); writer.startRecord (ESM::REC_DIAS); context.mDialogueState.save(writer); writer.endRecord(ESM::REC_DIAS); } } openmw-openmw-0.38.0/apps/essimporter/importer.hpp000066400000000000000000000006541264522266000223150ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORTER_IMPORTER_H #define OPENMW_ESSIMPORTER_IMPORTER_H #include namespace ESSImport { class Importer { public: Importer(const std::string& essfile, const std::string& outfile, const std::string& encoding); void run(); void compare(); private: std::string mEssFile; std::string mOutFile; std::string mEncoding; }; } #endif openmw-openmw-0.38.0/apps/essimporter/importercontext.cpp000066400000000000000000000000001264522266000236760ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/essimporter/importercontext.hpp000066400000000000000000000040671264522266000237240ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_CONTEXT_H #define OPENMW_ESSIMPORT_CONTEXT_H #include #include #include #include #include #include #include #include "importnpcc.hpp" #include "importcrec.hpp" #include "importcntc.hpp" #include "importplayer.hpp" namespace ESSImport { struct Context { // set from the TES3 header std::string mPlayerCellName; ESM::Player mPlayer; ESM::NPC mPlayerBase; std::string mCustomPlayerClassName; ESM::DialogueState mDialogueState; // cells which should show an explored overlay on the global map std::set > mExploredCells; ESM::GlobalMap mGlobalMapState; int mDay, mMonth, mYear; float mHour; // key std::map, CREC> mCreatureChanges; std::map, NPCC> mNpcChanges; std::map, CNTC> mContainerChanges; std::map mCreatures; std::map mNpcs; Context() : mDay(0) , mMonth(0) , mYear(0) , mHour(0.f) { mPlayer.mAutoMove = 0; ESM::CellId playerCellId; playerCellId.mPaged = true; playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0; mPlayer.mCellId = playerCellId; //mPlayer.mLastKnownExteriorPosition mPlayer.mHasMark = 0; // TODO mPlayer.mCurrentCrimeId = 0; // TODO mPlayer.mObject.blank(); mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame mGlobalMapState.mBounds.mMinX = 0; mGlobalMapState.mBounds.mMaxX = 0; mGlobalMapState.mBounds.mMinY = 0; mGlobalMapState.mBounds.mMaxY = 0; } }; } #endif openmw-openmw-0.38.0/apps/essimporter/importgame.cpp000066400000000000000000000010741264522266000226100ustar00rootroot00000000000000#include "importgame.hpp" #include namespace ESSImport { void GAME::load(ESM::ESMReader &esm) { esm.getSubNameIs("GMDT"); esm.getSubHeader(); if (esm.getSubSize() == 92) { esm.getExact(&mGMDT, 92); mGMDT.mSecundaPhase = 0; } else if (esm.getSubSize() == 96) { esm.getT(mGMDT); } else esm.fail("unexpected subrecord size for GAME.GMDT"); mGMDT.mWeatherTransition &= (0x000000ff); mGMDT.mSecundaPhase &= (0x000000ff); mGMDT.mMasserPhase &= (0x000000ff); } } openmw-openmw-0.38.0/apps/essimporter/importgame.hpp000066400000000000000000000012711264522266000226140ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_GAME_H #define OPENMW_ESSIMPORT_GAME_H namespace ESM { class ESMReader; } namespace ESSImport { /// Weather data struct GAME { struct GMDT { char mCellName[64]; int mFogColour; float mFogDensity; int mCurrentWeather, mNextWeather; int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition int mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage }; GMDT mGMDT; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importinfo.cpp000066400000000000000000000003531264522266000226310ustar00rootroot00000000000000#include "importinfo.hpp" #include namespace ESSImport { void INFO::load(ESM::ESMReader &esm) { mInfo = esm.getHNString("INAM"); mActorRefId = esm.getHNString("ACDT"); } } openmw-openmw-0.38.0/apps/essimporter/importinfo.hpp000066400000000000000000000004541264522266000226400ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_IMPORTINFO_H #define OPENMW_ESSIMPORT_IMPORTINFO_H #include namespace ESM { class ESMReader; } namespace ESSImport { struct INFO { std::string mInfo; std::string mActorRefId; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importinventory.cpp000066400000000000000000000042151264522266000237340ustar00rootroot00000000000000#include "importinventory.hpp" #include #include #include namespace ESSImport { void Inventory::load(ESM::ESMReader &esm) { while (esm.isNextSub("NPCO")) { ESM::ContItem contItem; esm.getHT(contItem); InventoryItem item; item.mId = contItem.mItem.toString(); item.mCount = contItem.mCount; item.mRelativeEquipmentSlot = -1; // seems that a stack of items can have a set of subrecords for each item? rings0000.ess // doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place? // I guess we should double check the stacking logic in OpenMW for (int i=0;i= int(mItems.size())) esm.fail("equipment item index out of range"); // appears to be a relative index for only the *possible* slots this item can be equipped in, // i.e. 0 most of the time int slotIndex; esm.getT(slotIndex); mItems[itemIndex].mRelativeEquipmentSlot = slotIndex; } } } openmw-openmw-0.38.0/apps/essimporter/importinventory.hpp000066400000000000000000000010721264522266000237370ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_IMPORTINVENTORY_H #define OPENMW_ESSIMPORT_IMPORTINVENTORY_H #include #include #include #include "importscri.hpp" namespace ESM { class ESMReader; } namespace ESSImport { struct Inventory { struct InventoryItem : public ESM::CellRef { std::string mId; int mCount; int mRelativeEquipmentSlot; SCRI mSCRI; }; std::vector mItems; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importjour.cpp000066400000000000000000000002741264522266000226570ustar00rootroot00000000000000#include "importjour.hpp" #include namespace ESSImport { void JOUR::load(ESM::ESMReader &esm) { mText = esm.getHNString("NAME"); } } openmw-openmw-0.38.0/apps/essimporter/importjour.hpp000066400000000000000000000005021264522266000226560ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_IMPORTJOUR_H #define OPENMW_ESSIMPORT_IMPORTJOUR_H #include namespace ESM { class ESMReader; } namespace ESSImport { /// Journal struct JOUR { // The entire journal, in HTML std::string mText; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importklst.cpp000066400000000000000000000006601264522266000226540ustar00rootroot00000000000000#include "importklst.hpp" #include namespace ESSImport { void KLST::load(ESM::ESMReader &esm) { while (esm.isNextSub("KNAM")) { std::string refId = esm.getHString(); int count; esm.getHNT(count, "CNAM"); mKillCounter[refId] = count; } mWerewolfKills = 0; esm.getHNOT(mWerewolfKills, "INTV"); } } openmw-openmw-0.38.0/apps/essimporter/importklst.hpp000066400000000000000000000005621264522266000226620ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_KLST_H #define OPENMW_ESSIMPORT_KLST_H #include #include namespace ESM { class ESMReader; } namespace ESSImport { /// Kill Stats struct KLST { void load(ESM::ESMReader& esm); /// RefId, kill count std::map mKillCounter; int mWerewolfKills; }; } #endif openmw-openmw-0.38.0/apps/essimporter/importnpcc.cpp000066400000000000000000000006211264522266000226170ustar00rootroot00000000000000#include "importnpcc.hpp" #include namespace ESSImport { void NPCC::load(ESM::ESMReader &esm) { esm.getHNT(mNPDT, "NPDT"); while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") || esm.isNextSub("AI_A")) mAiPackages.add(esm); mInventory.load(esm); } } openmw-openmw-0.38.0/apps/essimporter/importnpcc.hpp000066400000000000000000000011461264522266000226270ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_NPCC_H #define OPENMW_ESSIMPORT_NPCC_H #include #include #include "importinventory.hpp" namespace ESM { class ESMReader; } namespace ESSImport { struct NPCC { struct NPDT { unsigned char mDisposition; unsigned char unknown; unsigned char mReputation; unsigned char unknown2; int mIndex; } mNPDT; Inventory mInventory; ESM::AIPackageList mAiPackages; void load(ESM::ESMReader &esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importplayer.cpp000066400000000000000000000042011264522266000231660ustar00rootroot00000000000000#include "importplayer.hpp" #include namespace ESSImport { void REFR::load(ESM::ESMReader &esm) { esm.getHNT(mRefNum.mIndex, "FRMR"); mRefID = esm.getHNString("NAME"); mActorData.load(esm); esm.getHNOT(mPos, "DATA", 24); } void PCDT::load(ESM::ESMReader &esm) { while (esm.isNextSub("DNAM")) { mKnownDialogueTopics.push_back(esm.getHString()); } if (esm.isNextSub("MNAM")) esm.skipHSub(); // If this field is here it seems to specify the interior cell the player is in, // but it's not always here, so it's kinda useless esm.getHNT(mPNAM, "PNAM"); if (esm.isNextSub("SNAM")) esm.skipHSub(); if (esm.isNextSub("NAM9")) esm.skipHSub(); mBounty = 0; esm.getHNOT(mBounty, "CNAM"); mBirthsign = esm.getHNOString("BNAM"); // Holds the names of the last used Alchemy apparatus. Don't need to import this ATM, // because our GUI auto-selects the best apparatus. if (esm.isNextSub("NAM0")) esm.skipHSub(); if (esm.isNextSub("NAM1")) esm.skipHSub(); if (esm.isNextSub("NAM2")) esm.skipHSub(); if (esm.isNextSub("NAM3")) esm.skipHSub(); if (esm.isNextSub("ENAM")) esm.skipHSub(); if (esm.isNextSub("LNAM")) esm.skipHSub(); while (esm.isNextSub("FNAM")) { FNAM fnam; esm.getHT(fnam); mFactions.push_back(fnam); } if (esm.isNextSub("AADT")) esm.skipHSub(); // 44 bytes, no clue if (esm.isNextSub("KNAM")) esm.skipHSub(); // assigned Quick Keys, I think if (esm.isNextSub("WERE")) { // some werewolf data, 152 bytes // maybe current skills and attributes for werewolf form esm.getSubHeader(); esm.skip(152); } // unsure if before or after WERE if (esm.isNextSub("ANIS")) esm.skipHSub(); } } openmw-openmw-0.38.0/apps/essimporter/importplayer.hpp000066400000000000000000000030711264522266000231770ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_PLAYER_H #define OPENMW_ESSIMPORT_PLAYER_H #include #include #include #include #include #include "importacdt.hpp" namespace ESM { class ESMReader; } namespace ESSImport { /// Player-agnostic player data struct REFR { ActorData mActorData; std::string mRefID; ESM::Position mPos; ESM::RefNum mRefNum; void load(ESM::ESMReader& esm); }; /// Other player data struct PCDT { int mBounty; std::string mBirthsign; std::vector mKnownDialogueTopics; enum DrawState_ { DrawState_Weapon = 0x80, DrawState_Spell = 0x100 }; enum CameraState { CameraState_FirstPerson = 0x8, CameraState_ThirdPerson = 0xa }; #pragma pack(push) #pragma pack(1) struct FNAM { unsigned char mRank; unsigned char mUnknown1[3]; int mReputation; unsigned char mFlags; // 0x1: unknown, 0x2: expelled unsigned char mUnknown2[3]; ESM::NAME32 mFactionName; }; struct PNAM { short mDrawState; // DrawState short mCameraState; // CameraState unsigned int mLevelProgress; float mSkillProgress[27]; // skill progress, non-uniform scaled unsigned char mSkillIncreases[8]; // number of skill increases for each attribute unsigned char mUnknown3[88]; }; #pragma pack(pop) std::vector mFactions; PNAM mPNAM; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importques.cpp000066400000000000000000000003501264522266000226500ustar00rootroot00000000000000#include "importques.hpp" #include namespace ESSImport { void QUES::load(ESM::ESMReader &esm) { while (esm.isNextSub("DATA")) mInfo.push_back(esm.getHString()); } } openmw-openmw-0.38.0/apps/essimporter/importques.hpp000066400000000000000000000011151264522266000226550ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_IMPORTQUES_H #define OPENMW_ESSIMPORT_IMPORTQUES_H #include #include namespace ESM { class ESMReader; } namespace ESSImport { /// State for a quest /// Presumably this record only exists when Tribunal is installed, /// since pre-Tribunal there weren't any quest names in the data files. struct QUES { std::string mName; // NAME, should be assigned from outside as usual std::vector mInfo; // list of journal entries for the quest void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importscpt.cpp000066400000000000000000000006101264522266000226430ustar00rootroot00000000000000#include "importscpt.hpp" #include namespace ESSImport { void SCPT::load(ESM::ESMReader &esm) { esm.getHNT(mSCHD, "SCHD"); mSCRI.load(esm); mRefNum = -1; if (esm.isNextSub("RNAM")) { mRunning = true; esm.getHT(mRefNum); } else mRunning = false; } } openmw-openmw-0.38.0/apps/essimporter/importscpt.hpp000066400000000000000000000007571264522266000226640ustar00rootroot00000000000000#ifndef OPENMW_ESSIMPORT_IMPORTSCPT_H #define OPENMW_ESSIMPORT_IMPORTSCPT_H #include "importscri.hpp" #include namespace ESM { class ESMReader; } namespace ESSImport { // A running global script struct SCPT { ESM::Script::SCHD mSCHD; // values of local variables SCRI mSCRI; bool mRunning; int mRefNum; // Targeted reference, -1: no reference void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/importscri.cpp000066400000000000000000000024471264522266000226440ustar00rootroot00000000000000#include "importscri.hpp" #include namespace ESSImport { void SCRI::load(ESM::ESMReader &esm) { mScript = esm.getHNOString("SCRI"); int numShorts = 0, numLongs = 0, numFloats = 0; if (esm.isNextSub("SLCS")) { esm.getSubHeader(); esm.getT(numShorts); esm.getT(numLongs); esm.getT(numFloats); } if (esm.isNextSub("SLSD")) { esm.getSubHeader(); for (int i=0; i #include namespace ESM { class ESMReader; } namespace ESSImport { /// Local variable assigments for a running script struct SCRI { std::string mScript; std::vector mShorts; std::vector mLongs; std::vector mFloats; void load(ESM::ESMReader& esm); }; } #endif openmw-openmw-0.38.0/apps/essimporter/main.cpp000066400000000000000000000047261264522266000213770ustar00rootroot00000000000000#include #include #include #include #include #include #include "importer.hpp" namespace bpo = boost::program_options; namespace bfs = boost::filesystem; int main(int argc, char** argv) { try { bpo::options_description desc("Syntax: openmw-essimporter infile.ess outfile.omwsave\nAllowed options"); bpo::positional_options_description p_desc; desc.add_options() ("help,h", "produce help message") ("mwsave,m", bpo::value(), "morrowind .ess save file") ("output,o", bpo::value(), "output file (.omwsave)") ("compare,c", "compare two .ess files") ("encoding", boost::program_options::value()->default_value("win1252"), "encoding of the save file") ; p_desc.add("mwsave", 1).add("output", 1); bpo::variables_map variables; bpo::parsed_options parsed = bpo::command_line_parser(argc, argv) .options(desc) .positional(p_desc) .run(); bpo::store(parsed, variables); if(variables.count("help") || !variables.count("mwsave") || !variables.count("output")) { std::cout << desc; return 0; } bpo::notify(variables); Files::ConfigurationManager cfgManager(true); cfgManager.readConfiguration(variables, desc); std::string essFile = variables["mwsave"].as(); std::string outputFile = variables["output"].as(); std::string encoding = variables["encoding"].as(); ESSImport::Importer importer(essFile, outputFile, encoding); if (variables.count("compare")) importer.compare(); else { const std::string& ext = ".omwsave"; if (boost::filesystem::exists(boost::filesystem::path(outputFile)) && (outputFile.size() < ext.size() || outputFile.substr(outputFile.size()-ext.size()) != ext)) { throw std::runtime_error("Output file already exists and does not end in .omwsave. Did you mean to use --compare?"); } importer.run(); } } catch (std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } return 0; } openmw-openmw-0.38.0/apps/launcher/000077500000000000000000000000001264522266000171635ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/launcher/CMakeLists.txt000066400000000000000000000047741264522266000217370ustar00rootroot00000000000000set(LAUNCHER datafilespage.cpp graphicspage.cpp main.cpp maindialog.cpp playpage.cpp textslotmsgbox.cpp settingspage.cpp utils/profilescombobox.cpp utils/textinputdialog.cpp utils/lineedit.cpp ${CMAKE_SOURCE_DIR}/files/windows/launcher.rc ) set(LAUNCHER_HEADER datafilespage.hpp graphicspage.hpp maindialog.hpp playpage.hpp textslotmsgbox.hpp settingspage.hpp utils/profilescombobox.hpp utils/textinputdialog.hpp utils/lineedit.hpp ) # Headers that must be pre-processed set(LAUNCHER_HEADER_MOC datafilespage.hpp graphicspage.hpp maindialog.hpp playpage.hpp textslotmsgbox.hpp settingspage.hpp utils/textinputdialog.hpp utils/profilescombobox.hpp utils/lineedit.hpp ) set(LAUNCHER_UI ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui ${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui ${CMAKE_SOURCE_DIR}/files/ui/playpage.ui ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ${CMAKE_SOURCE_DIR}/files/ui/settingspage.ui ) source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER}) set(QT_USE_QTGUI 1) # Set some platform specific settings if(WIN32) set(GUI_TYPE WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) if (DESIRED_QT_VERSION MATCHES 4) include(${QT_USE_FILE}) QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) else() QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) endif() include_directories(${CMAKE_CURRENT_BINARY_DIR}) if(NOT WIN32) include_directories(${LIBUNSHIELD_INCLUDE_DIR}) endif(NOT WIN32) # Main executable add_executable(openmw-launcher ${GUI_TYPE} ${LAUNCHER} ${LAUNCHER_HEADER} ${RCC_SRCS} ${MOC_SRCS} ${UI_HDRS} ) target_link_libraries(openmw-launcher ${SDL2_LIBRARY_ONLY} components ) if (DESIRED_QT_VERSION MATCHES 4) target_link_libraries(openmw-launcher ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY}) if(WIN32) target_link_libraries(openmw-launcher ${QT_QTMAIN_LIBRARY}) endif(WIN32) else() qt5_use_modules(openmw-launcher Widgets Core) if (WIN32) target_link_libraries(Qt5::WinMain) endif() endif() if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw-launcher gcov) endif() openmw-openmw-0.38.0/apps/launcher/datafilespage.cpp000066400000000000000000000216601264522266000224650ustar00rootroot00000000000000#include "datafilespage.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "utils/textinputdialog.hpp" #include "utils/profilescombobox.hpp" const char *Launcher::DataFilesPage::mDefaultContentListName = "Default"; Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent) : QWidget(parent) , mCfgMgr(cfg) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) { ui.setupUi (this); setObjectName ("DataFilesPage"); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this); connect(mProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); buildView(); loadSettings(); } void Launcher::DataFilesPage::buildView() { ui.verticalLayout->insertWidget (0, mSelector->uiWidget()); //tool buttons ui.newProfileButton->setToolTip ("Create a new Content List"); ui.deleteProfileButton->setToolTip ("Delete an existing Content List"); //combo box ui.profilesComboBox->addItem(mDefaultContentListName); ui.profilesComboBox->setPlaceholderText (QString("Select a Content List...")); ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(QLatin1String(mDefaultContentListName))); // Add the actions to the toolbuttons ui.newProfileButton->setDefaultAction (ui.newProfileAction); ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction); //establish connections connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)), this, SLOT (slotProfileChanged(int))); connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString, QString)), this, SLOT (slotProfileRenamed(QString, QString))); connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)), this, SLOT (slotProfileChangedByUser(QString, QString))); } bool Launcher::DataFilesPage::loadSettings() { QStringList profiles = mLauncherSettings.getContentLists(); QString currentProfile = mLauncherSettings.getCurrentContentListName(); qDebug() << "The current profile is: " << currentProfile; foreach (const QString &item, profiles) addProfile (item, false); // Hack: also add the current profile if (!currentProfile.isEmpty()) addProfile(currentProfile, true); return true; } void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) { QStringList paths = mGameSettings.getDataDirs(); foreach(const QString &path, paths) mSelector->addFiles(path); mDataLocal = mGameSettings.getDataLocal(); if (!mDataLocal.isEmpty()) mSelector->addFiles(mDataLocal); paths.insert(0, mDataLocal); PathIterator pathIterator(paths); mSelector->setProfileContent(filesInProfile(contentModelName, pathIterator)); } QStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName, PathIterator& pathIterator) { QStringList files = mLauncherSettings.getContentListFiles(profileName); QStringList filepaths; foreach(const QString& file, files) { QString filepath = pathIterator.findFirstPath(file); if (!filepath.isEmpty()) filepaths << filepath; } return filepaths; } void Launcher::DataFilesPage::saveSettings(const QString &profile) { QString profileName = profile; if (profileName.isEmpty()) profileName = ui.profilesComboBox->currentText(); //retrieve the files selected for the profile ContentSelectorModel::ContentFileList items = mSelector->selectedFiles(); //set the value of the current profile (not necessarily the profile being saved!) mLauncherSettings.setCurrentContentListName(ui.profilesComboBox->currentText()); QStringList fileNames; foreach(const ContentSelectorModel::EsmFile *item, items) { fileNames.append(item->fileName()); } mLauncherSettings.setContentList(profileName, fileNames); mGameSettings.setContentList(fileNames); } void Launcher::DataFilesPage::removeProfile(const QString &profile) { mLauncherSettings.removeContentList(profile); } QAbstractItemModel *Launcher::DataFilesPage::profilesModel() const { return ui.profilesComboBox->model(); } int Launcher::DataFilesPage::profilesIndex() const { return ui.profilesComboBox->currentIndex(); } void Launcher::DataFilesPage::setProfile(int index, bool savePrevious) { if (index >= -1 && index < ui.profilesComboBox->count()) { QString previous = mPreviousProfile; QString current = ui.profilesComboBox->itemText(index); mPreviousProfile = current; setProfile (previous, current, savePrevious); } } void Launcher::DataFilesPage::setProfile (const QString &previous, const QString ¤t, bool savePrevious) { //abort if no change (poss. duplicate signal) if (previous == current) return; if (!previous.isEmpty() && savePrevious) saveSettings (previous); ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current)); populateFileViews(current); checkForDefaultProfile(); } void Launcher::DataFilesPage::slotProfileDeleted (const QString &item) { removeProfile (item); } void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) { setProfile(previous, current, true); emit signalProfileChanged (ui.profilesComboBox->findText(current)); } void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) { if (previous.isEmpty()) return; // Save the new profile name saveSettings(); // Remove the old one removeProfile (previous); loadSettings(); } void Launcher::DataFilesPage::slotProfileChanged(int index) { // in case the event was triggered externally if (ui.profilesComboBox->currentIndex() != index) ui.profilesComboBox->setCurrentIndex(index); setProfile (index, true); } void Launcher::DataFilesPage::on_newProfileAction_triggered() { if (mProfileDialog->exec() != QDialog::Accepted) return; QString profile = mProfileDialog->lineEdit()->text(); if (profile.isEmpty()) return; saveSettings(); mLauncherSettings.setCurrentContentListName(profile); addProfile(profile, true); } void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurrent) { if (profile.isEmpty()) return; if (ui.profilesComboBox->findText (profile) == -1) ui.profilesComboBox->addItem (profile); if (setAsCurrent) setProfile (ui.profilesComboBox->findText (profile), false); } void Launcher::DataFilesPage::on_deleteProfileAction_triggered() { QString profile = ui.profilesComboBox->currentText(); if (profile.isEmpty()) return; if (!showDeleteMessageBox (profile)) return; // this should work since the Default profile can't be deleted and is always index 0 int next = ui.profilesComboBox->currentIndex()-1; // changing the profile forces a reload of plugin file views. ui.profilesComboBox->setCurrentIndex(next); removeProfile(profile); ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile)); checkForDefaultProfile(); } void Launcher::DataFilesPage::updateOkButton(const QString &text) { // We do this here because we need the profiles combobox text if (text.isEmpty()) { mProfileDialog->setOkButtonEnabled(false); return; } (ui.profilesComboBox->findText(text) == -1) ? mProfileDialog->setOkButtonEnabled(true) : mProfileDialog->setOkButtonEnabled(false); } void Launcher::DataFilesPage::checkForDefaultProfile() { //don't allow deleting "Default" profile bool success = (ui.profilesComboBox->currentText() != mDefaultContentListName); ui.deleteProfileAction->setEnabled (success); ui.profilesComboBox->setEditEnabled (success); } bool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text) { QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Delete Content List")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Cancel); msgBox.setText(tr("Are you sure you want to delete %1?").arg(text)); QAbstractButton *deleteButton = msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); msgBox.exec(); return (msgBox.clickedButton() == deleteButton); } openmw-openmw-0.38.0/apps/launcher/datafilespage.hpp000066400000000000000000000076001264522266000224700ustar00rootroot00000000000000#ifndef DATAFILESPAGE_H #define DATAFILESPAGE_H #include "ui_datafilespage.h" #include #include #include class QSortFilterProxyModel; class QAbstractItemModel; class QMenu; namespace Files { struct ConfigurationManager; } namespace ContentSelectorView { class ContentSelector; } namespace Config { class GameSettings; class LauncherSettings; } namespace Launcher { class TextInputDialog; class ProfilesComboBox; class DataFilesPage : public QWidget { Q_OBJECT ContentSelectorView::ContentSelector *mSelector; Ui::DataFilesPage ui; public: explicit DataFilesPage (Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent = 0); QAbstractItemModel* profilesModel() const; int profilesIndex() const; //void writeConfig(QString profile = QString()); void saveSettings(const QString &profile = ""); bool loadSettings(); signals: void signalProfileChanged (int index); public slots: void slotProfileChanged (int index); private slots: void slotProfileChangedByUser(const QString &previous, const QString ¤t); void slotProfileRenamed(const QString &previous, const QString ¤t); void slotProfileDeleted(const QString &item); void updateOkButton(const QString &text); void on_newProfileAction_triggered(); void on_deleteProfileAction_triggered(); public: /// Content List that is always present const static char *mDefaultContentListName; private: TextInputDialog *mProfileDialog; Files::ConfigurationManager &mCfgMgr; Config::GameSettings &mGameSettings; Config::LauncherSettings &mLauncherSettings; QString mPreviousProfile; QString mDataLocal; void setPluginsCheckstates(Qt::CheckState state); void buildView(); void setupConfig(); void readConfig(); void setProfile (int index, bool savePrevious); void setProfile (const QString &previous, const QString ¤t, bool savePrevious); void removeProfile (const QString &profile); bool showDeleteMessageBox (const QString &text); void addProfile (const QString &profile, bool setAsCurrent); void checkForDefaultProfile(); void populateFileViews(const QString& contentModelName); class PathIterator { QStringList::ConstIterator mCitEnd; QStringList::ConstIterator mCitCurrent; QStringList::ConstIterator mCitBegin; QString mFile; QString mFilePath; public: PathIterator (const QStringList &list) { mCitBegin = list.constBegin(); mCitCurrent = mCitBegin; mCitEnd = list.constEnd(); } QString findFirstPath (const QString &file) { mCitCurrent = mCitBegin; mFile = file; return path(); } QString findNextPath () { return path(); } private: QString path () { bool success = false; QDir dir; QFileInfo file; while (!success) { if (mCitCurrent == mCitEnd) break; dir.setPath (*(mCitCurrent++)); file.setFile (dir.absoluteFilePath (mFile)); success = file.exists(); } if (success) return file.absoluteFilePath(); return ""; } }; QStringList filesInProfile(const QString& profileName, PathIterator& pathIterator); }; } #endif openmw-openmw-0.38.0/apps/launcher/graphicspage.cpp000066400000000000000000000213061264522266000223260ustar00rootroot00000000000000#include "graphicspage.hpp" #include #include #include #ifdef MAC_OS_X_VERSION_MIN_REQUIRED #undef MAC_OS_X_VERSION_MIN_REQUIRED // We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154 #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #endif // MAC_OS_X_VERSION_MIN_REQUIRED #include #include #include #include #include QString getAspect(int x, int y) { int gcd = boost::math::gcd (x, y); int xaspect = x / gcd; int yaspect = y / gcd; // special case: 8 : 5 is usually referred to as 16:10 if (xaspect == 8 && yaspect == 5) return QString("16:10"); return QString(QString::number(xaspect) + ":" + QString::number(yaspect)); } Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent) : QWidget(parent) , mCfgMgr(cfg) , mEngineSettings(engineSettings) { setObjectName ("GraphicsPage"); setupUi(this); // Set the maximum res we can set in windowed mode QRect res = getMaximumResolution(); customWidthSpinBox->setMaximum(res.width()); customHeightSpinBox->setMaximum(res.height()); connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int))); connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool))); connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int))); } bool Launcher::GraphicsPage::setupSDL() { int displays = SDL_GetNumVideoDisplays(); if (displays < 0) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error receiving number of screens")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("
SDL_GetNumDisplayModes failed:

") + QString::fromUtf8(SDL_GetError()) + "
"); msgBox.exec(); return false; } screenComboBox->clear(); for (int i = 0; i < displays; i++) { screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1)); } return true; } bool Launcher::GraphicsPage::loadSettings() { if (!setupSDL()) return false; if (mEngineSettings.getBool("vsync", "Video")) vSyncCheckBox->setCheckState(Qt::Checked); if (mEngineSettings.getBool("fullscreen", "Video")) fullScreenCheckBox->setCheckState(Qt::Checked); if (mEngineSettings.getBool("window border", "Video")) windowBorderCheckBox->setCheckState(Qt::Checked); // aaValue is the actual value (0, 1, 2, 4, 8, 16) int aaValue = mEngineSettings.getInt("antialiasing", "Video"); // aaIndex is the index into the allowed values in the pull down. int aaIndex = antiAliasingComboBox->findText(QString::number(aaValue)); if (aaIndex != -1) antiAliasingComboBox->setCurrentIndex(aaIndex); int width = mEngineSettings.getInt("resolution x", "Video"); int height = mEngineSettings.getInt("resolution y", "Video"); QString resolution = QString::number(width) + QString(" x ") + QString::number(height); screenComboBox->setCurrentIndex(mEngineSettings.getInt("screen", "Video")); int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith); if (resIndex != -1) { standardRadioButton->toggle(); resolutionComboBox->setCurrentIndex(resIndex); } else { customRadioButton->toggle(); customWidthSpinBox->setValue(width); customHeightSpinBox->setValue(height); } return true; } void Launcher::GraphicsPage::saveSettings() { // Ensure we only set the new settings if they changed. This is to avoid cluttering the // user settings file (which by definition should only contain settings the user has touched) bool cVSync = vSyncCheckBox->checkState(); if (cVSync != mEngineSettings.getBool("vsync", "Video")) mEngineSettings.setBool("vsync", "Video", cVSync); bool cFullScreen = fullScreenCheckBox->checkState(); if (cFullScreen != mEngineSettings.getBool("fullscreen", "Video")) mEngineSettings.setBool("fullscreen", "Video", cFullScreen); bool cWindowBorder = windowBorderCheckBox->checkState(); if (cWindowBorder != mEngineSettings.getBool("window border", "Video")) mEngineSettings.setBool("window border", "Video", cWindowBorder); int cAAValue = antiAliasingComboBox->currentText().toInt(); if (cAAValue != mEngineSettings.getInt("antialiasing", "Video")) mEngineSettings.setInt("antialiasing", "Video", cAAValue); int cWidth = 0; int cHeight = 0; if (standardRadioButton->isChecked()) { QRegExp resolutionRe(QString("(\\d+) x (\\d+).*")); if (resolutionRe.exactMatch(resolutionComboBox->currentText().simplified())) { cWidth = resolutionRe.cap(1).toInt(); cHeight = resolutionRe.cap(2).toInt(); } } else { cWidth = customWidthSpinBox->value(); cHeight = customHeightSpinBox->value(); } if (cWidth != mEngineSettings.getInt("resolution x", "Video")) mEngineSettings.setInt("resolution x", "Video", cWidth); if (cHeight != mEngineSettings.getInt("resolution y", "Video")) mEngineSettings.setInt("resolution y", "Video", cHeight); int cScreen = screenComboBox->currentIndex(); if (cScreen != mEngineSettings.getInt("screen", "Video")) mEngineSettings.setInt("screen", "Video", cScreen); } QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) { QStringList result; SDL_DisplayMode mode; int modeIndex, modes = SDL_GetNumDisplayModes(screen); if (modes < 0) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error receiving resolutions")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("
SDL_GetNumDisplayModes failed:

") + QString::fromUtf8(SDL_GetError()) + "
"); msgBox.exec(); return result; } for (modeIndex = 0; modeIndex < modes; modeIndex++) { if (SDL_GetDisplayMode(screen, modeIndex, &mode) < 0) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error receiving resolutions")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("
SDL_GetDisplayMode failed:

") + QString::fromUtf8(SDL_GetError()) + "
"); msgBox.exec(); return result; } QString aspect = getAspect(mode.w, mode.h); QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h); if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) { resolution.append(tr("\t(Wide ") + aspect + ")"); } else if (aspect == QLatin1String("4:3")) { resolution.append(tr("\t(Standard 4:3)")); } result.append(resolution); } result.removeDuplicates(); return result; } QRect Launcher::GraphicsPage::getMaximumResolution() { QRect max; int screens = QApplication::desktop()->screenCount(); for (int i = 0; i < screens; ++i) { QRect res = QApplication::desktop()->screenGeometry(i); if (res.width() > max.width()) max.setWidth(res.width()); if (res.height() > max.height()) max.setHeight(res.height()); } return max; } void Launcher::GraphicsPage::screenChanged(int screen) { if (screen >= 0) { resolutionComboBox->clear(); resolutionComboBox->addItems(getAvailableResolutions(screen)); } } void Launcher::GraphicsPage::slotFullScreenChanged(int state) { if (state == Qt::Checked) { standardRadioButton->toggle(); customRadioButton->setEnabled(false); customWidthSpinBox->setEnabled(false); customHeightSpinBox->setEnabled(false); windowBorderCheckBox->setEnabled(false); } else { customRadioButton->setEnabled(true); customWidthSpinBox->setEnabled(true); customHeightSpinBox->setEnabled(true); windowBorderCheckBox->setEnabled(true); } } void Launcher::GraphicsPage::slotStandardToggled(bool checked) { if (checked) { resolutionComboBox->setEnabled(true); customWidthSpinBox->setEnabled(false); customHeightSpinBox->setEnabled(false); } else { resolutionComboBox->setEnabled(false); customWidthSpinBox->setEnabled(true); customHeightSpinBox->setEnabled(true); } } openmw-openmw-0.38.0/apps/launcher/graphicspage.hpp000066400000000000000000000016431264522266000223350ustar00rootroot00000000000000#ifndef GRAPHICSPAGE_H #define GRAPHICSPAGE_H #include #include "ui_graphicspage.h" #include namespace Files { struct ConfigurationManager; } namespace Launcher { class GraphicsSettings; class GraphicsPage : public QWidget, private Ui::GraphicsPage { Q_OBJECT public: GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent = 0); void saveSettings(); bool loadSettings(); public slots: void screenChanged(int screen); private slots: void slotFullScreenChanged(int state); void slotStandardToggled(bool checked); private: Files::ConfigurationManager &mCfgMgr; Settings::Manager &mEngineSettings; QStringList getAvailableResolutions(int screen); QRect getMaximumResolution(); bool setupSDL(); }; } #endif openmw-openmw-0.38.0/apps/launcher/main.cpp000066400000000000000000000035601264522266000206170ustar00rootroot00000000000000#include #include #include #include #include #include #ifdef MAC_OS_X_VERSION_MIN_REQUIRED #undef MAC_OS_X_VERSION_MIN_REQUIRED // We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154 #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #endif // MAC_OS_X_VERSION_MIN_REQUIRED #include #include "maindialog.hpp" int main(int argc, char *argv[]) { try { SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); SDL_SetMainReady(); if (SDL_Init(SDL_INIT_VIDEO) != 0) { qDebug() << "SDL_Init failed: " << QString::fromUtf8(SDL_GetError()); return 0; } signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher, // so reset SIGINT which SDL wants to redirect to an SDL_Quit event. QApplication app(argc, argv); // Now we make sure the current dir is set to application path QDir dir(QCoreApplication::applicationDirPath()); #ifdef Q_OS_MAC if (dir.dirName() == "MacOS") { dir.cdUp(); dir.cdUp(); dir.cdUp(); } #endif QDir::setCurrent(dir.absolutePath()); Launcher::MainDialog mainWin; Launcher::FirstRunDialogResult result = mainWin.showFirstRunDialog(); if (result == Launcher::FirstRunDialogResultFailure) return 0; // if (!mainWin.setup()) { // return 0; // } if (result == Launcher::FirstRunDialogResultContinue) mainWin.show(); int returnValue = app.exec(); SDL_Quit(); return returnValue; } catch (std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; return 0; } } openmw-openmw-0.38.0/apps/launcher/maindialog.cpp000066400000000000000000000475631264522266000220120ustar00rootroot00000000000000#include "maindialog.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "playpage.hpp" #include "graphicspage.hpp" #include "datafilespage.hpp" #include "settingspage.hpp" using namespace Process; void cfgError(const QString& title, const QString& msg) { QMessageBox msgBox; msgBox.setWindowTitle(title); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(msg); msgBox.exec(); } Launcher::MainDialog::MainDialog(QWidget *parent) : QMainWindow(parent), mGameSettings (mCfgMgr) { setupUi(this); mGameInvoker = new ProcessInvoker(); mWizardInvoker = new ProcessInvoker(); connect(mWizardInvoker->getProcess(), SIGNAL(started()), this, SLOT(wizardStarted())); connect(mWizardInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(wizardFinished(int,QProcess::ExitStatus))); iconWidget->setViewMode(QListView::IconMode); iconWidget->setWrapping(false); iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure iconWidget->setIconSize(QSize(48, 48)); iconWidget->setMovement(QListView::Static); iconWidget->setSpacing(4); iconWidget->setCurrentRow(0); iconWidget->setFlow(QListView::LeftToRight); QPushButton *playButton = new QPushButton(tr("Play")); buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole); connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(play())); // Remove what's this? button setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); createIcons(); } Launcher::MainDialog::~MainDialog() { delete mGameInvoker; delete mWizardInvoker; } void Launcher::MainDialog::createIcons() { if (!QIcon::hasThemeIcon("document-new")) QIcon::setThemeName("tango"); QListWidgetItem *playButton = new QListWidgetItem(iconWidget); playButton->setIcon(QIcon(":/images/openmw.png")); playButton->setText(tr("Play")); playButton->setTextAlignment(Qt::AlignCenter); playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget); dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png")); dataFilesButton->setText(tr("Data Files")); dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget); graphicsButton->setIcon(QIcon::fromTheme("video-display")); graphicsButton->setText(tr("Graphics")); graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute); graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget); settingsButton->setIcon(QIcon::fromTheme("preferences-system")); settingsButton->setText(tr("Settings")); settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); connect(iconWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*))); } void Launcher::MainDialog::createPages() { mPlayPage = new PlayPage(this); mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this); mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this); // Set the combobox of the play page to imitate the combobox on the datafilespage mPlayPage->setProfilesModel(mDataFilesPage->profilesModel()); mPlayPage->setProfilesIndex(mDataFilesPage->profilesIndex()); // Add the pages to the stacked widget pagesWidget->addWidget(mPlayPage); pagesWidget->addWidget(mDataFilesPage); pagesWidget->addWidget(mGraphicsPage); pagesWidget->addWidget(mSettingsPage); // Select the first page iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select); connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play())); connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int))); connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int))); } Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog() { if (!setupLauncherSettings()) return FirstRunDialogResultFailure; if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true")) { QMessageBox msgBox; msgBox.setWindowTitle(tr("First run")); msgBox.setIcon(QMessageBox::Question); msgBox.setStandardButtons(QMessageBox::NoButton); msgBox.setText(tr("

Welcome to OpenMW!

\

It is recommended to run the Installation Wizard.

\

The Wizard will let you select an existing Morrowind installation, \ or install Morrowind for OpenMW to use.

")); QAbstractButton *wizardButton = msgBox.addButton(tr("Run &Installation Wizard"), QMessageBox::AcceptRole); // ActionRole doesn't work?! QAbstractButton *skipButton = msgBox.addButton(tr("Skip"), QMessageBox::RejectRole); Q_UNUSED(skipButton); // Surpress compiler unused warning msgBox.exec(); if (msgBox.clickedButton() == wizardButton) { if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) { return FirstRunDialogResultFailure; } else { return FirstRunDialogResultWizard; } } } return setup() ? FirstRunDialogResultContinue : FirstRunDialogResultFailure; } void Launcher::MainDialog::setVersionLabel() { // Add version information to bottom of the window Version::Version v = Version::getOpenmwVersion(mGameSettings.value("resources").toUtf8().constData()); QString revision(QString::fromUtf8(v.mCommitHash.c_str())); QString tag(QString::fromUtf8(v.mTagHash.c_str())); versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); if (!v.mVersion.empty() && (revision.isEmpty() || revision == tag)) versionLabel->setText(tr("OpenMW %1 release").arg(QString::fromUtf8(v.mVersion.c_str()))); else versionLabel->setText(tr("OpenMW development (%1)").arg(revision.left(10))); // Add the compile date and time versionLabel->setToolTip(tr("Compiled on %1 %2").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); } bool Launcher::MainDialog::setup() { if (!setupGameSettings()) return false; setVersionLabel(); mLauncherSettings.setContentList(mGameSettings); if (!setupGraphicsSettings()) return false; // Now create the pages as they need the settings createPages(); // Call this so we can exit on SDL errors before mainwindow is shown if (!mGraphicsPage->loadSettings()) return false; loadSettings(); return true; } bool Launcher::MainDialog::reloadSettings() { if (!setupLauncherSettings()) return false; if (!setupGameSettings()) return false; mLauncherSettings.setContentList(mGameSettings); if (!setupGraphicsSettings()) return false; if (!mSettingsPage->loadSettings()) return false; if (!mDataFilesPage->loadSettings()) return false; if (!mGraphicsPage->loadSettings()) return false; return true; } void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) { if (!current) current = previous; int currentIndex = iconWidget->row(current); pagesWidget->setCurrentIndex(currentIndex); mSettingsPage->resetProgressBar(); } bool Launcher::MainDialog::setupLauncherSettings() { mLauncherSettings.clear(); mLauncherSettings.setMultiValueEnabled(true); QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QStringList paths; paths.append(QString(Config::LauncherSettings::sLauncherConfigFileName)); paths.append(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName)); foreach (const QString &path, paths) { qDebug() << "Loading config file:" << path.toUtf8().constData(); QFile file(path); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { cfgError(tr("Error opening OpenMW configuration file"), tr("
Could not open %0 for reading

\ Please make sure you have the right permissions \ and try again.
").arg(file.fileName())); return false; } QTextStream stream(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); mLauncherSettings.readFile(stream); } file.close(); } return true; } bool Launcher::MainDialog::setupGameSettings() { mGameSettings.clear(); QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); // Load the user config file first, separately // So we can write it properly, uncontaminated QString path = userPath + QLatin1String("openmw.cfg"); QFile file(path); qDebug() << "Loading config file:" << path.toUtf8().constData(); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { cfgError(tr("Error opening OpenMW configuration file"), tr("
Could not open %0 for reading

\ Please make sure you have the right permissions \ and try again.
").arg(file.fileName())); return false; } QTextStream stream(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); mGameSettings.readUserFile(stream); } // Now the rest - priority: user > local > global QStringList paths; paths.append(globalPath + QString("openmw.cfg")); paths.append(QString("openmw.cfg")); paths.append(userPath + QString("openmw.cfg")); foreach (const QString &path, paths) { qDebug() << "Loading config file:" << path.toUtf8().constData(); QFile file(path); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { cfgError(tr("Error opening OpenMW configuration file"), tr("
Could not open %0 for reading

\ Please make sure you have the right permissions \ and try again.
").arg(file.fileName())); return false; } QTextStream stream(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); mGameSettings.readFile(stream); } file.close(); } QStringList dataDirs; // Check if the paths actually contain data files foreach (const QString path, mGameSettings.getDataDirs()) { QDir dir(path); QStringList filters; filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; if (!dir.entryList(filters).isEmpty()) dataDirs.append(path); } if (dataDirs.isEmpty()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error detecting Morrowind installation")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Cancel); msgBox.setText(tr("
Could not find the Data Files location

\ The directory containing the data files was not found.")); QAbstractButton *wizardButton = msgBox.addButton(tr("Run &Installation Wizard..."), QMessageBox::ActionRole); msgBox.exec(); if (msgBox.clickedButton() == wizardButton) { if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) { return false; } else { return true; } } } return true; } bool Launcher::MainDialog::setupGraphicsSettings() { // This method is almost a copy of OMW::Engine::loadSettings(). They should definitely // remain consistent, and possibly be merged into a shared component. At the very least // the filenames should be in the CfgMgr component. // Ensure to clear previous settings in case we had already loaded settings. mEngineSettings.clear(); // Create the settings manager and load default settings file const std::string localDefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string(); const std::string globalDefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string(); std::string defaultPath; // Prefer the settings-default.cfg in the current directory. if (boost::filesystem::exists(localDefault)) defaultPath = localDefault; else if (boost::filesystem::exists(globalDefault)) defaultPath = globalDefault; // Something's very wrong if we can't find the file at all. else { cfgError(tr("Error reading OpenMW configuration file"), tr("
Could not find settings-default.cfg

\ The problem may be due to an incomplete installation of OpenMW.
\ Reinstalling OpenMW may resolve the problem.")); return false; } // Load the default settings, report any parsing errors. try { mEngineSettings.loadDefault(defaultPath); } catch (std::exception& e) { std::string msg = std::string("
Error reading settings-default.cfg

") + e.what(); cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str())); return false; } // Load user settings if they exist const std::string userPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string(); // User settings are not required to exist, so if they don't we're done. if (!boost::filesystem::exists(userPath)) return true; try { mEngineSettings.loadUser(userPath); } catch (std::exception& e) { std::string msg = std::string("
Error reading settings.cfg

") + e.what(); cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str())); return false; } return true; } void Launcher::MainDialog::loadSettings() { int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt(); int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt(); int posX = mLauncherSettings.value(QString("General/MainWindow/posx")).toInt(); int posY = mLauncherSettings.value(QString("General/MainWindow/posy")).toInt(); resize(width, height); move(posX, posY); } void Launcher::MainDialog::saveSettings() { QString width = QString::number(this->width()); QString height = QString::number(this->height()); mLauncherSettings.setValue(QString("General/MainWindow/width"), width); mLauncherSettings.setValue(QString("General/MainWindow/height"), height); QString posX = QString::number(this->pos().x()); QString posY = QString::number(this->pos().y()); mLauncherSettings.setValue(QString("General/MainWindow/posx"), posX); mLauncherSettings.setValue(QString("General/MainWindow/posy"), posY); mLauncherSettings.setValue(QString("General/firstrun"), QString("false")); } bool Launcher::MainDialog::writeSettings() { // Now write all config files saveSettings(); mDataFilesPage->saveSettings(); mGraphicsPage->saveSettings(); mSettingsPage->saveSettings(); QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QDir dir(userPath); if (!dir.exists()) { if (!dir.mkpath(userPath)) { cfgError(tr("Error creating OpenMW configuration directory"), tr("
Could not create %0

\ Please make sure you have the right permissions \ and try again.
").arg(userPath)); return false; } } // Game settings QFile file(userPath + QString("openmw.cfg")); if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { // File cannot be opened or created cfgError(tr("Error writing OpenMW configuration file"), tr("
Could not open or create %0 for writing

\ Please make sure you have the right permissions \ and try again.
").arg(file.fileName())); return false; } mGameSettings.writeFileWithComments(file); file.close(); // Graphics settings const std::string settingsPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string(); try { mEngineSettings.saveUser(settingsPath); } catch (std::exception& e) { std::string msg = "
Error writing settings.cfg

" + settingsPath + "

" + e.what(); cfgError(tr("Error writing user settings file"), tr(msg.c_str())); return false; } // Launcher settings file.setFileName(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName)); if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { // File cannot be opened or created cfgError(tr("Error writing Launcher configuration file"), tr("
Could not open or create %0 for writing

\ Please make sure you have the right permissions \ and try again.
").arg(file.fileName())); return false; } QTextStream stream(&file); stream.setDevice(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); mLauncherSettings.writeFile(stream); file.close(); return true; } void Launcher::MainDialog::closeEvent(QCloseEvent *event) { writeSettings(); event->accept(); } void Launcher::MainDialog::wizardStarted() { hide(); } void Launcher::MainDialog::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode != 0 || exitStatus == QProcess::CrashExit) return qApp->quit(); // HACK: Ensure the pages are created, else segfault setup(); if (reloadSettings()) show(); } void Launcher::MainDialog::play() { if (!writeSettings()) return qApp->quit(); if (!mGameSettings.hasMaster()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("No game file selected")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("
You do not have a game file selected.

\ OpenMW will not start without a game file selected.
")); msgBox.exec(); return; } // Launch the game detached if (mGameInvoker->startProcess(QLatin1String("openmw"), true)) return qApp->quit(); } openmw-openmw-0.38.0/apps/launcher/maindialog.hpp000066400000000000000000000045521264522266000220060ustar00rootroot00000000000000#ifndef MAINDIALOG_H #define MAINDIALOG_H #include #include #ifndef Q_MOC_RUN #include #endif #include #include #include #include #include "ui_mainwindow.h" class QListWidgetItem; class QStackedWidget; class QStringList; class QStringListModel; class QString; namespace Launcher { class PlayPage; class GraphicsPage; class DataFilesPage; class UnshieldThread; class SettingsPage; enum FirstRunDialogResult { FirstRunDialogResultFailure, FirstRunDialogResultContinue, FirstRunDialogResultWizard }; #ifndef WIN32 bool expansions(Launcher::UnshieldThread& cd); #endif class MainDialog : public QMainWindow, private Ui::MainWindow { Q_OBJECT public: explicit MainDialog(QWidget *parent = 0); ~MainDialog(); bool setup(); FirstRunDialogResult showFirstRunDialog(); bool reloadSettings(); bool writeSettings(); public slots: void changePage(QListWidgetItem *current, QListWidgetItem *previous); void play(); private slots: void wizardStarted(); void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus); private: void createIcons(); void createPages(); bool setupLauncherSettings(); bool setupGameSettings(); bool setupGraphicsSettings(); void setVersionLabel(); void loadSettings(); void saveSettings(); inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); } bool startProgram(const QString &name, const QStringList &arguments, bool detached = false); void closeEvent(QCloseEvent *event); PlayPage *mPlayPage; GraphicsPage *mGraphicsPage; DataFilesPage *mDataFilesPage; SettingsPage *mSettingsPage; Process::ProcessInvoker *mGameInvoker; Process::ProcessInvoker *mWizardInvoker; Files::ConfigurationManager mCfgMgr; Config::GameSettings mGameSettings; Settings::Manager mEngineSettings; Config::LauncherSettings mLauncherSettings; }; } #endif openmw-openmw-0.38.0/apps/launcher/playpage.cpp000066400000000000000000000012541264522266000214730ustar00rootroot00000000000000#include "playpage.hpp" #include Launcher::PlayPage::PlayPage(QWidget *parent) : QWidget(parent) { setObjectName ("PlayPage"); setupUi(this); profilesComboBox->setView(new QListView()); connect(profilesComboBox, SIGNAL(activated(int)), this, SIGNAL (signalProfileChanged(int))); connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked())); } void Launcher::PlayPage::setProfilesModel(QAbstractItemModel *model) { profilesComboBox->setModel(model); } void Launcher::PlayPage::setProfilesIndex(int index) { profilesComboBox->setCurrentIndex(index); } void Launcher::PlayPage::slotPlayClicked() { emit playButtonClicked(); } openmw-openmw-0.38.0/apps/launcher/playpage.hpp000066400000000000000000000011121264522266000214710ustar00rootroot00000000000000#ifndef PLAYPAGE_H #define PLAYPAGE_H #include #include "ui_playpage.h" class QComboBox; class QPushButton; class QAbstractItemModel; namespace Launcher { class PlayPage : public QWidget, private Ui::PlayPage { Q_OBJECT public: PlayPage(QWidget *parent = 0); void setProfilesModel(QAbstractItemModel *model); signals: void signalProfileChanged(int index); void playButtonClicked(); public slots: void setProfilesIndex(int index); private slots: void slotPlayClicked(); }; } #endif openmw-openmw-0.38.0/apps/launcher/settingspage.cpp000066400000000000000000000200471264522266000223670ustar00rootroot00000000000000#include "settingspage.hpp" #include #include #include #include #include #include #include #include "utils/textinputdialog.hpp" #include "datafilespage.hpp" using namespace Process; Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, MainDialog *parent) : QWidget(parent) , mCfgMgr(cfg) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) , mMain(parent) { setupUi(this); QStringList languages; languages << QLatin1String("English") << QLatin1String("French") << QLatin1String("German") << QLatin1String("Italian") << QLatin1String("Polish") << QLatin1String("Russian") << QLatin1String("Spanish"); languageComboBox->addItems(languages); mWizardInvoker = new ProcessInvoker(); mImporterInvoker = new ProcessInvoker(); resetProgressBar(); connect(mWizardInvoker->getProcess(), SIGNAL(started()), this, SLOT(wizardStarted())); connect(mWizardInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(wizardFinished(int,QProcess::ExitStatus))); connect(mImporterInvoker->getProcess(), SIGNAL(started()), this, SLOT(importerStarted())); connect(mImporterInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(importerFinished(int,QProcess::ExitStatus))); mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this); connect(mProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); // Detect Morrowind configuration files QStringList iniPaths; foreach (const QString &path, mGameSettings.getDataDirs()) { QDir dir(path); dir.setPath(dir.canonicalPath()); // Resolve symlinks if (dir.exists(QString("Morrowind.ini"))) iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini"))); else { if (!dir.cdUp()) continue; // Cannot move from Data Files if (dir.exists(QString("Morrowind.ini"))) iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini"))); } } if (!iniPaths.isEmpty()) { settingsComboBox->addItems(iniPaths); importerButton->setEnabled(true); } else { importerButton->setEnabled(false); } loadSettings(); } Launcher::SettingsPage::~SettingsPage() { delete mWizardInvoker; delete mImporterInvoker; } void Launcher::SettingsPage::on_wizardButton_clicked() { mMain->writeSettings(); if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) return; } void Launcher::SettingsPage::on_importerButton_clicked() { mMain->writeSettings(); // Create the file if it doesn't already exist, else the importer will fail QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str())); path.append(QLatin1String("openmw.cfg")); QFile file(path); if (!file.exists()) { if (!file.open(QIODevice::ReadWrite)) { // File cannot be created QMessageBox msgBox; msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not open or create %1 for writing

\

Please make sure you have the right permissions \ and try again.

").arg(file.fileName())); msgBox.exec(); return; } file.close(); } // Construct the arguments to run the importer QStringList arguments; if (addonsCheckBox->isChecked()) arguments.append(QString("--game-files")); arguments.append(QString("--encoding")); arguments.append(mGameSettings.value(QString("encoding"), QString("win1252"))); arguments.append(QString("--ini")); arguments.append(settingsComboBox->currentText()); arguments.append(QString("--cfg")); arguments.append(path); qDebug() << "arguments " << arguments; // start the progress bar as a "bouncing ball" progressBar->setMaximum(0); progressBar->setValue(0); if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false)) { resetProgressBar(); } } void Launcher::SettingsPage::on_browseButton_clicked() { QString iniFile = QFileDialog::getOpenFileName( this, QObject::tr("Select configuration file"), QDir::currentPath(), QString(tr("Morrowind configuration file (*.ini)"))); if (iniFile.isEmpty()) return; QFileInfo info(iniFile); if (!info.exists() || !info.isReadable()) return; const QString path(QDir::toNativeSeparators(info.absoluteFilePath())); if (settingsComboBox->findText(path) == -1) { settingsComboBox->addItem(path); settingsComboBox->setCurrentIndex(settingsComboBox->findText(path)); importerButton->setEnabled(true); } } void Launcher::SettingsPage::wizardStarted() { mMain->hide(); // Hide the launcher wizardButton->setEnabled(false); } void Launcher::SettingsPage::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode != 0 || exitStatus == QProcess::CrashExit) return qApp->quit(); mMain->reloadSettings(); wizardButton->setEnabled(true); mMain->show(); // Show the launcher again } void Launcher::SettingsPage::importerStarted() { importerButton->setEnabled(false); } void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode != 0 || exitStatus == QProcess::CrashExit) { resetProgressBar(); QMessageBox msgBox; msgBox.setWindowTitle(tr("Importer finished")); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setIcon(QMessageBox::Warning); msgBox.setText(tr("Failed to import settings from INI file.")); msgBox.exec(); } else { // indicate progress finished progressBar->setMaximum(1); progressBar->setValue(1); // Importer may have changed settings, so refresh mMain->reloadSettings(); } importerButton->setEnabled(true); } void Launcher::SettingsPage::resetProgressBar() { // set progress bar to 0 % progressBar->reset(); } void Launcher::SettingsPage::updateOkButton(const QString &text) { // We do this here because we need to access the profiles if (text.isEmpty()) { mProfileDialog->setOkButtonEnabled(false); return; } const QStringList profiles(mLauncherSettings.getContentLists()); (profiles.contains(text)) ? mProfileDialog->setOkButtonEnabled(false) : mProfileDialog->setOkButtonEnabled(true); } void Launcher::SettingsPage::saveSettings() { QString language(languageComboBox->currentText()); mLauncherSettings.setValue(QLatin1String("Settings/language"), language); if (language == QLatin1String("Polish")) { mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250")); } else if (language == QLatin1String("Russian")) { mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251")); } else { mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252")); } } bool Launcher::SettingsPage::loadSettings() { QString language(mLauncherSettings.value(QLatin1String("Settings/language"))); int index = languageComboBox->findText(language); if (index != -1) languageComboBox->setCurrentIndex(index); return true; } openmw-openmw-0.38.0/apps/launcher/settingspage.hpp000066400000000000000000000031161264522266000223720ustar00rootroot00000000000000#ifndef SETTINGSPAGE_HPP #define SETTINGSPAGE_HPP #include #include #include #include "ui_settingspage.h" #include "maindialog.hpp" namespace Files { struct ConfigurationManager; } namespace Config { class GameSettings; class LauncherSettings; } namespace Launcher { class TextInputDialog; class SettingsPage : public QWidget, private Ui::SettingsPage { Q_OBJECT public: SettingsPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, MainDialog *parent = 0); ~SettingsPage(); void saveSettings(); bool loadSettings(); /// set progress bar on page to 0% void resetProgressBar(); private slots: void on_wizardButton_clicked(); void on_importerButton_clicked(); void on_browseButton_clicked(); void wizardStarted(); void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus); void importerStarted(); void importerFinished(int exitCode, QProcess::ExitStatus exitStatus); void updateOkButton(const QString &text); private: Process::ProcessInvoker *mWizardInvoker; Process::ProcessInvoker *mImporterInvoker; Files::ConfigurationManager &mCfgMgr; Config::GameSettings &mGameSettings; Config::LauncherSettings &mLauncherSettings; MainDialog *mMain; TextInputDialog *mProfileDialog; }; } #endif // SETTINGSPAGE_HPP openmw-openmw-0.38.0/apps/launcher/textslotmsgbox.cpp000066400000000000000000000001721264522266000227750ustar00rootroot00000000000000#include "textslotmsgbox.hpp" void Launcher::TextSlotMsgBox::setTextSlot(const QString& string) { setText(string); } openmw-openmw-0.38.0/apps/launcher/textslotmsgbox.hpp000066400000000000000000000003761264522266000230100ustar00rootroot00000000000000#ifndef TEXT_SLOT_MSG_BOX #define TEXT_SLOT_MSG_BOX #include namespace Launcher { class TextSlotMsgBox : public QMessageBox { Q_OBJECT public slots: void setTextSlot(const QString& string); }; } #endif openmw-openmw-0.38.0/apps/launcher/utils/000077500000000000000000000000001264522266000203235ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/launcher/utils/lineedit.cpp000066400000000000000000000020201264522266000226160ustar00rootroot00000000000000#include "lineedit.hpp" LineEdit::LineEdit(QWidget *parent) : QLineEdit(parent) { setupClearButton(); } void LineEdit::setupClearButton() { mClearButton = new QToolButton(this); QPixmap pixmap(":images/clear.png"); mClearButton->setIcon(QIcon(pixmap)); mClearButton->setIconSize(pixmap.size()); mClearButton->setCursor(Qt::ArrowCursor); mClearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }"); mClearButton->hide(); connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear())); connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&))); } void LineEdit::resizeEvent(QResizeEvent *) { QSize sz = mClearButton->sizeHint(); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); mClearButton->move(rect().right() - frameWidth - sz.width(), (rect().bottom() + 1 - sz.height())/2); } void LineEdit::updateClearButton(const QString& text) { mClearButton->setVisible(!text.isEmpty()); } openmw-openmw-0.38.0/apps/launcher/utils/lineedit.hpp000066400000000000000000000014701264522266000226330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (c) 2007 Trolltech ASA ** ** Use, modification and distribution is allowed without limitation, ** warranty, liability or support of any kind. ** ****************************************************************************/ #ifndef LINEEDIT_H #define LINEEDIT_H #include #include #include #include class QToolButton; class LineEdit : public QLineEdit { Q_OBJECT QString mPlaceholderText; public: LineEdit(QWidget *parent = 0); protected: void resizeEvent(QResizeEvent *); private slots: void updateClearButton(const QString &text); protected: QToolButton *mClearButton; void setupClearButton(); }; #endif // LIENEDIT_H openmw-openmw-0.38.0/apps/launcher/utils/profilescombobox.cpp000066400000000000000000000050751264522266000244120ustar00rootroot00000000000000#include #include #include #include #include #include "profilescombobox.hpp" ProfilesComboBox::ProfilesComboBox(QWidget *parent) : ContentSelectorView::ComboBox(parent) { connect(this, SIGNAL(activated(int)), this, SLOT(slotIndexChangedByUser(int))); setInsertPolicy(QComboBox::NoInsert); } void ProfilesComboBox::setEditEnabled(bool editable) { if (isEditable() == editable) return; if (!editable) { disconnect(lineEdit(), SIGNAL(editingFinished()), this, SLOT(slotEditingFinished())); disconnect(lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); return setEditable(false); } // Reset the completer and validator setEditable(true); setValidator(mValidator); ComboBoxLineEdit *edit = new ComboBoxLineEdit(this); setLineEdit(edit); setCompleter(0); connect(lineEdit(), SIGNAL(editingFinished()), this, SLOT(slotEditingFinished())); connect(lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); connect (lineEdit(), SIGNAL(textChanged(QString)), this, SIGNAL (signalProfileTextChanged (QString))); } void ProfilesComboBox::slotTextChanged(const QString &text) { QPalette palette; palette.setColor(QPalette::Text,Qt::red); int index = findText(text); if (text.isEmpty() || (index != -1 && index != currentIndex())) { lineEdit()->setPalette(palette); } else { lineEdit()->setPalette(QApplication::palette()); } } void ProfilesComboBox::slotEditingFinished() { QString current = currentText(); QString previous = itemText(currentIndex()); if (currentIndex() == -1) return; if (current.isEmpty()) return; if (current == previous) return; if (findText(current) != -1) return; setItemText(currentIndex(), current); emit(profileRenamed(previous, current)); } void ProfilesComboBox::slotIndexChangedByUser(int index) { if (index == -1) return; emit (signalProfileChanged(mOldProfile, currentText())); mOldProfile = currentText(); } ProfilesComboBox::ComboBoxLineEdit::ComboBoxLineEdit (QWidget *parent) : LineEdit (parent) { int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); setObjectName(QString("ComboBoxLineEdit")); setStyleSheet(QString("ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); } openmw-openmw-0.38.0/apps/launcher/utils/profilescombobox.hpp000066400000000000000000000021011264522266000244020ustar00rootroot00000000000000#ifndef PROFILESCOMBOBOX_HPP #define PROFILESCOMBOBOX_HPP #include "components/contentselector/view/combobox.hpp" #include "lineedit.hpp" #include class QString; class ProfilesComboBox : public ContentSelectorView::ComboBox { Q_OBJECT public: class ComboBoxLineEdit : public LineEdit { public: explicit ComboBoxLineEdit (QWidget *parent = 0); }; public: explicit ProfilesComboBox(QWidget *parent = 0); void setEditEnabled(bool editable); void setCurrentProfile(int index) { ComboBox::setCurrentIndex(index); mOldProfile = currentText(); } signals: void signalProfileTextChanged(const QString &item); void signalProfileChanged(const QString &previous, const QString ¤t); void signalProfileChanged(int index); void profileRenamed(const QString &oldName, const QString &newName); private slots: void slotEditingFinished(); void slotIndexChangedByUser(int index); void slotTextChanged(const QString &text); private: QString mOldProfile; }; #endif // PROFILESCOMBOBOX_HPP openmw-openmw-0.38.0/apps/launcher/utils/textinputdialog.cpp000066400000000000000000000036261264522266000242620ustar00rootroot00000000000000#include "textinputdialog.hpp" #include #include #include #include #include #include Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : QDialog(parent) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); mButtonBox = new QDialogButtonBox(this); mButtonBox->addButton(QDialogButtonBox::Ok); mButtonBox->addButton(QDialogButtonBox::Cancel); mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); QLabel *label = new QLabel(this); label->setText(text); // Line edit QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore mLineEdit = new LineEdit(this); mLineEdit->setValidator(validator); mLineEdit->setCompleter(0); QVBoxLayout *dialogLayout = new QVBoxLayout(this); dialogLayout->addWidget(label); dialogLayout->addWidget(mLineEdit); dialogLayout->addWidget(mButtonBox); // Messageboxes on mac have no title #ifndef Q_OS_MAC setWindowTitle(title); #else Q_UNUSED(title); #endif setModal(true); connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); } Launcher::TextInputDialog::~TextInputDialog() { } int Launcher::TextInputDialog::exec() { mLineEdit->clear(); mLineEdit->setFocus(); return QDialog::exec(); } void Launcher::TextInputDialog::setOkButtonEnabled(bool enabled) { QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(enabled); QPalette palette; palette.setColor(QPalette::Text, Qt::red); if (enabled) { mLineEdit->setPalette(QApplication::palette()); } else { // Existing profile name, make the text red mLineEdit->setPalette(palette); } } openmw-openmw-0.38.0/apps/launcher/utils/textinputdialog.hpp000066400000000000000000000011351264522266000242600ustar00rootroot00000000000000#ifndef TEXTINPUTDIALOG_HPP #define TEXTINPUTDIALOG_HPP #include #include "lineedit.hpp" class QDialogButtonBox; namespace Launcher { class TextInputDialog : public QDialog { Q_OBJECT public: explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); ~TextInputDialog (); inline LineEdit *lineEdit() { return mLineEdit; } void setOkButtonEnabled(bool enabled); int exec(); private: QDialogButtonBox *mButtonBox; LineEdit *mLineEdit; }; } #endif // TEXTINPUTDIALOG_HPP openmw-openmw-0.38.0/apps/mwiniimporter/000077500000000000000000000000001264522266000202675ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/mwiniimporter/CMakeLists.txt000066400000000000000000000011651264522266000230320ustar00rootroot00000000000000set(MWINIIMPORT main.cpp importer.cpp ) set(MWINIIMPORT_HEADER importer.hpp ) source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER}) add_executable(openmw-iniimporter ${MWINIIMPORT} ) target_link_libraries(openmw-iniimporter ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} components ) if (WIN32) target_link_libraries(openmw-iniimporter ${Boost_LOCALE_LIBRARY}) endif() if (MINGW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") endif() if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw-iniimporter gcov) endif() openmw-openmw-0.38.0/apps/mwiniimporter/importer.cpp000066400000000000000000001001651264522266000226370ustar00rootroot00000000000000#include "importer.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace bfs = boost::filesystem; MwIniImporter::MwIniImporter() : mVerbose(false) , mEncoding(ToUTF8::WINDOWS_1250) { const char *map[][2] = { { "no-sound", "General:Disable Audio" }, { 0, 0 } }; const char *fallback[] = { // light "LightAttenuation:UseConstant", "LightAttenuation:ConstantValue", "LightAttenuation:UseLinear", "LightAttenuation:LinearMethod", "LightAttenuation:LinearValue", "LightAttenuation:LinearRadiusMult", "LightAttenuation:UseQuadratic", "LightAttenuation:QuadraticMethod", "LightAttenuation:QuadraticValue", "LightAttenuation:QuadraticRadiusMult", "LightAttenuation:OutQuadInLin", // inventory "Inventory:DirectionalDiffuseR", "Inventory:DirectionalDiffuseG", "Inventory:DirectionalDiffuseB", "Inventory:DirectionalAmbientR", "Inventory:DirectionalAmbientG", "Inventory:DirectionalAmbientB", "Inventory:DirectionalRotationX", "Inventory:DirectionalRotationY", "Inventory:UniformScaling", // map "Map:Travel Siltstrider Red", "Map:Travel Siltstrider Green", "Map:Travel Siltstrider Blue", "Map:Travel Boat Red", "Map:Travel Boat Green", "Map:Travel Boat Blue", "Map:Travel Magic Red", "Map:Travel Magic Green", "Map:Travel Magic Blue", "Map:Show Travel Lines", // water "Water:Map Alpha", "Water:World Alpha", "Water:SurfaceTextureSize", "Water:SurfaceTileCount", "Water:SurfaceFPS", "Water:SurfaceTexture", "Water:SurfaceFrameCount", "Water:TileTextureDivisor", "Water:RippleTexture", "Water:RippleFrameCount", "Water:RippleLifetime", "Water:MaxNumberRipples", "Water:RippleScale", "Water:RippleRotSpeed", "Water:RippleAlphas", "Water:PSWaterReflectTerrain", "Water:PSWaterReflectUpdate", "Water:NearWaterRadius", "Water:NearWaterPoints", "Water:NearWaterUnderwaterFreq", "Water:NearWaterUnderwaterVolume", "Water:NearWaterIndoorTolerance", "Water:NearWaterOutdoorTolerance", "Water:NearWaterIndoorID", "Water:NearWaterOutdoorID", "Water:UnderwaterSunriseFog", "Water:UnderwaterDayFog", "Water:UnderwaterSunsetFog", "Water:UnderwaterNightFog", "Water:UnderwaterIndoorFog", "Water:UnderwaterColor", "Water:UnderwaterColorWeight", // pixelwater "PixelWater:SurfaceFPS", "PixelWater:TileCount", "PixelWater:Resolution", // fonts "Fonts:Font 0", "Fonts:Font 1", "Fonts:Font 2", // UI colors "FontColor:color_normal", "FontColor:color_normal_over", "FontColor:color_normal_pressed", "FontColor:color_active", "FontColor:color_active_over", "FontColor:color_active_pressed", "FontColor:color_disabled", "FontColor:color_disabled_over", "FontColor:color_disabled_pressed", "FontColor:color_link", "FontColor:color_link_over", "FontColor:color_link_pressed", "FontColor:color_journal_link", "FontColor:color_journal_link_over", "FontColor:color_journal_link_pressed", "FontColor:color_journal_topic", "FontColor:color_journal_topic_over", "FontColor:color_journal_topic_pressed", "FontColor:color_answer", "FontColor:color_answer_over", "FontColor:color_answer_pressed", "FontColor:color_header", "FontColor:color_notify", "FontColor:color_big_normal", "FontColor:color_big_normal_over", "FontColor:color_big_normal_pressed", "FontColor:color_big_link", "FontColor:color_big_link_over", "FontColor:color_big_link_pressed", "FontColor:color_big_answer", "FontColor:color_big_answer_over", "FontColor:color_big_answer_pressed", "FontColor:color_big_header", "FontColor:color_big_notify", "FontColor:color_background", "FontColor:color_focus", "FontColor:color_health", "FontColor:color_magic", "FontColor:color_fatigue", "FontColor:color_misc", "FontColor:color_weapon_fill", "FontColor:color_magic_fill", "FontColor:color_positive", "FontColor:color_negative", "FontColor:color_count", // level up messages "Level Up:Level2", "Level Up:Level3", "Level Up:Level4", "Level Up:Level5", "Level Up:Level6", "Level Up:Level7", "Level Up:Level8", "Level Up:Level9", "Level Up:Level10", "Level Up:Level11", "Level Up:Level12", "Level Up:Level13", "Level Up:Level14", "Level Up:Level15", "Level Up:Level16", "Level Up:Level17", "Level Up:Level18", "Level Up:Level19", "Level Up:Level20", "Level Up:Default", // character creation multiple choice test "Question 1:Question", "Question 1:AnswerOne", "Question 1:AnswerTwo", "Question 1:AnswerThree", "Question 1:Sound", "Question 2:Question", "Question 2:AnswerOne", "Question 2:AnswerTwo", "Question 2:AnswerThree", "Question 2:Sound", "Question 3:Question", "Question 3:AnswerOne", "Question 3:AnswerTwo", "Question 3:AnswerThree", "Question 3:Sound", "Question 4:Question", "Question 4:AnswerOne", "Question 4:AnswerTwo", "Question 4:AnswerThree", "Question 4:Sound", "Question 5:Question", "Question 5:AnswerOne", "Question 5:AnswerTwo", "Question 5:AnswerThree", "Question 5:Sound", "Question 6:Question", "Question 6:AnswerOne", "Question 6:AnswerTwo", "Question 6:AnswerThree", "Question 6:Sound", "Question 7:Question", "Question 7:AnswerOne", "Question 7:AnswerTwo", "Question 7:AnswerThree", "Question 7:Sound", "Question 8:Question", "Question 8:AnswerOne", "Question 8:AnswerTwo", "Question 8:AnswerThree", "Question 8:Sound", "Question 9:Question", "Question 9:AnswerOne", "Question 9:AnswerTwo", "Question 9:AnswerThree", "Question 9:Sound", "Question 10:Question", "Question 10:AnswerOne", "Question 10:AnswerTwo", "Question 10:AnswerThree", "Question 10:Sound", // blood textures and models "Blood:Model 0", "Blood:Model 1", "Blood:Model 2", "Blood:Texture 0", "Blood:Texture 1", "Blood:Texture 2", "Blood:Texture Name 0", "Blood:Texture Name 1", "Blood:Texture Name 2", // movies "Movies:Company Logo", "Movies:Morrowind Logo", "Movies:New Game", "Movies:Loading", "Movies:Options Menu", // weather related values "Weather Thunderstorm:Thunder Sound ID 0", "Weather Thunderstorm:Thunder Sound ID 1", "Weather Thunderstorm:Thunder Sound ID 2", "Weather Thunderstorm:Thunder Sound ID 3", "Weather:Sunrise Time", "Weather:Sunset Time", "Weather:Sunrise Duration", "Weather:Sunset Duration", "Weather:Hours Between Weather Changes", // AKA weather update time "Weather Thunderstorm:Thunder Frequency", "Weather Thunderstorm:Thunder Threshold", "Weather:EnvReduceColor", "Weather:LerpCloseColor", "Weather:BumpFadeColor", "Weather:AlphaReduce", "Weather:Minimum Time Between Environmental Sounds", "Weather:Maximum Time Between Environmental Sounds", "Weather:Sun Glare Fader Max", "Weather:Sun Glare Fader Angle Max", "Weather:Sun Glare Fader Color", "Weather:Timescale Clouds", "Weather:Precip Gravity", "Weather:Rain Ripples", "Weather:Rain Ripple Radius", "Weather:Rain Ripples Per Drop", "Weather:Rain Ripple Scale", "Weather:Rain Ripple Speed", "Weather:Fog Depth Change Speed", "Weather:Sky Pre-Sunrise Time", "Weather:Sky Post-Sunrise Time", "Weather:Sky Pre-Sunset Time", "Weather:Sky Post-Sunset Time", "Weather:Ambient Pre-Sunrise Time", "Weather:Ambient Post-Sunrise Time", "Weather:Ambient Pre-Sunset Time", "Weather:Ambient Post-Sunset Time", "Weather:Fog Pre-Sunrise Time", "Weather:Fog Post-Sunrise Time", "Weather:Fog Pre-Sunset Time", "Weather:Fog Post-Sunset Time", "Weather:Sun Pre-Sunrise Time", "Weather:Sun Post-Sunrise Time", "Weather:Sun Pre-Sunset Time", "Weather:Sun Post-Sunset Time", "Weather:Stars Post-Sunset Start", "Weather:Stars Pre-Sunrise Finish", "Weather:Stars Fading Duration", "Weather:Snow Ripples", "Weather:Snow Ripple Radius", "Weather:Snow Ripples Per Flake", "Weather:Snow Ripple Scale", "Weather:Snow Ripple Speed", "Weather:Snow Gravity Scale", "Weather:Snow High Kill", "Weather:Snow Low Kill", "Weather Clear:Cloud Texture", "Weather Clear:Clouds Maximum Percent", "Weather Clear:Transition Delta", "Weather Clear:Sky Sunrise Color", "Weather Clear:Sky Day Color", "Weather Clear:Sky Sunset Color", "Weather Clear:Sky Night Color", "Weather Clear:Fog Sunrise Color", "Weather Clear:Fog Day Color", "Weather Clear:Fog Sunset Color", "Weather Clear:Fog Night Color", "Weather Clear:Ambient Sunrise Color", "Weather Clear:Ambient Day Color", "Weather Clear:Ambient Sunset Color", "Weather Clear:Ambient Night Color", "Weather Clear:Sun Sunrise Color", "Weather Clear:Sun Day Color", "Weather Clear:Sun Sunset Color", "Weather Clear:Sun Night Color", "Weather Clear:Sun Disc Sunset Color", "Weather Clear:Land Fog Day Depth", "Weather Clear:Land Fog Night Depth", "Weather Clear:Wind Speed", "Weather Clear:Cloud Speed", "Weather Clear:Glare View", "Weather Clear:Ambient Loop Sound ID", "Weather Cloudy:Cloud Texture", "Weather Cloudy:Clouds Maximum Percent", "Weather Cloudy:Transition Delta", "Weather Cloudy:Sky Sunrise Color", "Weather Cloudy:Sky Day Color", "Weather Cloudy:Sky Sunset Color", "Weather Cloudy:Sky Night Color", "Weather Cloudy:Fog Sunrise Color", "Weather Cloudy:Fog Day Color", "Weather Cloudy:Fog Sunset Color", "Weather Cloudy:Fog Night Color", "Weather Cloudy:Ambient Sunrise Color", "Weather Cloudy:Ambient Day Color", "Weather Cloudy:Ambient Sunset Color", "Weather Cloudy:Ambient Night Color", "Weather Cloudy:Sun Sunrise Color", "Weather Cloudy:Sun Day Color", "Weather Cloudy:Sun Sunset Color", "Weather Cloudy:Sun Night Color", "Weather Cloudy:Sun Disc Sunset Color", "Weather Cloudy:Land Fog Day Depth", "Weather Cloudy:Land Fog Night Depth", "Weather Cloudy:Wind Speed", "Weather Cloudy:Cloud Speed", "Weather Cloudy:Glare View", "Weather Cloudy:Ambient Loop Sound ID", "Weather Foggy:Cloud Texture", "Weather Foggy:Clouds Maximum Percent", "Weather Foggy:Transition Delta", "Weather Foggy:Sky Sunrise Color", "Weather Foggy:Sky Day Color", "Weather Foggy:Sky Sunset Color", "Weather Foggy:Sky Night Color", "Weather Foggy:Fog Sunrise Color", "Weather Foggy:Fog Day Color", "Weather Foggy:Fog Sunset Color", "Weather Foggy:Fog Night Color", "Weather Foggy:Ambient Sunrise Color", "Weather Foggy:Ambient Day Color", "Weather Foggy:Ambient Sunset Color", "Weather Foggy:Ambient Night Color", "Weather Foggy:Sun Sunrise Color", "Weather Foggy:Sun Day Color", "Weather Foggy:Sun Sunset Color", "Weather Foggy:Sun Night Color", "Weather Foggy:Sun Disc Sunset Color", "Weather Foggy:Land Fog Day Depth", "Weather Foggy:Land Fog Night Depth", "Weather Foggy:Wind Speed", "Weather Foggy:Cloud Speed", "Weather Foggy:Glare View", "Weather Foggy:Ambient Loop Sound ID", "Weather Thunderstorm:Cloud Texture", "Weather Thunderstorm:Clouds Maximum Percent", "Weather Thunderstorm:Transition Delta", "Weather Thunderstorm:Sky Sunrise Color", "Weather Thunderstorm:Sky Day Color", "Weather Thunderstorm:Sky Sunset Color", "Weather Thunderstorm:Sky Night Color", "Weather Thunderstorm:Fog Sunrise Color", "Weather Thunderstorm:Fog Day Color", "Weather Thunderstorm:Fog Sunset Color", "Weather Thunderstorm:Fog Night Color", "Weather Thunderstorm:Ambient Sunrise Color", "Weather Thunderstorm:Ambient Day Color", "Weather Thunderstorm:Ambient Sunset Color", "Weather Thunderstorm:Ambient Night Color", "Weather Thunderstorm:Sun Sunrise Color", "Weather Thunderstorm:Sun Day Color", "Weather Thunderstorm:Sun Sunset Color", "Weather Thunderstorm:Sun Night Color", "Weather Thunderstorm:Sun Disc Sunset Color", "Weather Thunderstorm:Land Fog Day Depth", "Weather Thunderstorm:Land Fog Night Depth", "Weather Thunderstorm:Wind Speed", "Weather Thunderstorm:Cloud Speed", "Weather Thunderstorm:Glare View", "Weather Thunderstorm:Rain Loop Sound ID", "Weather Thunderstorm:Using Precip", "Weather Thunderstorm:Rain Diameter", "Weather Thunderstorm:Rain Height Min", "Weather Thunderstorm:Rain Height Max", "Weather Thunderstorm:Rain Threshold", "Weather Thunderstorm:Max Raindrops", "Weather Thunderstorm:Rain Entrance Speed", "Weather Thunderstorm:Ambient Loop Sound ID", "Weather Thunderstorm:Flash Decrement", "Weather Rain:Cloud Texture", "Weather Rain:Clouds Maximum Percent", "Weather Rain:Transition Delta", "Weather Rain:Sky Sunrise Color", "Weather Rain:Sky Day Color", "Weather Rain:Sky Sunset Color", "Weather Rain:Sky Night Color", "Weather Rain:Fog Sunrise Color", "Weather Rain:Fog Day Color", "Weather Rain:Fog Sunset Color", "Weather Rain:Fog Night Color", "Weather Rain:Ambient Sunrise Color", "Weather Rain:Ambient Day Color", "Weather Rain:Ambient Sunset Color", "Weather Rain:Ambient Night Color", "Weather Rain:Sun Sunrise Color", "Weather Rain:Sun Day Color", "Weather Rain:Sun Sunset Color", "Weather Rain:Sun Night Color", "Weather Rain:Sun Disc Sunset Color", "Weather Rain:Land Fog Day Depth", "Weather Rain:Land Fog Night Depth", "Weather Rain:Wind Speed", "Weather Rain:Cloud Speed", "Weather Rain:Glare View", "Weather Rain:Rain Loop Sound ID", "Weather Rain:Using Precip", "Weather Rain:Rain Diameter", "Weather Rain:Rain Height Min", "Weather Rain:Rain Height Max", "Weather Rain:Rain Threshold", "Weather Rain:Rain Entrance Speed", "Weather Rain:Ambient Loop Sound ID", "Weather Rain:Max Raindrops", "Weather Overcast:Cloud Texture", "Weather Overcast:Clouds Maximum Percent", "Weather Overcast:Transition Delta", "Weather Overcast:Sky Sunrise Color", "Weather Overcast:Sky Day Color", "Weather Overcast:Sky Sunset Color", "Weather Overcast:Sky Night Color", "Weather Overcast:Fog Sunrise Color", "Weather Overcast:Fog Day Color", "Weather Overcast:Fog Sunset Color", "Weather Overcast:Fog Night Color", "Weather Overcast:Ambient Sunrise Color", "Weather Overcast:Ambient Day Color", "Weather Overcast:Ambient Sunset Color", "Weather Overcast:Ambient Night Color", "Weather Overcast:Sun Sunrise Color", "Weather Overcast:Sun Day Color", "Weather Overcast:Sun Sunset Color", "Weather Overcast:Sun Night Color", "Weather Overcast:Sun Disc Sunset Color", "Weather Overcast:Land Fog Day Depth", "Weather Overcast:Land Fog Night Depth", "Weather Overcast:Wind Speed", "Weather Overcast:Cloud Speed", "Weather Overcast:Glare View", "Weather Overcast:Ambient Loop Sound ID", "Weather Ashstorm:Cloud Texture", "Weather Ashstorm:Clouds Maximum Percent", "Weather Ashstorm:Transition Delta", "Weather Ashstorm:Sky Sunrise Color", "Weather Ashstorm:Sky Day Color", "Weather Ashstorm:Sky Sunset Color", "Weather Ashstorm:Sky Night Color", "Weather Ashstorm:Fog Sunrise Color", "Weather Ashstorm:Fog Day Color", "Weather Ashstorm:Fog Sunset Color", "Weather Ashstorm:Fog Night Color", "Weather Ashstorm:Ambient Sunrise Color", "Weather Ashstorm:Ambient Day Color", "Weather Ashstorm:Ambient Sunset Color", "Weather Ashstorm:Ambient Night Color", "Weather Ashstorm:Sun Sunrise Color", "Weather Ashstorm:Sun Day Color", "Weather Ashstorm:Sun Sunset Color", "Weather Ashstorm:Sun Night Color", "Weather Ashstorm:Sun Disc Sunset Color", "Weather Ashstorm:Land Fog Day Depth", "Weather Ashstorm:Land Fog Night Depth", "Weather Ashstorm:Wind Speed", "Weather Ashstorm:Cloud Speed", "Weather Ashstorm:Glare View", "Weather Ashstorm:Ambient Loop Sound ID", "Weather Ashstorm:Storm Threshold", "Weather Blight:Cloud Texture", "Weather Blight:Clouds Maximum Percent", "Weather Blight:Transition Delta", "Weather Blight:Sky Sunrise Color", "Weather Blight:Sky Day Color", "Weather Blight:Sky Sunset Color", "Weather Blight:Sky Night Color", "Weather Blight:Fog Sunrise Color", "Weather Blight:Fog Day Color", "Weather Blight:Fog Sunset Color", "Weather Blight:Fog Night Color", "Weather Blight:Ambient Sunrise Color", "Weather Blight:Ambient Day Color", "Weather Blight:Ambient Sunset Color", "Weather Blight:Ambient Night Color", "Weather Blight:Sun Sunrise Color", "Weather Blight:Sun Day Color", "Weather Blight:Sun Sunset Color", "Weather Blight:Sun Night Color", "Weather Blight:Sun Disc Sunset Color", "Weather Blight:Land Fog Day Depth", "Weather Blight:Land Fog Night Depth", "Weather Blight:Wind Speed", "Weather Blight:Cloud Speed", "Weather Blight:Glare View", "Weather Blight:Ambient Loop Sound ID", "Weather Blight:Storm Threshold", "Weather Blight:Disease Chance", // for Bloodmoon "Weather Snow:Cloud Texture", "Weather Snow:Clouds Maximum Percent", "Weather Snow:Transition Delta", "Weather Snow:Sky Sunrise Color", "Weather Snow:Sky Day Color", "Weather Snow:Sky Sunset Color", "Weather Snow:Sky Night Color", "Weather Snow:Fog Sunrise Color", "Weather Snow:Fog Day Color", "Weather Snow:Fog Sunset Color", "Weather Snow:Fog Night Color", "Weather Snow:Ambient Sunrise Color", "Weather Snow:Ambient Day Color", "Weather Snow:Ambient Sunset Color", "Weather Snow:Ambient Night Color", "Weather Snow:Sun Sunrise Color", "Weather Snow:Sun Day Color", "Weather Snow:Sun Sunset Color", "Weather Snow:Sun Night Color", "Weather Snow:Sun Disc Sunset Color", "Weather Snow:Land Fog Day Depth", "Weather Snow:Land Fog Night Depth", "Weather Snow:Wind Speed", "Weather Snow:Cloud Speed", "Weather Snow:Glare View", "Weather Snow:Snow Diameter", "Weather Snow:Snow Height Min", "Weather Snow:Snow Height Max", "Weather Snow:Snow Entrance Speed", "Weather Snow:Max Snowflakes", "Weather Snow:Ambient Loop Sound ID", "Weather Snow:Snow Threshold", // for Bloodmoon "Weather Blizzard:Cloud Texture", "Weather Blizzard:Clouds Maximum Percent", "Weather Blizzard:Transition Delta", "Weather Blizzard:Sky Sunrise Color", "Weather Blizzard:Sky Day Color", "Weather Blizzard:Sky Sunset Color", "Weather Blizzard:Sky Night Color", "Weather Blizzard:Fog Sunrise Color", "Weather Blizzard:Fog Day Color", "Weather Blizzard:Fog Sunset Color", "Weather Blizzard:Fog Night Color", "Weather Blizzard:Ambient Sunrise Color", "Weather Blizzard:Ambient Day Color", "Weather Blizzard:Ambient Sunset Color", "Weather Blizzard:Ambient Night Color", "Weather Blizzard:Sun Sunrise Color", "Weather Blizzard:Sun Day Color", "Weather Blizzard:Sun Sunset Color", "Weather Blizzard:Sun Night Color", "Weather Blizzard:Sun Disc Sunset Color", "Weather Blizzard:Land Fog Day Depth", "Weather Blizzard:Land Fog Night Depth", "Weather Blizzard:Wind Speed", "Weather Blizzard:Cloud Speed", "Weather Blizzard:Glare View", "Weather Blizzard:Ambient Loop Sound ID", "Weather Blizzard:Storm Threshold", // moons "Moons:Secunda Size", "Moons:Secunda Axis Offset", "Moons:Secunda Speed", "Moons:Secunda Daily Increment", "Moons:Secunda Moon Shadow Early Fade Angle", "Moons:Secunda Fade Start Angle", "Moons:Secunda Fade End Angle", "Moons:Secunda Fade In Start", "Moons:Secunda Fade In Finish", "Moons:Secunda Fade Out Start", "Moons:Secunda Fade Out Finish", "Moons:Masser Size", "Moons:Masser Axis Offset", "Moons:Masser Speed", "Moons:Masser Daily Increment", "Moons:Masser Moon Shadow Early Fade Angle", "Moons:Masser Fade Start Angle", "Moons:Masser Fade End Angle", "Moons:Masser Fade In Start", "Moons:Masser Fade In Finish", "Moons:Masser Fade Out Start", "Moons:Masser Fade Out Finish", "Moons:Script Color", // blood "Blood:Model 0", "Blood:Model 1", "Blood:Model 2", "Blood:Texture 0", "Blood:Texture 1", "Blood:Texture 2", "Blood:Texture Name 0", "Blood:Texture Name 1", "Blood:Texture Name 2", // werewolf (Bloodmoon) "General:Werewolf FOV", 0 }; for(int i=0; map[i][0]; i++) { mMergeMap.insert(std::make_pair(map[i][0], map[i][1])); } for(int i=0; fallback[i]; i++) { mMergeFallback.push_back(fallback[i]); } } void MwIniImporter::setVerbose(bool verbose) { mVerbose = verbose; } std::string MwIniImporter::numberToString(int n) { std::stringstream str; str << n; return str.str(); } MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path& filename) const { std::cout << "load ini file: " << filename << std::endl; std::string section(""); MwIniImporter::multistrmap map; bfs::ifstream file((bfs::path(filename))); ToUTF8::Utf8Encoder encoder(mEncoding); std::string line; while (std::getline(file, line)) { line = encoder.getUtf8(line); // unify Unix-style and Windows file ending if (!(line.empty()) && (line[line.length()-1]) == '\r') { line = line.substr(0, line.length()-1); } if(line.empty()) { continue; } if(line[0] == '[') { int pos = line.find(']'); if(pos < 2) { std::cout << "Warning: ini file wrongly formatted (" << line << "). Line ignored." << std::endl; continue; } section = line.substr(1, line.find(']')-1); continue; } int comment_pos = line.find(";"); if(comment_pos > 0) { line = line.substr(0,comment_pos); } int pos = line.find("="); if(pos < 1) { continue; } std::string key(section + ":" + line.substr(0,pos)); std::string value(line.substr(pos+1)); if(value.empty()) { std::cout << "Warning: ignored empty value for key '" << key << "'." << std::endl; continue; } if(map.find(key) == map.end()) { map.insert( std::make_pair (key, std::vector() ) ); } map[key].push_back(value); } return map; } MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const boost::filesystem::path& filename) { std::cout << "load cfg file: " << filename << std::endl; MwIniImporter::multistrmap map; bfs::ifstream file((bfs::path(filename))); std::string line; while (std::getline(file, line)) { // we cant say comment by only looking at first char anymore int comment_pos = line.find("#"); if(comment_pos > 0) { line = line.substr(0,comment_pos); } if(line.empty()) { continue; } int pos = line.find("="); if(pos < 1) { continue; } std::string key(line.substr(0,pos)); std::string value(line.substr(pos+1)); if(map.find(key) == map.end()) { map.insert( std::make_pair (key, std::vector() ) ); } map[key].push_back(value); } return map; } void MwIniImporter::merge(multistrmap &cfg, const multistrmap &ini) const { multistrmap::const_iterator iniIt; for(strmap::const_iterator it=mMergeMap.begin(); it!=mMergeMap.end(); ++it) { if((iniIt = ini.find(it->second)) != ini.end()) { for(std::vector::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) { cfg.erase(it->first); insertMultistrmap(cfg, it->first, *vc); } } } } void MwIniImporter::mergeFallback(multistrmap &cfg, const multistrmap &ini) const { cfg.erase("fallback"); multistrmap::const_iterator iniIt; for(std::vector::const_iterator it=mMergeFallback.begin(); it!=mMergeFallback.end(); ++it) { if((iniIt = ini.find(*it)) != ini.end()) { for(std::vector::const_iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); ++vc) { std::string value(*it); std::replace( value.begin(), value.end(), ' ', '_' ); std::replace( value.begin(), value.end(), ':', '_' ); value.append(",").append(vc->substr(0,vc->length())); insertMultistrmap(cfg, "fallback", value); } } } } void MwIniImporter::insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value) { const multistrmap::const_iterator it = cfg.find(key); if(it == cfg.end()) { cfg.insert(std::make_pair (key, std::vector() )); } cfg[key].push_back(value); } void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) const { std::vector archives; std::string baseArchive("Archives:Archive "); std::string archive; // Search archives listed in ini file multistrmap::const_iterator it = ini.begin(); for(int i=0; it != ini.end(); i++) { archive = baseArchive; archive.append(this->numberToString(i)); it = ini.find(archive); if(it == ini.end()) { break; } for(std::vector::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { archives.push_back(*entry); } } cfg.erase("fallback-archive"); cfg.insert( std::make_pair > ("fallback-archive", std::vector())); // Add Morrowind.bsa by default, since Vanilla loads this archive even if it // does not appears in the ini file cfg["fallback-archive"].push_back("Morrowind.bsa"); for(std::vector::const_iterator it=archives.begin(); it!=archives.end(); ++it) { cfg["fallback-archive"].push_back(*it); } } void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const { std::vector > contentFiles; std::string baseGameFile("Game Files:GameFile"); std::string gameFile(""); std::time_t defaultTime = 0; // assume the Game Files are all in a "Data Files" directory under the directory holding Morrowind.ini const boost::filesystem::path gameFilesDir(iniFilename.parent_path() /= "Data Files"); multistrmap::const_iterator it = ini.begin(); for(int i=0; it != ini.end(); i++) { gameFile = baseGameFile; gameFile.append(this->numberToString(i)); it = ini.find(gameFile); if(it == ini.end()) { break; } for(std::vector::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { std::string filetype(entry->substr(entry->length()-3)); Misc::StringUtils::lowerCaseInPlace(filetype); if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { boost::filesystem::path filepath(gameFilesDir); filepath /= *entry; contentFiles.push_back(std::make_pair(lastWriteTime(filepath, defaultTime), *entry)); } } } cfg.erase("content"); cfg.insert( std::make_pair("content", std::vector() ) ); // this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed. sort(contentFiles.begin(), contentFiles.end()); for(std::vector >::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) { cfg["content"].push_back(it->second); } } void MwIniImporter::writeToFile(std::ostream &out, const multistrmap &cfg) { for(multistrmap::const_iterator it=cfg.begin(); it != cfg.end(); ++it) { for(std::vector::const_iterator entry=it->second.begin(); entry != it->second.end(); ++entry) { out << (it->first) << "=" << (*entry) << std::endl; } } } void MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding) { mEncoding = encoding; } std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime) { std::time_t writeTime(defaultTime); if (boost::filesystem::exists(filename)) { // FixMe: remove #if when Boost dependency for Linux builds updated // This allows Linux to build until then #if (BOOST_VERSION >= 104800) // need to resolve any symlinks so that we get time of file, not symlink boost::filesystem::path resolved = boost::filesystem::canonical(filename); #else boost::filesystem::path resolved = filename; #endif writeTime = boost::filesystem::last_write_time(resolved); // print timestamp const int size=1024; char timeStrBuffer[size]; if (std::strftime(timeStrBuffer, size, "%x %X", localtime(&writeTime)) > 0) std::cout << "content file: " << resolved << " timestamp = (" << writeTime << ") " << timeStrBuffer << std::endl; } else { std::cout << "content file: " << filename << " not found" << std::endl; } return writeTime; } openmw-openmw-0.38.0/apps/mwiniimporter/importer.hpp000066400000000000000000000030711264522266000226420ustar00rootroot00000000000000#ifndef MWINIIMPORTER_IMPORTER #define MWINIIMPORTER_IMPORTER 1 #include #include #include #include #include #include #include class MwIniImporter { public: typedef std::map strmap; typedef std::map > multistrmap; MwIniImporter(); void setInputEncoding(const ToUTF8::FromType& encoding); void setVerbose(bool verbose); multistrmap loadIniFile(const boost::filesystem::path& filename) const; static multistrmap loadCfgFile(const boost::filesystem::path& filename); void merge(multistrmap &cfg, const multistrmap &ini) const; void mergeFallback(multistrmap &cfg, const multistrmap &ini) const; void importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const; void importArchives(multistrmap &cfg, const multistrmap &ini) const; static void writeToFile(std::ostream &out, const multistrmap &cfg); private: static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); static std::string numberToString(int n); /// \return file's "last modified time", used in original MW to determine plug-in load order static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime); bool mVerbose; strmap mMergeMap; std::vector mMergeFallback; ToUTF8::FromType mEncoding; }; #endif openmw-openmw-0.38.0/apps/mwiniimporter/main.cpp000066400000000000000000000111001264522266000217100ustar00rootroot00000000000000#include "importer.hpp" #include #include #include #include #include #include namespace bpo = boost::program_options; namespace bfs = boost::filesystem; #ifndef _WIN32 int main(int argc, char *argv[]) { #else // Include on Windows only #include class utf8argv { public: utf8argv(int argc, wchar_t *wargv[]) { args.reserve(argc); argv = new const char *[argc]; for (int i = 0; i < argc; ++i) { args.push_back(boost::locale::conv::utf_to_utf(wargv[i])); argv[i] = args.back().c_str(); } } ~utf8argv() { delete[] argv; } char **get() const { return const_cast(argv); } private: utf8argv(const utf8argv&); utf8argv& operator=(const utf8argv&); const char **argv; std::vector args; }; /* The only way to pass Unicode on Winodws with CLI is to use wide characters interface which presents UTF-16 encoding. The rest of OpenMW application stack assumes UTF-8 encoding, therefore this conversion. For boost::filesystem::path::imbue see components/files/windowspath.cpp */ int wmain(int argc, wchar_t *wargv[]) { utf8argv converter(argc, wargv); char **argv = converter.get(); boost::filesystem::path::imbue(boost::locale::generator().generate("")); #endif try { bpo::options_description desc("Syntax: openmw-iniimporter inifile configfile\nAllowed options"); bpo::positional_options_description p_desc; desc.add_options() ("help,h", "produce help message") ("verbose,v", "verbose output") ("ini,i", bpo::value(), "morrowind.ini file") ("cfg,c", bpo::value(), "openmw.cfg file") ("output,o", bpo::value()->default_value(""), "openmw.cfg file") ("game-files,g", "import esm and esp files") ("no-archives,A", "disable bsa archives import") ("encoding,e", bpo::value()-> default_value("win1252"), "Character encoding used in OpenMW game messages:\n" "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" "\n\twin1252 - Western European (Latin) alphabet, used by default") ; p_desc.add("ini", 1).add("cfg", 1); bpo::variables_map vm; bpo::parsed_options parsed = bpo::command_line_parser(argc, argv) .options(desc) .positional(p_desc) .run(); bpo::store(parsed, vm); if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) { std::cout << desc; return 0; } bpo::notify(vm); boost::filesystem::path iniFile(vm["ini"].as()); boost::filesystem::path cfgFile(vm["cfg"].as()); // if no output is given, write back to cfg file std::string outputFile(vm["output"].as()); if(vm["output"].defaulted()) { outputFile = vm["cfg"].as(); } if(!boost::filesystem::exists(iniFile)) { std::cerr << "ini file does not exist" << std::endl; return -3; } if(!boost::filesystem::exists(cfgFile)) std::cerr << "cfg file does not exist" << std::endl; MwIniImporter importer; importer.setVerbose(vm.count("verbose") != 0); // Font encoding settings std::string encoding(vm["encoding"].as()); importer.setInputEncoding(ToUTF8::calculateEncoding(encoding)); MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile); MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile); importer.merge(cfg, ini); importer.mergeFallback(cfg, ini); if(vm.count("game-files")) { importer.importGameFiles(cfg, ini, iniFile); } if(!vm.count("no-archives")) { importer.importArchives(cfg, ini); } std::cout << "write to: " << outputFile << std::endl; bfs::ofstream file((bfs::path(outputFile))); importer.writeToFile(file, cfg); } catch (std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; } return 0; } openmw-openmw-0.38.0/apps/niftest/000077500000000000000000000000001264522266000170365ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/niftest/CMakeLists.txt000066400000000000000000000005071264522266000216000ustar00rootroot00000000000000set(NIFTEST niftest.cpp ) source_group(components\\nif\\tests FILES ${NIFTEST}) # Main executable add_executable(niftest ${NIFTEST} ) target_link_libraries(niftest ${Boost_FILESYSTEM_LIBRARY} components ) if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(niftest gcov) endif() openmw-openmw-0.38.0/apps/niftest/niftest.cpp000066400000000000000000000122031264522266000212140ustar00rootroot00000000000000///Program to test .nif files both on the FileSystem and in BSA archives. #include #include #include #include #include #include #include #include #include #include // Create local aliases for brevity namespace bpo = boost::program_options; namespace bfs = boost::filesystem; ///See if the file has the named extension bool hasExtension(std::string filename, std::string extensionToFind) { std::string extension = filename.substr(filename.find_last_of(".")+1); //Convert strings to lower case for comparison std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower); if(extension == extensionToFind) return true; else return false; } ///See if the file has the "nif" extension. bool isNIF(std::string filename) { return hasExtension(filename,"nif"); } ///See if the file has the "bsa" extension. bool isBSA(std::string filename) { return hasExtension(filename,"bsa"); } /// Check all the nif files in a given VFS::Archive /// \note Takes ownership! /// \note Can not read a bsa file inside of a bsa file. void readVFS(VFS::Archive* anArchive,std::string archivePath = "") { VFS::Manager myManager(true); myManager.addArchive(anArchive); myManager.buildIndex(); std::map files=myManager.getIndex(); for(std::map::const_iterator it=files.begin(); it!=files.end(); ++it) { std::string name = it->first; try{ if(isNIF(name)) { // std::cout << "Decoding: " << name << std::endl; Nif::NIFFile temp_nif(myManager.get(name),archivePath+name); } else if(isBSA(name)) { if(!archivePath.empty() && !isBSA(archivePath)) { // std::cout << "Reading BSA File: " << name << std::endl; readVFS(new VFS::BsaArchive(archivePath+name),archivePath+name+"/"); // std::cout << "Done with BSA File: " << name << std::endl; } } } catch (std::exception& e) { std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl; } } } std::vector parseOptions (int argc, char** argv) { bpo::options_description desc("Ensure that OpenMW can use the provided NIF and BSA files\n\n" "Usages:\n" " niftool \n" " Scan the file or directories for nif errors.\n\n" "Allowed options"); desc.add_options() ("help,h", "print help message.") ("input-file", bpo::value< std::vector >(), "input file") ; //Default option if none provided bpo::positional_options_description p; p.add("input-file", -1); bpo::variables_map variables; try { bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv). options(desc).positional(p).run(); bpo::store(valid_opts, variables); } catch(std::exception &e) { std::cout << "ERROR parsing arguments: " << e.what() << "\n\n" << desc << std::endl; exit(1); } bpo::notify(variables); if (variables.count ("help")) { std::cout << desc << std::endl; exit(1); } if (variables.count("input-file")) { return variables["input-file"].as< std::vector >(); } std::cout << "No input files or directories specified!" << std::endl; std::cout << desc << std::endl; exit(1); } int main(int argc, char **argv) { std::vector files = parseOptions (argc, argv); // std::cout << "Reading Files" << std::endl; for(std::vector::const_iterator it=files.begin(); it!=files.end(); ++it) { std::string name = *it; try{ if(isNIF(name)) { //std::cout << "Decoding: " << name << std::endl; Nif::NIFFile temp_nif(Files::openConstrainedFileStream(name.c_str()),name); } else if(isBSA(name)) { // std::cout << "Reading BSA File: " << name << std::endl; readVFS(new VFS::BsaArchive(name)); } else if(bfs::is_directory(bfs::path(name))) { // std::cout << "Reading All Files in: " << name << std::endl; readVFS(new VFS::FileSystemArchive(name),name); } else { std::cerr << "ERROR: \"" << name << "\" is not a nif file, bsa file, or directory!" << std::endl; } } catch (std::exception& e) { std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl; } } return 0; } openmw-openmw-0.38.0/apps/opencs/000077500000000000000000000000001264522266000166515ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/CMakeLists.txt000066400000000000000000000141351264522266000214150ustar00rootroot00000000000000set (OPENCS_SRC main.cpp ${CMAKE_SOURCE_DIR}/files/windows/opencs.rc ) opencs_units (. editor) opencs_units (model/doc document operation saving documentmanager loader runner operationholder ) opencs_units_noqt (model/doc stage savingstate savingstages blacklist messages ) opencs_hdrs_noqt (model/doc state ) opencs_units (model/world idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel ) opencs_units_noqt (model/world universalid record commands columnbase columnimp scriptcontext cell refidcollection refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection idcompletionmanager metadata ) opencs_hdrs_noqt (model/world columnimp idcollection collection info subcellcollection ) opencs_units (model/tools tools reportmodel mergeoperation ) opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck mergestages ) opencs_hdrs_noqt (model/tools mergestate ) opencs_units (view/doc viewmanager view operations operation subview startup filedialog newgame filewidget adjusterwidget loader globaldebugprofilemenu runlogsubview sizehint ) opencs_units_noqt (view/doc subviewfactory ) opencs_hdrs_noqt (view/doc subviewfactoryimp ) opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator startscriptcreator referencecreator scenesubview infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator ) opencs_units_noqt (view/world subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate scripthighlighter idvalidator dialoguecreator idcompletiondelegate colordelegate dragdroputils ) opencs_units (view/widget scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton scenetooltoggle2 completerpopup coloreditor colorpickerpopup droplineedit ) opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode ) opencs_units_noqt (view/render lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase cellarrow ) opencs_hdrs_noqt (view/render elements ) opencs_units (view/tools reportsubview reporttable searchsubview searchbox merge ) opencs_units_noqt (view/tools subviews ) opencs_units (view/prefs dialogue pagebase page ) opencs_units (model/prefs state setting intsetting doublesetting boolsetting enumsetting coloursetting ) opencs_units_noqt (model/prefs category ) opencs_units_noqt (model/filter node unarynode narynode leafnode booleannode parser andnode ornode notnode textnode valuenode ) opencs_units (view/filter filterbox recordfilterbox editwidget ) set (OPENCS_US ) set (OPENCS_RES ${CMAKE_SOURCE_DIR}/files/opencs/resources.qrc ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc ) set (OPENCS_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui ) source_group (openmw-cs FILES ${OPENCS_SRC} ${OPENCS_HDR}) if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) if (DESIRED_QT_VERSION MATCHES 4) include(${QT_USE_FILE}) qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) else() qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) endif() # for compiled .ui files include_directories(${CMAKE_CURRENT_BINARY_DIR}) if(APPLE) set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns) else() set (OPENCS_MAC_ICON "") endif(APPLE) add_executable(openmw-cs MACOSX_BUNDLE ${OPENCS_SRC} ${OPENCS_UI_HDR} ${OPENCS_MOC_SRC} ${OPENCS_RES_SRC} ${OPENCS_MAC_ICON} ) if(APPLE) set_target_properties(openmw-cs PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}" OUTPUT_NAME "OpenMW-CS" MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns" MACOSX_BUNDLE_BUNDLE_NAME "OpenCS" MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs" MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION} MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION} MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/files/mac/openmw-cs-Info.plist.in" ) set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) endif(APPLE) target_link_libraries(openmw-cs ${OSG_LIBRARIES} ${OPENTHREADS_LIBRARIES} ${OSGUTIL_LIBRARIES} ${OSGVIEWER_LIBRARIES} ${OSGGA_LIBRARIES} ${OSGFX_LIBRARIES} ${OSGQT_LIBRARIES} ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} components ) if (DESIRED_QT_VERSION MATCHES 4) target_link_libraries(openmw-cs ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${QT_QTOPENGL_LIBRARY}) if (WIN32) target_link_libraries(openmw-cs ${QT_QTMAIN_LIBRARY}) endif() else() qt5_use_modules(openmw-cs Widgets Core Network OpenGL) if (WIN32) target_link_libraries(Qt5::WinMain) endif() endif() if (WIN32) target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY}) endif() if(APPLE) INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) endif() openmw-openmw-0.38.0/apps/opencs/editor.cpp000066400000000000000000000266651264522266000206620ustar00rootroot00000000000000#include "editor.hpp" #include #include #include #include #include #include #include #include "model/doc/document.hpp" #include "model/world/data.hpp" #ifdef _WIN32 #include #endif CS::Editor::Editor () : mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr), mViewManager (mDocumentManager), mPid(""), mLock(), mMerge (mDocumentManager), mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) { std::pair > config = readConfig(); setupDataFiles (config.first); NifOsg::Loader::setShowMarkers(true); mVFS.reset(new VFS::Manager(mFsStrict)); VFS::registerArchives(mVFS.get(), Files::Collections(config.first, !mFsStrict), config.second, true); mDocumentManager.setVFS(mVFS.get()); mNewGame.setLocalData (mLocal); mFileDialog.setLocalData (mLocal); mMerge.setLocalData (mLocal); connect (&mDocumentManager, SIGNAL (documentAdded (CSMDoc::Document *)), this, SLOT (documentAdded (CSMDoc::Document *))); connect (&mDocumentManager, SIGNAL (documentAboutToBeRemoved (CSMDoc::Document *)), this, SLOT (documentAboutToBeRemoved (CSMDoc::Document *))); connect (&mDocumentManager, SIGNAL (lastDocumentDeleted()), this, SLOT (lastDocumentDeleted())); connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ())); connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ())); connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ())); connect (&mViewManager, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SLOT (mergeDocument (CSMDoc::Document *))); connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ())); connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ())); connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ())); connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ())); connect (&mFileDialog, SIGNAL(signalOpenFiles (const boost::filesystem::path&)), this, SLOT(openFiles (const boost::filesystem::path&))); connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)), this, SLOT(createNewFile (const boost::filesystem::path&))); connect (&mFileDialog, SIGNAL (rejected()), this, SLOT (cancelFileDialog ())); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), this, SLOT (createNewGame (const boost::filesystem::path&))); connect (&mNewGame, SIGNAL (cancelCreateGame()), this, SLOT (cancelCreateGame ())); } CS::Editor::~Editor () { mPidFile.close(); if(mServer && boost::filesystem::exists(mPid)) static_cast ( // silence coverity warning remove(mPid.string().c_str())); // ignore any error } void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs) { for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { QString path = QString::fromUtf8 (iter->string().c_str()); mFileDialog.addFiles(path); } } std::pair > CS::Editor::readConfig(bool quiet) { boost::program_options::variables_map variables; boost::program_options::options_description desc("Syntax: openmw-cs \nAllowed options"); desc.add_options() ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()->composing()) ("data-local", boost::program_options::value()->default_value("")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) ("encoding", boost::program_options::value()->default_value("win1252")) ("resources", boost::program_options::value()->default_value("resources")) ("fallback-archive", boost::program_options::value >()-> default_value(std::vector(), "fallback-archive")->multitoken()) ("script-blacklist", boost::program_options::value >()->default_value(std::vector(), "") ->multitoken(), "exclude specified script from the verifier (if the use of the blacklist is enabled)") ("script-blacklist-use", boost::program_options::value()->implicit_value(true) ->default_value(true), "enable script blacklisting"); boost::program_options::notify(variables); mCfgMgr.readConfiguration(variables, desc, quiet); mDocumentManager.setEncoding ( ToUTF8::calculateEncoding (variables["encoding"].as())); mDocumentManager.setResourceDir (mResources = variables["resources"].as()); if (variables["script-blacklist-use"].as()) mDocumentManager.setBlacklistedScripts ( variables["script-blacklist"].as >()); mFsStrict = variables["fs-strict"].as(); Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { dataDirs = Files::PathContainer(variables["data"].as()); } std::string local = variables["data-local"].as(); if (!local.empty()) { dataLocal.push_back(Files::PathContainer::value_type(local)); } mCfgMgr.processPaths (dataDirs); mCfgMgr.processPaths (dataLocal, true); if (!dataLocal.empty()) mLocal = dataLocal[0]; else { QMessageBox messageBox; messageBox.setWindowTitle (tr ("No local data path available")); messageBox.setIcon (QMessageBox::Critical); messageBox.setStandardButtons (QMessageBox::Ok); messageBox.setText(tr("
OpenCS is unable to access the local data directory. This may indicate a faulty configuration or a broken install.")); messageBox.exec(); QApplication::exit (1); } dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); //iterate the data directories and add them to the file dialog for loading for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { QString path = QString::fromUtf8 (iter->string().c_str()); mFileDialog.addFiles(path); } return std::make_pair (dataDirs, variables["fallback-archive"].as >()); } void CS::Editor::createGame() { mStartup.hide(); if (mNewGame.isHidden()) mNewGame.show(); mNewGame.raise(); mNewGame.activateWindow(); } void CS::Editor::cancelCreateGame() { if (!mDocumentManager.isEmpty()) return; mNewGame.hide(); if (mStartup.isHidden()) mStartup.show(); mStartup.raise(); mStartup.activateWindow(); } void CS::Editor::createAddon() { mStartup.hide(); mFileDialog.clearFiles(); std::pair > config = readConfig(/*quiet*/true); setupDataFiles (config.first); mFileDialog.showDialog (CSVDoc::ContentAction_New); } void CS::Editor::cancelFileDialog() { if (!mDocumentManager.isEmpty()) return; mFileDialog.hide(); if (mStartup.isHidden()) mStartup.show(); mStartup.raise(); mStartup.activateWindow(); } void CS::Editor::loadDocument() { mStartup.hide(); mFileDialog.clearFiles(); std::pair > config = readConfig(/*quiet*/true); setupDataFiles (config.first); mFileDialog.showDialog (CSVDoc::ContentAction_Edit); } void CS::Editor::openFiles (const boost::filesystem::path &savePath) { std::vector files; foreach (const QString &path, mFileDialog.selectedFilePaths()) files.push_back(path.toUtf8().constData()); mDocumentManager.addDocument (files, savePath, false); mFileDialog.hide(); } void CS::Editor::createNewFile (const boost::filesystem::path &savePath) { std::vector files; foreach (const QString &path, mFileDialog.selectedFilePaths()) { files.push_back(path.toUtf8().constData()); } files.push_back (savePath); mDocumentManager.addDocument (files, savePath, true); mFileDialog.hide(); } void CS::Editor::createNewGame (const boost::filesystem::path& file) { std::vector files; files.push_back (file); mDocumentManager.addDocument (files, file, true); mNewGame.hide(); } void CS::Editor::showStartup() { if(mStartup.isHidden()) mStartup.show(); mStartup.raise(); mStartup.activateWindow(); } void CS::Editor::showSettings() { if (mSettings.isHidden()) mSettings.show(); mSettings.move (QCursor::pos()); mSettings.raise(); mSettings.activateWindow(); } bool CS::Editor::makeIPCServer() { try { mPid = boost::filesystem::temp_directory_path(); mPid /= "openmw-cs.pid"; bool pidExists = boost::filesystem::exists(mPid); mPidFile.open(mPid); mLock = boost::interprocess::file_lock(mPid.string().c_str()); if(!mLock.try_lock()) { std::cerr << "OpenCS already running." << std::endl; return false; } #ifdef _WIN32 mPidFile << GetCurrentProcessId() << std::endl; #else mPidFile << getpid() << std::endl; #endif mServer = new QLocalServer(this); if(pidExists) { // hack to get the temp directory path mServer->listen("dummy"); QString fullPath = mServer->fullServerName(); mServer->close(); fullPath.remove(QRegExp("dummy$")); fullPath += mIpcServerName; if(boost::filesystem::exists(fullPath.toUtf8().constData())) { // TODO: compare pid of the current process with that in the file std::cout << "Detected unclean shutdown." << std::endl; // delete the stale file if(remove(fullPath.toUtf8().constData())) std::cerr << "ERROR removing stale connection file" << std::endl; } } } catch(const std::exception& e) { std::cerr << "ERROR " << e.what() << std::endl; return false; } if(mServer->listen(mIpcServerName)) { connect(mServer, SIGNAL(newConnection()), this, SLOT(showStartup())); return true; } mServer->close(); mServer = NULL; return false; } void CS::Editor::connectToIPCServer() { mClientSocket = new QLocalSocket(this); mClientSocket->connectToServer(mIpcServerName); mClientSocket->close(); } int CS::Editor::run() { if (mLocal.empty()) return 1; mStartup.show(); QApplication::setQuitOnLastWindowClosed (true); return QApplication::exec(); } void CS::Editor::documentAdded (CSMDoc::Document *document) { mViewManager.addView (document); } void CS::Editor::documentAboutToBeRemoved (CSMDoc::Document *document) { if (mMerge.getDocument()==document) mMerge.cancel(); } void CS::Editor::lastDocumentDeleted() { QApplication::quit(); } void CS::Editor::mergeDocument (CSMDoc::Document *document) { mMerge.configure (document); mMerge.show(); mMerge.raise(); mMerge.activateWindow(); } openmw-openmw-0.38.0/apps/opencs/editor.hpp000066400000000000000000000057061264522266000206600ustar00rootroot00000000000000#ifndef CS_EDITOR_H #define CS_EDITOR_H #include #include #include #include #include #include #include #ifndef Q_MOC_RUN #include #endif #include #include "model/doc/documentmanager.hpp" #include "model/prefs/state.hpp" #include "view/doc/viewmanager.hpp" #include "view/doc/startup.hpp" #include "view/doc/filedialog.hpp" #include "view/doc/newgame.hpp" #include "view/prefs/dialogue.hpp" #include "view/tools/merge.hpp" namespace VFS { class Manager; } namespace CSMDoc { class Document; } namespace CS { class Editor : public QObject { Q_OBJECT // FIXME: should be moved to document, so we can have different resources for each opened project std::auto_ptr mVFS; Files::ConfigurationManager mCfgMgr; CSMPrefs::State mSettingsState; CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; CSVDoc::StartupDialogue mStartup; CSVDoc::NewGameDialogue mNewGame; CSVPrefs::Dialogue mSettings; CSVDoc::FileDialog mFileDialog; boost::filesystem::path mLocal; boost::filesystem::path mResources; boost::filesystem::path mPid; boost::interprocess::file_lock mLock; boost::filesystem::ofstream mPidFile; bool mFsStrict; CSVTools::Merge mMerge; void setupDataFiles (const Files::PathContainer& dataDirs); std::pair > readConfig(bool quiet=false); ///< \return data paths // not implemented Editor (const Editor&); Editor& operator= (const Editor&); public: Editor (); ~Editor (); bool makeIPCServer(); void connectToIPCServer(); int run(); ///< \return error status private slots: void createGame(); void createAddon(); void cancelCreateGame(); void cancelFileDialog(); void loadDocument(); void openFiles (const boost::filesystem::path &path); void createNewFile (const boost::filesystem::path& path); void createNewGame (const boost::filesystem::path& file); void showStartup(); void showSettings(); void documentAdded (CSMDoc::Document *document); void documentAboutToBeRemoved (CSMDoc::Document *document); void lastDocumentDeleted(); void mergeDocument (CSMDoc::Document *document); private: QString mIpcServerName; QLocalServer *mServer; QLocalSocket *mClientSocket; }; } #endif openmw-openmw-0.38.0/apps/opencs/main.cpp000066400000000000000000000037061264522266000203070ustar00rootroot00000000000000#include "editor.hpp" #include #include #include #include #include #include #include "model/doc/messages.hpp" #include "model/world/universalid.hpp" #ifdef Q_OS_MAC #include #endif Q_DECLARE_METATYPE (std::string) class Application : public QApplication { private: bool notify (QObject *receiver, QEvent *event) { try { return QApplication::notify (receiver, event); } catch (const std::exception& exception) { std::cerr << "An exception has been caught: " << exception.what() << std::endl; } return false; } public: Application (int& argc, char *argv[]) : QApplication (argc, argv) {} }; int main(int argc, char *argv[]) { #ifdef Q_OS_MAC setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); #endif try { // To allow background thread drawing in OSG QApplication::setAttribute(Qt::AA_X11InitThreads, true); Q_INIT_RESOURCE (resources); qRegisterMetaType ("std::string"); qRegisterMetaType ("CSMWorld::UniversalId"); qRegisterMetaType ("CSMDoc::Message"); Application application (argc, argv); #ifdef Q_OS_MAC QDir dir(QCoreApplication::applicationDirPath()); if (dir.dirName() == "MacOS") { dir.cdUp(); dir.cdUp(); dir.cdUp(); } QDir::setCurrent(dir.absolutePath()); #endif application.setWindowIcon (QIcon (":./openmw-cs.png")); CS::Editor editor; if(!editor.makeIPCServer()) { editor.connectToIPCServer(); return 0; } return editor.run(); } catch (std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; return 0; } } openmw-openmw-0.38.0/apps/opencs/model/000077500000000000000000000000001264522266000177515ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/model/doc/000077500000000000000000000000001264522266000205165ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/model/doc/blacklist.cpp000066400000000000000000000015011264522266000231670ustar00rootroot00000000000000#include "blacklist.hpp" #include #include bool CSMDoc::Blacklist::isBlacklisted (const CSMWorld::UniversalId& id) const { std::map >::const_iterator iter = mIds.find (id.getType()); if (iter==mIds.end()) return false; return std::binary_search (iter->second.begin(), iter->second.end(), Misc::StringUtils::lowerCase (id.getId())); } void CSMDoc::Blacklist::add (CSMWorld::UniversalId::Type type, const std::vector& ids) { std::vector& list = mIds[type]; int size = list.size(); list.resize (size+ids.size()); std::transform (ids.begin(), ids.end(), list.begin()+size, Misc::StringUtils::lowerCase); std::sort (list.begin(), list.end()); } openmw-openmw-0.38.0/apps/opencs/model/doc/blacklist.hpp000066400000000000000000000010231264522266000231730ustar00rootroot00000000000000#ifndef CSM_DOC_BLACKLIST_H #define CSM_DOC_BLACKLIST_H #include #include #include #include "../world/universalid.hpp" namespace CSMDoc { /// \brief ID blacklist sorted by UniversalId type class Blacklist { std::map > mIds; public: bool isBlacklisted (const CSMWorld::UniversalId& id) const; void add (CSMWorld::UniversalId::Type type, const std::vector& ids); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/document.cpp000066400000000000000000002060001264522266000230360ustar00rootroot00000000000000#include "document.hpp" #include #include #include #include #ifndef Q_MOC_RUN #include #endif void CSMDoc::Document::addGmsts() { static const char *gmstFloats[] = { "fAIFleeFleeMult", "fAIFleeHealthMult", "fAIMagicSpellMult", "fAIMeleeArmorMult", "fAIMeleeSummWeaponMult", "fAIMeleeWeaponMult", "fAIRangeMagicSpellMult", "fAIRangeMeleeWeaponMult", "fAlarmRadius", "fAthleticsRunBonus", "fAudioDefaultMaxDistance", "fAudioDefaultMinDistance", "fAudioMaxDistanceMult", "fAudioMinDistanceMult", "fAudioVoiceDefaultMaxDistance", "fAudioVoiceDefaultMinDistance", "fAutoPCSpellChance", "fAutoSpellChance", "fBargainOfferBase", "fBargainOfferMulti", "fBarterGoldResetDelay", "fBaseRunMultiplier", "fBlockStillBonus", "fBribe1000Mod", "fBribe100Mod", "fBribe10Mod", "fCombatAngleXY", "fCombatAngleZ", "fCombatArmorMinMult", "fCombatBlockLeftAngle", "fCombatBlockRightAngle", "fCombatCriticalStrikeMult", "fCombatDelayCreature", "fCombatDelayNPC", "fCombatDistance", "fCombatDistanceWerewolfMod", "fCombatForceSideAngle", "fCombatInvisoMult", "fCombatKODamageMult", "fCombatTorsoSideAngle", "fCombatTorsoStartPercent", "fCombatTorsoStopPercent", "fConstantEffectMult", "fCorpseClearDelay", "fCorpseRespawnDelay", "fCrimeGoldDiscountMult", "fCrimeGoldTurnInMult", "fCrimeStealing", "fDamageStrengthBase", "fDamageStrengthMult", "fDifficultyMult", "fDiseaseXferChance", "fDispAttacking", "fDispBargainFailMod", "fDispBargainSuccessMod", "fDispCrimeMod", "fDispDiseaseMod", "fDispFactionMod", "fDispFactionRankBase", "fDispFactionRankMult", "fDispositionMod", "fDispPersonalityBase", "fDispPersonalityMult", "fDispPickPocketMod", "fDispRaceMod", "fDispStealing", "fDispWeaponDrawn", "fEffectCostMult", "fElementalShieldMult", "fEnchantmentChanceMult", "fEnchantmentConstantChanceMult", "fEnchantmentConstantDurationMult", "fEnchantmentMult", "fEnchantmentValueMult", "fEncumberedMoveEffect", "fEncumbranceStrMult", "fEndFatigueMult", "fFallAcroBase", "fFallAcroMult", "fFallDamageDistanceMin", "fFallDistanceBase", "fFallDistanceMult", "fFatigueAttackBase", "fFatigueAttackMult", "fFatigueBase", "fFatigueBlockBase", "fFatigueBlockMult", "fFatigueJumpBase", "fFatigueJumpMult", "fFatigueMult", "fFatigueReturnBase", "fFatigueReturnMult", "fFatigueRunBase", "fFatigueRunMult", "fFatigueSneakBase", "fFatigueSneakMult", "fFatigueSpellBase", "fFatigueSpellCostMult", "fFatigueSpellMult", "fFatigueSwimRunBase", "fFatigueSwimRunMult", "fFatigueSwimWalkBase", "fFatigueSwimWalkMult", "fFightDispMult", "fFightDistanceMultiplier", "fFightStealing", "fFleeDistance", "fGreetDistanceReset", "fHandtoHandHealthPer", "fHandToHandReach", "fHoldBreathEndMult", "fHoldBreathTime", "fIdleChanceMultiplier", "fIngredientMult", "fInteriorHeadTrackMult", "fJumpAcrobaticsBase", "fJumpAcroMultiplier", "fJumpEncumbranceBase", "fJumpEncumbranceMultiplier", "fJumpMoveBase", "fJumpMoveMult", "fJumpRunMultiplier", "fKnockDownMult", "fLevelMod", "fLevelUpHealthEndMult", "fLightMaxMod", "fLuckMod", "fMagesGuildTravel", "fMagicCreatureCastDelay", "fMagicDetectRefreshRate", "fMagicItemConstantMult", "fMagicItemCostMult", "fMagicItemOnceMult", "fMagicItemPriceMult", "fMagicItemRechargePerSecond", "fMagicItemStrikeMult", "fMagicItemUsedMult", "fMagicStartIconBlink", "fMagicSunBlockedMult", "fMajorSkillBonus", "fMaxFlySpeed", "fMaxHandToHandMult", "fMaxHeadTrackDistance", "fMaxWalkSpeed", "fMaxWalkSpeedCreature", "fMedMaxMod", "fMessageTimePerChar", "fMinFlySpeed", "fMinHandToHandMult", "fMinorSkillBonus", "fMinWalkSpeed", "fMinWalkSpeedCreature", "fMiscSkillBonus", "fNPCbaseMagickaMult", "fNPCHealthBarFade", "fNPCHealthBarTime", "fPCbaseMagickaMult", "fPerDieRollMult", "fPersonalityMod", "fPerTempMult", "fPickLockMult", "fPickPocketMod", "fPotionMinUsefulDuration", "fPotionStrengthMult", "fPotionT1DurMult", "fPotionT1MagMult", "fPotionT4BaseStrengthMult", "fPotionT4EquipStrengthMult", "fProjectileMaxSpeed", "fProjectileMinSpeed", "fProjectileThrownStoreChance", "fRepairAmountMult", "fRepairMult", "fReputationMod", "fRestMagicMult", "fSeriousWoundMult", "fSleepRandMod", "fSleepRestMod", "fSneakBootMult", "fSneakDistanceBase", "fSneakDistanceMultiplier", "fSneakNoViewMult", "fSneakSkillMult", "fSneakSpeedMultiplier", "fSneakUseDelay", "fSneakUseDist", "fSneakViewMult", "fSoulGemMult", "fSpecialSkillBonus", "fSpellMakingValueMult", "fSpellPriceMult", "fSpellValueMult", "fStromWalkMult", "fStromWindSpeed", "fSuffocationDamage", "fSwimHeightScale", "fSwimRunAthleticsMult", "fSwimRunBase", "fSwimWalkAthleticsMult", "fSwimWalkBase", "fSwingBlockBase", "fSwingBlockMult", "fTargetSpellMaxSpeed", "fThrownWeaponMaxSpeed", "fThrownWeaponMinSpeed", "fTrapCostMult", "fTravelMult", "fTravelTimeMult", "fUnarmoredBase1", "fUnarmoredBase2", "fVanityDelay", "fVoiceIdleOdds", "fWaterReflectUpdateAlways", "fWaterReflectUpdateSeldom", "fWeaponDamageMult", "fWeaponFatigueBlockMult", "fWeaponFatigueMult", "fWereWolfAcrobatics", "fWereWolfAgility", "fWereWolfAlchemy", "fWereWolfAlteration", "fWereWolfArmorer", "fWereWolfAthletics", "fWereWolfAxe", "fWereWolfBlock", "fWereWolfBluntWeapon", "fWereWolfConjuration", "fWereWolfDestruction", "fWereWolfEnchant", "fWereWolfEndurance", "fWereWolfFatigue", "fWereWolfHandtoHand", "fWereWolfHealth", "fWereWolfHeavyArmor", "fWereWolfIllusion", "fWereWolfIntellegence", "fWereWolfLightArmor", "fWereWolfLongBlade", "fWereWolfLuck", "fWereWolfMagicka", "fWereWolfMarksman", "fWereWolfMediumArmor", "fWereWolfMerchantile", "fWereWolfMysticism", "fWereWolfPersonality", "fWereWolfRestoration", "fWereWolfRunMult", "fWereWolfSecurity", "fWereWolfShortBlade", "fWereWolfSilverWeaponDamageMult", "fWereWolfSneak", "fWereWolfSpear", "fWereWolfSpeechcraft", "fWereWolfSpeed", "fWereWolfStrength", "fWereWolfUnarmored", "fWereWolfWillPower", "fWortChanceValue", 0 }; static const float gmstFloatsValues[] = { 0.3, // fAIFleeFleeMult 7.0, // fAIFleeHealthMult 3.0, // fAIMagicSpellMult 1.0, // fAIMeleeArmorMult 1.0, // fAIMeleeSummWeaponMult 2.0, // fAIMeleeWeaponMult 5.0, // fAIRangeMagicSpellMult 5.0, // fAIRangeMeleeWeaponMult 2000.0, // fAlarmRadius 1.0, // fAthleticsRunBonus 40.0, // fAudioDefaultMaxDistance 5.0, // fAudioDefaultMinDistance 50.0, // fAudioMaxDistanceMult 20.0, // fAudioMinDistanceMult 60.0, // fAudioVoiceDefaultMaxDistance 10.0, // fAudioVoiceDefaultMinDistance 50.0, // fAutoPCSpellChance 80.0, // fAutoSpellChance 50.0, // fBargainOfferBase -4.0, // fBargainOfferMulti 24.0, // fBarterGoldResetDelay 1.75, // fBaseRunMultiplier 1.25, // fBlockStillBonus 150.0, // fBribe1000Mod 75.0, // fBribe100Mod 35.0, // fBribe10Mod 60.0, // fCombatAngleXY 60.0, // fCombatAngleZ 0.25, // fCombatArmorMinMult -90.0, // fCombatBlockLeftAngle 30.0, // fCombatBlockRightAngle 4.0, // fCombatCriticalStrikeMult 0.1, // fCombatDelayCreature 0.1, // fCombatDelayNPC 128.0, // fCombatDistance 0.3, // fCombatDistanceWerewolfMod 30.0, // fCombatForceSideAngle 0.2, // fCombatInvisoMult 1.5, // fCombatKODamageMult 45.0, // fCombatTorsoSideAngle 0.3, // fCombatTorsoStartPercent 0.8, // fCombatTorsoStopPercent 15.0, // fConstantEffectMult 72.0, // fCorpseClearDelay 72.0, // fCorpseRespawnDelay 0.5, // fCrimeGoldDiscountMult 0.9, // fCrimeGoldTurnInMult 1.0, // fCrimeStealing 0.5, // fDamageStrengthBase 0.1, // fDamageStrengthMult 5.0, // fDifficultyMult 2.5, // fDiseaseXferChance -10.0, // fDispAttacking -1.0, // fDispBargainFailMod 1.0, // fDispBargainSuccessMod 0.0, // fDispCrimeMod -10.0, // fDispDiseaseMod 3.0, // fDispFactionMod 1.0, // fDispFactionRankBase 0.5, // fDispFactionRankMult 1.0, // fDispositionMod 50.0, // fDispPersonalityBase 0.5, // fDispPersonalityMult -25.0, // fDispPickPocketMod 5.0, // fDispRaceMod -0.5, // fDispStealing -5.0, // fDispWeaponDrawn 0.5, // fEffectCostMult 0.1, // fElementalShieldMult 3.0, // fEnchantmentChanceMult 0.5, // fEnchantmentConstantChanceMult 100.0, // fEnchantmentConstantDurationMult 0.1, // fEnchantmentMult 1000.0, // fEnchantmentValueMult 0.3, // fEncumberedMoveEffect 5.0, // fEncumbranceStrMult 0.04, // fEndFatigueMult 0.25, // fFallAcroBase 0.01, // fFallAcroMult 400.0, // fFallDamageDistanceMin 0.0, // fFallDistanceBase 0.07, // fFallDistanceMult 2.0, // fFatigueAttackBase 0.0, // fFatigueAttackMult 1.25, // fFatigueBase 4.0, // fFatigueBlockBase 0.0, // fFatigueBlockMult 5.0, // fFatigueJumpBase 0.0, // fFatigueJumpMult 0.5, // fFatigueMult 2.5, // fFatigueReturnBase 0.02, // fFatigueReturnMult 5.0, // fFatigueRunBase 2.0, // fFatigueRunMult 1.5, // fFatigueSneakBase 1.5, // fFatigueSneakMult 0.0, // fFatigueSpellBase 0.0, // fFatigueSpellCostMult 0.0, // fFatigueSpellMult 7.0, // fFatigueSwimRunBase 0.0, // fFatigueSwimRunMult 2.5, // fFatigueSwimWalkBase 0.0, // fFatigueSwimWalkMult 0.2, // fFightDispMult 0.005, // fFightDistanceMultiplier 50.0, // fFightStealing 3000.0, // fFleeDistance 512.0, // fGreetDistanceReset 0.1, // fHandtoHandHealthPer 1.0, // fHandToHandReach 0.5, // fHoldBreathEndMult 20.0, // fHoldBreathTime 0.75, // fIdleChanceMultiplier 1.0, // fIngredientMult 0.5, // fInteriorHeadTrackMult 128.0, // fJumpAcrobaticsBase 4.0, // fJumpAcroMultiplier 0.5, // fJumpEncumbranceBase 1.0, // fJumpEncumbranceMultiplier 0.5, // fJumpMoveBase 0.5, // fJumpMoveMult 1.0, // fJumpRunMultiplier 0.5, // fKnockDownMult 5.0, // fLevelMod 0.1, // fLevelUpHealthEndMult 0.6, // fLightMaxMod 10.0, // fLuckMod 10.0, // fMagesGuildTravel 1.5, // fMagicCreatureCastDelay 0.0167, // fMagicDetectRefreshRate 1.0, // fMagicItemConstantMult 1.0, // fMagicItemCostMult 1.0, // fMagicItemOnceMult 1.0, // fMagicItemPriceMult 0.05, // fMagicItemRechargePerSecond 1.0, // fMagicItemStrikeMult 1.0, // fMagicItemUsedMult 3.0, // fMagicStartIconBlink 0.5, // fMagicSunBlockedMult 0.75, // fMajorSkillBonus 300.0, // fMaxFlySpeed 0.5, // fMaxHandToHandMult 400.0, // fMaxHeadTrackDistance 200.0, // fMaxWalkSpeed 300.0, // fMaxWalkSpeedCreature 0.9, // fMedMaxMod 0.1, // fMessageTimePerChar 5.0, // fMinFlySpeed 0.1, // fMinHandToHandMult 1.0, // fMinorSkillBonus 100.0, // fMinWalkSpeed 5.0, // fMinWalkSpeedCreature 1.25, // fMiscSkillBonus 2.0, // fNPCbaseMagickaMult 0.5, // fNPCHealthBarFade 3.0, // fNPCHealthBarTime 1.0, // fPCbaseMagickaMult 0.3, // fPerDieRollMult 5.0, // fPersonalityMod 1.0, // fPerTempMult -1.0, // fPickLockMult 0.3, // fPickPocketMod 20.0, // fPotionMinUsefulDuration 0.5, // fPotionStrengthMult 0.5, // fPotionT1DurMult 1.5, // fPotionT1MagMult 20.0, // fPotionT4BaseStrengthMult 12.0, // fPotionT4EquipStrengthMult 3000.0, // fProjectileMaxSpeed 400.0, // fProjectileMinSpeed 25.0, // fProjectileThrownStoreChance 3.0, // fRepairAmountMult 1.0, // fRepairMult 1.0, // fReputationMod 0.15, // fRestMagicMult 0.0, // fSeriousWoundMult 0.25, // fSleepRandMod 0.3, // fSleepRestMod -1.0, // fSneakBootMult 0.5, // fSneakDistanceBase 0.002, // fSneakDistanceMultiplier 0.5, // fSneakNoViewMult 1.0, // fSneakSkillMult 0.75, // fSneakSpeedMultiplier 1.0, // fSneakUseDelay 500.0, // fSneakUseDist 1.5, // fSneakViewMult 3.0, // fSoulGemMult 0.8, // fSpecialSkillBonus 7.0, // fSpellMakingValueMult 2.0, // fSpellPriceMult 10.0, // fSpellValueMult 0.25, // fStromWalkMult 0.7, // fStromWindSpeed 3.0, // fSuffocationDamage 0.9, // fSwimHeightScale 0.1, // fSwimRunAthleticsMult 0.5, // fSwimRunBase 0.02, // fSwimWalkAthleticsMult 0.5, // fSwimWalkBase 1.0, // fSwingBlockBase 1.0, // fSwingBlockMult 1000.0, // fTargetSpellMaxSpeed 1000.0, // fThrownWeaponMaxSpeed 300.0, // fThrownWeaponMinSpeed 0.0, // fTrapCostMult 4000.0, // fTravelMult 16000.0,// fTravelTimeMult 0.1, // fUnarmoredBase1 0.065, // fUnarmoredBase2 30.0, // fVanityDelay 10.0, // fVoiceIdleOdds 0.0, // fWaterReflectUpdateAlways 10.0, // fWaterReflectUpdateSeldom 0.1, // fWeaponDamageMult 1.0, // fWeaponFatigueBlockMult 0.25, // fWeaponFatigueMult 150.0, // fWereWolfAcrobatics 150.0, // fWereWolfAgility 1.0, // fWereWolfAlchemy 1.0, // fWereWolfAlteration 1.0, // fWereWolfArmorer 150.0, // fWereWolfAthletics 1.0, // fWereWolfAxe 1.0, // fWereWolfBlock 1.0, // fWereWolfBluntWeapon 1.0, // fWereWolfConjuration 1.0, // fWereWolfDestruction 1.0, // fWereWolfEnchant 150.0, // fWereWolfEndurance 400.0, // fWereWolfFatigue 100.0, // fWereWolfHandtoHand 2.0, // fWereWolfHealth 1.0, // fWereWolfHeavyArmor 1.0, // fWereWolfIllusion 1.0, // fWereWolfIntellegence 1.0, // fWereWolfLightArmor 1.0, // fWereWolfLongBlade 1.0, // fWereWolfLuck 100.0, // fWereWolfMagicka 1.0, // fWereWolfMarksman 1.0, // fWereWolfMediumArmor 1.0, // fWereWolfMerchantile 1.0, // fWereWolfMysticism 1.0, // fWereWolfPersonality 1.0, // fWereWolfRestoration 1.5, // fWereWolfRunMult 1.0, // fWereWolfSecurity 1.0, // fWereWolfShortBlade 1.5, // fWereWolfSilverWeaponDamageMult 1.0, // fWereWolfSneak 1.0, // fWereWolfSpear 1.0, // fWereWolfSpeechcraft 150.0, // fWereWolfSpeed 150.0, // fWereWolfStrength 100.0, // fWereWolfUnarmored 1.0, // fWereWolfWillPower 15.0, // fWortChanceValue }; static const char *gmstIntegers[] = { "i1stPersonSneakDelta", "iAlarmAttack", "iAlarmKilling", "iAlarmPickPocket", "iAlarmStealing", "iAlarmTresspass", "iAlchemyMod", "iAutoPCSpellMax", "iAutoRepFacMod", "iAutoRepLevMod", "iAutoSpellAlterationMax", "iAutoSpellAttSkillMin", "iAutoSpellConjurationMax", "iAutoSpellDestructionMax", "iAutoSpellIllusionMax", "iAutoSpellMysticismMax", "iAutoSpellRestorationMax", "iAutoSpellTimesCanCast", "iBarterFailDisposition", "iBarterSuccessDisposition", "iBaseArmorSkill", "iBlockMaxChance", "iBlockMinChance", "iBootsWeight", "iCrimeAttack", "iCrimeKilling", "iCrimePickPocket", "iCrimeThreshold", "iCrimeThresholdMultiplier", "iCrimeTresspass", "iCuirassWeight", "iDaysinPrisonMod", "iDispAttackMod", "iDispKilling", "iDispTresspass", "iFightAlarmMult", "iFightAttack", "iFightAttacking", "iFightDistanceBase", "iFightKilling", "iFightPickpocket", "iFightTrespass", "iFlee", "iGauntletWeight", "iGreavesWeight", "iGreetDistanceMultiplier", "iGreetDuration", "iHelmWeight", "iKnockDownOddsBase", "iKnockDownOddsMult", "iLevelUp01Mult", "iLevelUp02Mult", "iLevelUp03Mult", "iLevelUp04Mult", "iLevelUp05Mult", "iLevelUp06Mult", "iLevelUp07Mult", "iLevelUp08Mult", "iLevelUp09Mult", "iLevelUp10Mult", "iLevelupMajorMult", "iLevelupMajorMultAttribute", "iLevelupMinorMult", "iLevelupMinorMultAttribute", "iLevelupMiscMultAttriubte", "iLevelupSpecialization", "iLevelupTotal", "iMagicItemChargeConst", "iMagicItemChargeOnce", "iMagicItemChargeStrike", "iMagicItemChargeUse", "iMaxActivateDist", "iMaxInfoDist", "iMonthsToRespawn", "iNumberCreatures", "iPauldronWeight", "iPerMinChance", "iPerMinChange", "iPickMaxChance", "iPickMinChance", "iShieldWeight", "iSoulAmountForConstantEffect", "iTrainingMod", "iVoiceAttackOdds", "iVoiceHitOdds", "iWereWolfBounty", "iWereWolfFightMod", "iWereWolfFleeMod", "iWereWolfLevelToAttack", 0 }; static const int gmstIntegersValues[] = { 10, // i1stPersonSneakDelta 50, // iAlarmAttack 90, // iAlarmKilling 20, // iAlarmPickPocket 1, // iAlarmStealing 5, // iAlarmTresspass 2, // iAlchemyMod 100, // iAutoPCSpellMax 2, // iAutoRepFacMod 0, // iAutoRepLevMod 5, // iAutoSpellAlterationMax 70, // iAutoSpellAttSkillMin 2, // iAutoSpellConjurationMax 5, // iAutoSpellDestructionMax 5, // iAutoSpellIllusionMax 5, // iAutoSpellMysticismMax 5, // iAutoSpellRestorationMax 3, // iAutoSpellTimesCanCast -1, // iBarterFailDisposition 1, // iBarterSuccessDisposition 30, // iBaseArmorSkill 50, // iBlockMaxChance 10, // iBlockMinChance 20, // iBootsWeight 40, // iCrimeAttack 1000, // iCrimeKilling 25, // iCrimePickPocket 1000, // iCrimeThreshold 10, // iCrimeThresholdMultiplier 5, // iCrimeTresspass 30, // iCuirassWeight 100, // iDaysinPrisonMod -50, // iDispAttackMod -50, // iDispKilling -20, // iDispTresspass 1, // iFightAlarmMult 100, // iFightAttack 50, // iFightAttacking 20, // iFightDistanceBase 50, // iFightKilling 25, // iFightPickpocket 25, // iFightTrespass 0, // iFlee 5, // iGauntletWeight 15, // iGreavesWeight 6, // iGreetDistanceMultiplier 4, // iGreetDuration 5, // iHelmWeight 50, // iKnockDownOddsBase 50, // iKnockDownOddsMult 2, // iLevelUp01Mult 2, // iLevelUp02Mult 2, // iLevelUp03Mult 2, // iLevelUp04Mult 3, // iLevelUp05Mult 3, // iLevelUp06Mult 3, // iLevelUp07Mult 4, // iLevelUp08Mult 4, // iLevelUp09Mult 5, // iLevelUp10Mult 1, // iLevelupMajorMult 1, // iLevelupMajorMultAttribute 1, // iLevelupMinorMult 1, // iLevelupMinorMultAttribute 1, // iLevelupMiscMultAttriubte 1, // iLevelupSpecialization 10, // iLevelupTotal 10, // iMagicItemChargeConst 1, // iMagicItemChargeOnce 10, // iMagicItemChargeStrike 5, // iMagicItemChargeUse 192, // iMaxActivateDist 192, // iMaxInfoDist 4, // iMonthsToRespawn 1, // iNumberCreatures 10, // iPauldronWeight 5, // iPerMinChance 10, // iPerMinChange 75, // iPickMaxChance 5, // iPickMinChance 15, // iShieldWeight 400, // iSoulAmountForConstantEffect 10, // iTrainingMod 10, // iVoiceAttackOdds 30, // iVoiceHitOdds 10000, // iWereWolfBounty 100, // iWereWolfFightMod 100, // iWereWolfFleeMod 20, // iWereWolfLevelToAttack }; static const char *gmstStrings[] = { "s3dAudio", "s3dHardware", "s3dSoftware", "sAbsorb", "sAcrobat", "sActivate", "sActivateXbox", "sActorInCombat", "sAdmire", "sAdmireFail", "sAdmireSuccess", "sAgent", "sAgiDesc", "sAIDistance", "sAlembic", "sAllTab", "sAlways", "sAlways_Run", "sand", "sApparatus", "sApparelTab", "sArcher", "sArea", "sAreaDes", "sArmor", "sArmorRating", "sAsk", "sAssassin", "sAt", "sAttack", "sAttributeAgility", "sAttributeEndurance", "sAttributeIntelligence", "sAttributeListTitle", "sAttributeLuck", "sAttributePersonality", "sAttributesMenu1", "sAttributeSpeed", "sAttributeStrength", "sAttributeWillpower", "sAudio", "sAuto_Run", "sBack", "sBackspace", "sBackXbox", "sBarbarian", "sBard", "sBarter", "sBarterDialog1", "sBarterDialog10", "sBarterDialog11", "sBarterDialog12", "sBarterDialog2", "sBarterDialog3", "sBarterDialog4", "sBarterDialog5", "sBarterDialog6", "sBarterDialog7", "sBarterDialog8", "sBarterDialog9", "sBattlemage", "sBestAttack", "sBirthSign", "sBirthsignmenu1", "sBirthsignmenu2", "sBlocks", "sBonusSkillTitle", "sBookPageOne", "sBookPageTwo", "sBookSkillMessage", "sBounty", "sBreath", "sBribe 10 Gold", "sBribe 100 Gold", "sBribe 1000 Gold", "sBribeFail", "sBribeSuccess", "sBuy", "sBye", "sCalcinator", "sCancel", "sCantEquipWeapWarning", "sCastCost", "sCaughtStealingMessage", "sCenter", "sChangedMastersMsg", "sCharges", "sChooseClassMenu1", "sChooseClassMenu2", "sChooseClassMenu3", "sChooseClassMenu4", "sChop", "sClass", "sClassChoiceMenu1", "sClassChoiceMenu2", "sClassChoiceMenu3", "sClose", "sCompanionShare", "sCompanionWarningButtonOne", "sCompanionWarningButtonTwo", "sCompanionWarningMessage", "sCondition", "sConsoleTitle", "sContainer", "sContentsMessage1", "sContentsMessage2", "sContentsMessage3", "sControlerVibration", "sControls", "sControlsMenu1", "sControlsMenu2", "sControlsMenu3", "sControlsMenu4", "sControlsMenu5", "sControlsMenu6", "sCostChance", "sCostCharge", "sCreate", "sCreateClassMenu1", "sCreateClassMenu2", "sCreateClassMenu3", "sCreateClassMenuHelp1", "sCreateClassMenuHelp2", "sCreateClassMenuWarning", "sCreatedEffects", "sCrimeHelp", "sCrimeMessage", "sCrouch_Sneak", "sCrouchXbox", "sCrusader", "sCursorOff", "sCustom", "sCustomClassName", "sDamage", "sDark_Gamma", "sDay", "sDefaultCellname", "sDelete", "sDeleteGame", "sDeleteNote", "sDeleteSpell", "sDeleteSpellError", "sDetail_Level", "sDialogMenu1", "sDialogText1Xbox", "sDialogText2Xbox", "sDialogText3Xbox", "sDifficulty", "sDisposeCorpseFail", "sDisposeofCorpse", "sDone", "sDoYouWantTo", "sDrain", "sDrop", "sDuration", "sDurationDes", "sEasy", "sEditNote", "sEffectAbsorbAttribute", "sEffectAbsorbFatigue", "sEffectAbsorbHealth", "sEffectAbsorbSkill", "sEffectAbsorbSpellPoints", "sEffectAlmsiviIntervention", "sEffectBlind", "sEffectBoundBattleAxe", "sEffectBoundBoots", "sEffectBoundCuirass", "sEffectBoundDagger", "sEffectBoundGloves", "sEffectBoundHelm", "sEffectBoundLongbow", "sEffectBoundLongsword", "sEffectBoundMace", "sEffectBoundShield", "sEffectBoundSpear", "sEffectBurden", "sEffectCalmCreature", "sEffectCalmHumanoid", "sEffectChameleon", "sEffectCharm", "sEffectCommandCreatures", "sEffectCommandHumanoids", "sEffectCorpus", "sEffectCureBlightDisease", "sEffectCureCommonDisease", "sEffectCureCorprusDisease", "sEffectCureParalyzation", "sEffectCurePoison", "sEffectDamageAttribute", "sEffectDamageFatigue", "sEffectDamageHealth", "sEffectDamageMagicka", "sEffectDamageSkill", "sEffectDemoralizeCreature", "sEffectDemoralizeHumanoid", "sEffectDetectAnimal", "sEffectDetectEnchantment", "sEffectDetectKey", "sEffectDisintegrateArmor", "sEffectDisintegrateWeapon", "sEffectDispel", "sEffectDivineIntervention", "sEffectDrainAttribute", "sEffectDrainFatigue", "sEffectDrainHealth", "sEffectDrainSkill", "sEffectDrainSpellpoints", "sEffectExtraSpell", "sEffectFeather", "sEffectFireDamage", "sEffectFireShield", "sEffectFortifyAttackBonus", "sEffectFortifyAttribute", "sEffectFortifyFatigue", "sEffectFortifyHealth", "sEffectFortifyMagickaMultiplier", "sEffectFortifySkill", "sEffectFortifySpellpoints", "sEffectFrenzyCreature", "sEffectFrenzyHumanoid", "sEffectFrostDamage", "sEffectFrostShield", "sEffectInvisibility", "sEffectJump", "sEffectLevitate", "sEffectLight", "sEffectLightningShield", "sEffectLock", "sEffectMark", "sEffectNightEye", "sEffectOpen", "sEffectParalyze", "sEffectPoison", "sEffectRallyCreature", "sEffectRallyHumanoid", "sEffectRecall", "sEffectReflect", "sEffectRemoveCurse", "sEffectResistBlightDisease", "sEffectResistCommonDisease", "sEffectResistCorprusDisease", "sEffectResistFire", "sEffectResistFrost", "sEffectResistMagicka", "sEffectResistNormalWeapons", "sEffectResistParalysis", "sEffectResistPoison", "sEffectResistShock", "sEffectRestoreAttribute", "sEffectRestoreFatigue", "sEffectRestoreHealth", "sEffectRestoreSkill", "sEffectRestoreSpellPoints", "sEffects", "sEffectSanctuary", "sEffectShield", "sEffectShockDamage", "sEffectSilence", "sEffectSlowFall", "sEffectSoultrap", "sEffectSound", "sEffectSpellAbsorption", "sEffectStuntedMagicka", "sEffectSummonAncestralGhost", "sEffectSummonBonelord", "sEffectSummonCenturionSphere", "sEffectSummonClannfear", "sEffectSummonCreature01", "sEffectSummonCreature02", "sEffectSummonCreature03", "sEffectSummonCreature04", "sEffectSummonCreature05", "sEffectSummonDaedroth", "sEffectSummonDremora", "sEffectSummonFabricant", "sEffectSummonFlameAtronach", "sEffectSummonFrostAtronach", "sEffectSummonGoldensaint", "sEffectSummonGreaterBonewalker", "sEffectSummonHunger", "sEffectSummonLeastBonewalker", "sEffectSummonScamp", "sEffectSummonSkeletalMinion", "sEffectSummonStormAtronach", "sEffectSummonWingedTwilight", "sEffectSunDamage", "sEffectSwiftSwim", "sEffectTelekinesis", "sEffectTurnUndead", "sEffectVampirism", "sEffectWaterBreathing", "sEffectWaterWalking", "sEffectWeaknessToBlightDisease", "sEffectWeaknessToCommonDisease", "sEffectWeaknessToCorprusDisease", "sEffectWeaknessToFire", "sEffectWeaknessToFrost", "sEffectWeaknessToMagicka", "sEffectWeaknessToNormalWeapons", "sEffectWeaknessToPoison", "sEffectWeaknessToShock", "sEnableJoystick", "sEnchanting", "sEnchantItems", "sEnchantmentHelp1", "sEnchantmentHelp10", "sEnchantmentHelp2", "sEnchantmentHelp3", "sEnchantmentHelp4", "sEnchantmentHelp5", "sEnchantmentHelp6", "sEnchantmentHelp7", "sEnchantmentHelp8", "sEnchantmentHelp9", "sEnchantmentMenu1", "sEnchantmentMenu10", "sEnchantmentMenu11", "sEnchantmentMenu12", "sEnchantmentMenu2", "sEnchantmentMenu3", "sEnchantmentMenu4", "sEnchantmentMenu5", "sEnchantmentMenu6", "sEnchantmentMenu7", "sEnchantmentMenu8", "sEnchantmentMenu9", "sEncumbrance", "sEndDesc", "sEquip", "sExitGame", "sExpelled", "sExpelledMessage", "sFace", "sFaction", "sFar", "sFast", "sFatDesc", "sFatigue", "sFavoriteSkills", "sfeet", "sFileSize", "sfootarea", "sFootsteps", "sfor", "sFortify", "sForward", "sForwardXbox", "sFull", "sGame", "sGameWithoutLauncherXbox", "sGamma_Correction", "sGeneralMastPlugMismatchMsg", "sGold", "sGoodbye", "sGoverningAttribute", "sgp", "sHair", "sHard", "sHeal", "sHealer", "sHealth", "sHealthDesc", "sHealthPerHourOfRest", "sHealthPerLevel", "sHeavy", "sHigh", "sin", "sInfo", "sInfoRefusal", "sIngredients", "sInPrisonTitle", "sInputMenu1", "sIntDesc", "sIntimidate", "sIntimidateFail", "sIntimidateSuccess", "sInvalidSaveGameMsg", "sInvalidSaveGameMsgXBOX", "sInventory", "sInventoryMenu1", "sInventoryMessage1", "sInventoryMessage2", "sInventoryMessage3", "sInventoryMessage4", "sInventoryMessage5", "sInventorySelectNoIngredients", "sInventorySelectNoItems", "sInventorySelectNoSoul", "sItem", "sItemCastConstant", "sItemCastOnce", "sItemCastWhenStrikes", "sItemCastWhenUsed", "sItemName", "sJournal", "sJournalCmd", "sJournalEntry", "sJournalXbox", "sJoystickHatShort", "sJoystickNotFound", "sJoystickShort", "sJump", "sJumpXbox", "sKeyName_00", "sKeyName_01", "sKeyName_02", "sKeyName_03", "sKeyName_04", "sKeyName_05", "sKeyName_06", "sKeyName_07", "sKeyName_08", "sKeyName_09", "sKeyName_0A", "sKeyName_0B", "sKeyName_0C", "sKeyName_0D", "sKeyName_0E", "sKeyName_0F", "sKeyName_10", "sKeyName_11", "sKeyName_12", "sKeyName_13", "sKeyName_14", "sKeyName_15", "sKeyName_16", "sKeyName_17", "sKeyName_18", "sKeyName_19", "sKeyName_1A", "sKeyName_1B", "sKeyName_1C", "sKeyName_1D", "sKeyName_1E", "sKeyName_1F", "sKeyName_20", "sKeyName_21", "sKeyName_22", "sKeyName_23", "sKeyName_24", "sKeyName_25", "sKeyName_26", "sKeyName_27", "sKeyName_28", "sKeyName_29", "sKeyName_2A", "sKeyName_2B", "sKeyName_2C", "sKeyName_2D", "sKeyName_2E", "sKeyName_2F", "sKeyName_30", "sKeyName_31", "sKeyName_32", "sKeyName_33", "sKeyName_34", "sKeyName_35", "sKeyName_36", "sKeyName_37", "sKeyName_38", "sKeyName_39", "sKeyName_3A", "sKeyName_3B", "sKeyName_3C", "sKeyName_3D", "sKeyName_3E", "sKeyName_3F", "sKeyName_40", "sKeyName_41", "sKeyName_42", "sKeyName_43", "sKeyName_44", "sKeyName_45", "sKeyName_46", "sKeyName_47", "sKeyName_48", "sKeyName_49", "sKeyName_4A", "sKeyName_4B", "sKeyName_4C", "sKeyName_4D", "sKeyName_4E", "sKeyName_4F", "sKeyName_50", "sKeyName_51", "sKeyName_52", "sKeyName_53", "sKeyName_54", "sKeyName_55", "sKeyName_56", "sKeyName_57", "sKeyName_58", "sKeyName_59", "sKeyName_5A", "sKeyName_5B", "sKeyName_5C", "sKeyName_5D", "sKeyName_5E", "sKeyName_5F", "sKeyName_60", "sKeyName_61", "sKeyName_62", "sKeyName_63", "sKeyName_64", "sKeyName_65", "sKeyName_66", "sKeyName_67", "sKeyName_68", "sKeyName_69", "sKeyName_6A", "sKeyName_6B", "sKeyName_6C", "sKeyName_6D", "sKeyName_6E", "sKeyName_6F", "sKeyName_70", "sKeyName_71", "sKeyName_72", "sKeyName_73", "sKeyName_74", "sKeyName_75", "sKeyName_76", "sKeyName_77", "sKeyName_78", "sKeyName_79", "sKeyName_7A", "sKeyName_7B", "sKeyName_7C", "sKeyName_7D", "sKeyName_7E", "sKeyName_7F", "sKeyName_80", "sKeyName_81", "sKeyName_82", "sKeyName_83", "sKeyName_84", "sKeyName_85", "sKeyName_86", "sKeyName_87", "sKeyName_88", "sKeyName_89", "sKeyName_8A", "sKeyName_8B", "sKeyName_8C", "sKeyName_8D", "sKeyName_8E", "sKeyName_8F", "sKeyName_90", "sKeyName_91", "sKeyName_92", "sKeyName_93", "sKeyName_94", "sKeyName_95", "sKeyName_96", "sKeyName_97", "sKeyName_98", "sKeyName_99", "sKeyName_9A", "sKeyName_9B", "sKeyName_9C", "sKeyName_9D", "sKeyName_9E", "sKeyName_9F", "sKeyName_A0", "sKeyName_A1", "sKeyName_A2", "sKeyName_A3", "sKeyName_A4", "sKeyName_A5", "sKeyName_A6", "sKeyName_A7", "sKeyName_A8", "sKeyName_A9", "sKeyName_AA", "sKeyName_AB", "sKeyName_AC", "sKeyName_AD", "sKeyName_AE", "sKeyName_AF", "sKeyName_B0", "sKeyName_B1", "sKeyName_B2", "sKeyName_B3", "sKeyName_B4", "sKeyName_B5", "sKeyName_B6", "sKeyName_B7", "sKeyName_B8", "sKeyName_B9", "sKeyName_BA", "sKeyName_BB", "sKeyName_BC", "sKeyName_BD", "sKeyName_BE", "sKeyName_BF", "sKeyName_C0", "sKeyName_C1", "sKeyName_C2", "sKeyName_C3", "sKeyName_C4", "sKeyName_C5", "sKeyName_C6", "sKeyName_C7", "sKeyName_C8", "sKeyName_C9", "sKeyName_CA", "sKeyName_CB", "sKeyName_CC", "sKeyName_CD", "sKeyName_CE", "sKeyName_CF", "sKeyName_D0", "sKeyName_D1", "sKeyName_D2", "sKeyName_D3", "sKeyName_D4", "sKeyName_D5", "sKeyName_D6", "sKeyName_D7", "sKeyName_D8", "sKeyName_D9", "sKeyName_DA", "sKeyName_DB", "sKeyName_DC", "sKeyName_DD", "sKeyName_DE", "sKeyName_DF", "sKeyName_E0", "sKeyName_E1", "sKeyName_E2", "sKeyName_E3", "sKeyName_E4", "sKeyName_E5", "sKeyName_E6", "sKeyName_E7", "sKeyName_E8", "sKeyName_E9", "sKeyName_EA", "sKeyName_EB", "sKeyName_EC", "sKeyName_ED", "sKeyName_EE", "sKeyName_EF", "sKeyName_F0", "sKeyName_F1", "sKeyName_F2", "sKeyName_F3", "sKeyName_F4", "sKeyName_F5", "sKeyName_F6", "sKeyName_F7", "sKeyName_F8", "sKeyName_F9", "sKeyName_FA", "sKeyName_FB", "sKeyName_FC", "sKeyName_FD", "sKeyName_FE", "sKeyName_FF", "sKeyUsed", "sKilledEssential", "sKnight", "sLeft", "sLess", "sLevel", "sLevelProgress", "sLevels", "sLevelUp", "sLevelUpMenu1", "sLevelUpMenu2", "sLevelUpMenu3", "sLevelUpMenu4", "sLevelUpMsg", "sLevitateDisabled", "sLight", "sLight_Gamma", "sLoadFailedMessage", "sLoadGame", "sLoadingErrorsMsg", "sLoadingMessage1", "sLoadingMessage14", "sLoadingMessage15", "sLoadingMessage2", "sLoadingMessage3", "sLoadingMessage4", "sLoadingMessage5", "sLoadingMessage9", "sLoadLastSaveMsg", "sLocal", "sLockFail", "sLockImpossible", "sLockLevel", "sLockSuccess", "sLookDownXbox", "sLookUpXbox", "sLow", "sLucDesc", "sMagDesc", "sMage", "sMagic", "sMagicAncestralGhostID", "sMagicBonelordID", "sMagicBoundBattleAxeID", "sMagicBoundBootsID", "sMagicBoundCuirassID", "sMagicBoundDaggerID", "sMagicBoundHelmID", "sMagicBoundLeftGauntletID", "sMagicBoundLongbowID", "sMagicBoundLongswordID", "sMagicBoundMaceID", "sMagicBoundRightGauntletID", "sMagicBoundShieldID", "sMagicBoundSpearID", "sMagicCannotRecast", "sMagicCenturionSphereID", "sMagicClannfearID", "sMagicContractDisease", "sMagicCorprusWorsens", "sMagicCreature01ID", "sMagicCreature02ID", "sMagicCreature03ID", "sMagicCreature04ID", "sMagicCreature05ID", "sMagicDaedrothID", "sMagicDremoraID", "sMagicEffects", "sMagicFabricantID", "sMagicFlameAtronachID", "sMagicFrostAtronachID", "sMagicGoldenSaintID", "sMagicGreaterBonewalkerID", "sMagicHungerID", "sMagicInsufficientCharge", "sMagicInsufficientSP", "sMagicInvalidEffect", "sMagicInvalidTarget", "sMagicItem", "sMagicLeastBonewalkerID", "sMagicLockSuccess", "sMagicMenu", "sMagicOpenSuccess", "sMagicPCResisted", "sMagicScampID", "sMagicSelectTitle", "sMagicSkeletalMinionID", "sMagicSkillFail", "sMagicStormAtronachID", "sMagicTab", "sMagicTargetResisted", "sMagicTargetResistsWeapons", "sMagicWingedTwilightID", "sMagnitude", "sMagnitudeDes", "sMake", "sMap", "sMaster", "sMastPlugMismatchMsg", "sMaximumSaveGameMessage", "sMaxSale", "sMedium", "sMenu_Help_Delay", "sMenu_Mode", "sMenuModeXbox", "sMenuNextXbox", "sMenuPrevXbox", "sMenus", "sMessage1", "sMessage2", "sMessage3", "sMessage4", "sMessage5", "sMessageQuestionAnswer1", "sMessageQuestionAnswer2", "sMessageQuestionAnswer3", "sMiscTab", "sMissingMastersMsg", "sMonk", "sMonthEveningstar", "sMonthFirstseed", "sMonthFrostfall", "sMonthHeartfire", "sMonthLastseed", "sMonthMidyear", "sMonthMorningstar", "sMonthRainshand", "sMonthSecondseed", "sMonthSunsdawn", "sMonthSunsdusk", "sMonthSunsheight", "sMore", "sMortar", "sMouse", "sMouseFlip", "sMouseWheelDownShort", "sMouseWheelUpShort", "sMove", "sMoveDownXbox", "sMoveUpXbox", "sMusic", "sName", "sNameTitle", "sNear", "sNeedOneSkill", "sNeedTwoSkills", "sNewGame", "sNext", "sNextRank", "sNextSpell", "sNextSpellXbox", "sNextWeapon", "sNextWeaponXbox", "sNightblade", "sNo", "sNoName", "sNone", "sNotifyMessage1", "sNotifyMessage10", "sNotifyMessage11", "sNotifyMessage12", "sNotifyMessage13", "sNotifyMessage14", "sNotifyMessage15", "sNotifyMessage16", "sNotifyMessage16_a", "sNotifyMessage17", "sNotifyMessage18", "sNotifyMessage19", "sNotifyMessage2", "sNotifyMessage20", "sNotifyMessage21", "sNotifyMessage22", "sNotifyMessage23", "sNotifyMessage24", "sNotifyMessage25", "sNotifyMessage26", "sNotifyMessage27", "sNotifyMessage28", "sNotifyMessage29", "sNotifyMessage3", "sNotifyMessage30", "sNotifyMessage31", "sNotifyMessage32", "sNotifyMessage33", "sNotifyMessage34", "sNotifyMessage35", "sNotifyMessage36", "sNotifyMessage37", "sNotifyMessage38", "sNotifyMessage39", "sNotifyMessage4", "sNotifyMessage40", "sNotifyMessage41", "sNotifyMessage42", "sNotifyMessage43", "sNotifyMessage44", "sNotifyMessage45", "sNotifyMessage46", "sNotifyMessage47", "sNotifyMessage48", "sNotifyMessage49", "sNotifyMessage4XBOX", "sNotifyMessage5", "sNotifyMessage50", "sNotifyMessage51", "sNotifyMessage52", "sNotifyMessage53", "sNotifyMessage54", "sNotifyMessage55", "sNotifyMessage56", "sNotifyMessage57", "sNotifyMessage58", "sNotifyMessage59", "sNotifyMessage6", "sNotifyMessage60", "sNotifyMessage61", "sNotifyMessage62", "sNotifyMessage63", "sNotifyMessage64", "sNotifyMessage65", "sNotifyMessage66", "sNotifyMessage67", "sNotifyMessage6a", "sNotifyMessage7", "sNotifyMessage8", "sNotifyMessage9", "sOff", "sOffer", "sOfferMenuTitle", "sOK", "sOn", "sOnce", "sOneHanded", "sOnetypeEffectMessage", "sonword", "sOptions", "sOptionsMenuXbox", "spercent", "sPerDesc", "sPersuasion", "sPersuasionMenuTitle", "sPickUp", "sPilgrim", "spoint", "spoints", "sPotionSuccess", "sPowerAlreadyUsed", "sPowers", "sPreferences", "sPrefs", "sPrev", "sPrevSpell", "sPrevSpellXbox", "sPrevWeapon", "sPrevWeaponXbox", "sProfitValue", "sQuality", "sQuanityMenuMessage01", "sQuanityMenuMessage02", "sQuestionDeleteSpell", "sQuestionMark", "sQuick0Xbox", "sQuick10Cmd", "sQuick1Cmd", "sQuick2Cmd", "sQuick3Cmd", "sQuick4Cmd", "sQuick4Xbox", "sQuick5Cmd", "sQuick5Xbox", "sQuick6Cmd", "sQuick6Xbox", "sQuick7Cmd", "sQuick7Xbox", "sQuick8Cmd", "sQuick8Xbox", "sQuick9Cmd", "sQuick9Xbox", "sQuick_Save", "sQuickLoadCmd", "sQuickLoadXbox", "sQuickMenu", "sQuickMenu1", "sQuickMenu2", "sQuickMenu3", "sQuickMenu4", "sQuickMenu5", "sQuickMenu6", "sQuickMenuInstruc", "sQuickMenuTitle", "sQuickSaveCmd", "sQuickSaveXbox", "sRace", "sRaceMenu1", "sRaceMenu2", "sRaceMenu3", "sRaceMenu4", "sRaceMenu5", "sRaceMenu6", "sRaceMenu7", "sRacialTraits", "sRange", "sRangeDes", "sRangeSelf", "sRangeTarget", "sRangeTouch", "sReady_Magic", "sReady_Weapon", "sReadyItemXbox", "sReadyMagicXbox", "sRechargeEnchantment", "sRender_Distance", "sRepair", "sRepairFailed", "sRepairServiceTitle", "sRepairSuccess", "sReputation", "sResChangeWarning", "sRest", "sRestIllegal", "sRestKey", "sRestMenu1", "sRestMenu2", "sRestMenu3", "sRestMenu4", "sRestMenuXbox", "sRestore", "sRetort", "sReturnToGame", "sRight", "sRogue", "sRun", "sRunXbox", "sSave", "sSaveGame", "sSaveGameDenied", "sSaveGameFailed", "sSaveGameNoMemory", "sSaveGameTooBig", "sSaveMenu1", "sSaveMenuHelp01", "sSaveMenuHelp02", "sSaveMenuHelp03", "sSaveMenuHelp04", "sSaveMenuHelp05", "sSaveMenuHelp06", "sSchool", "sSchoolAlteration", "sSchoolConjuration", "sSchoolDestruction", "sSchoolIllusion", "sSchoolMysticism", "sSchoolRestoration", "sScout", "sScrolldown", "sScrollup", "ssecond", "sseconds", "sSeldom", "sSelect", "sSell", "sSellerGold", "sService", "sServiceRefusal", "sServiceRepairTitle", "sServiceSpellsTitle", "sServiceTrainingTitle", "sServiceTrainingWords", "sServiceTravelTitle", "sSetValueMessage01", "sSex", "sShadows", "sShadowText", "sShift", "sSkill", "sSkillAcrobatics", "sSkillAlchemy", "sSkillAlteration", "sSkillArmorer", "sSkillAthletics", "sSkillAxe", "sSkillBlock", "sSkillBluntweapon", "sSkillClassMajor", "sSkillClassMinor", "sSkillClassMisc", "sSkillConjuration", "sSkillDestruction", "sSkillEnchant", "sSkillHandtohand", "sSkillHeavyarmor", "sSkillIllusion", "sSkillLightarmor", "sSkillLongblade", "sSkillMarksman", "sSkillMaxReached", "sSkillMediumarmor", "sSkillMercantile", "sSkillMysticism", "sSkillProgress", "sSkillRestoration", "sSkillSecurity", "sSkillShortblade", "sSkillsMenu1", "sSkillsMenuReputationHelp", "sSkillSneak", "sSkillSpear", "sSkillSpeechcraft", "sSkillUnarmored", "sSlash", "sSleepInterrupt", "sSlideLeftXbox", "sSlideRightXbox", "sSlow", "sSorceror", "sSoulGem", "sSoulGemsWithSouls", "sSoultrapSuccess", "sSpace", "sSpdDesc", "sSpecialization", "sSpecializationCombat", "sSpecializationMagic", "sSpecializationMenu1", "sSpecializationStealth", "sSpellmaking", "sSpellmakingHelp1", "sSpellmakingHelp2", "sSpellmakingHelp3", "sSpellmakingHelp4", "sSpellmakingHelp5", "sSpellmakingHelp6", "sSpellmakingMenu1", "sSpellmakingMenuTitle", "sSpells", "sSpellServiceTitle", "sSpellsword", "sStartCell", "sStartCellError", "sStartError", "sStats", "sStrafe", "sStrDesc", "sStrip", "sSubtitles", "sSystemMenuXbox", "sTake", "sTakeAll", "sTargetCriticalStrike", "sTaunt", "sTauntFail", "sTauntSuccess", "sTeleportDisabled", "sThief", "sThrust", "sTo", "sTogglePOVCmd", "sTogglePOVXbox", "sToggleRunXbox", "sTopics", "sTotalCost", "sTotalSold", "sTraining", "sTrainingServiceTitle", "sTraits", "sTransparency_Menu", "sTrapFail", "sTrapImpossible", "sTrapped", "sTrapSuccess", "sTravel", "sTravelServiceTitle", "sTurn", "sTurnLeftXbox", "sTurnRightXbox", "sTwoHanded", "sType", "sTypeAbility", "sTypeBlightDisease", "sTypeCurse", "sTypeDisease", "sTypePower", "sTypeSpell", "sUnequip", "sUnlocked", "sUntilHealed", "sUse", "sUserDefinedClass", "sUses", "sUseXbox", "sValue", "sVideo", "sVideoWarning", "sVoice", "sWait", "sWarrior", "sWaterReflectUpdate", "sWaterTerrainReflect", "sWeaponTab", "sWeight", "sWerewolfAlarmMessage", "sWerewolfPopup", "sWerewolfRefusal", "sWerewolfRestMessage", "sWilDesc", "sWitchhunter", "sWorld", "sWornTab", "sXStrafe", "sXTimes", "sXTimesINT", "sYes", "sYourGold", 0 }; for (int i=0; gmstFloats[i]; i++) { ESM::GameSetting gmst; gmst.mId = gmstFloats[i]; gmst.mValue.setType (ESM::VT_Float); gmst.mValue.setFloat (gmstFloatsValues[i]); getData().getGmsts().add (gmst); } for (int i=0; gmstIntegers[i]; i++) { ESM::GameSetting gmst; gmst.mId = gmstIntegers[i]; gmst.mValue.setType (ESM::VT_Int); gmst.mValue.setInteger (gmstIntegersValues[i]); getData().getGmsts().add (gmst); } for (int i=0; gmstStrings[i]; i++) { ESM::GameSetting gmst; gmst.mId = gmstStrings[i]; gmst.mValue.setType (ESM::VT_String); gmst.mValue.setString (""); getData().getGmsts().add (gmst); } } void CSMDoc::Document::addOptionalGmsts() { static const char *sFloats[] = { "fCombatDistanceWerewolfMod", "fFleeDistance", "fWereWolfAcrobatics", "fWereWolfAgility", "fWereWolfAlchemy", "fWereWolfAlteration", "fWereWolfArmorer", "fWereWolfAthletics", "fWereWolfAxe", "fWereWolfBlock", "fWereWolfBluntWeapon", "fWereWolfConjuration", "fWereWolfDestruction", "fWereWolfEnchant", "fWereWolfEndurance", "fWereWolfFatigue", "fWereWolfHandtoHand", "fWereWolfHealth", "fWereWolfHeavyArmor", "fWereWolfIllusion", "fWereWolfIntellegence", "fWereWolfLightArmor", "fWereWolfLongBlade", "fWereWolfLuck", "fWereWolfMagicka", "fWereWolfMarksman", "fWereWolfMediumArmor", "fWereWolfMerchantile", "fWereWolfMysticism", "fWereWolfPersonality", "fWereWolfRestoration", "fWereWolfRunMult", "fWereWolfSecurity", "fWereWolfShortBlade", "fWereWolfSilverWeaponDamageMult", "fWereWolfSneak", "fWereWolfSpear", "fWereWolfSpeechcraft", "fWereWolfSpeed", "fWereWolfStrength", "fWereWolfUnarmored", "fWereWolfWillPower", 0 }; static const char *sIntegers[] = { "iWereWolfBounty", "iWereWolfFightMod", "iWereWolfFleeMod", "iWereWolfLevelToAttack", 0 }; static const char *sStrings[] = { "sCompanionShare", "sCompanionWarningButtonOne", "sCompanionWarningButtonTwo", "sCompanionWarningMessage", "sDeleteNote", "sEditNote", "sEffectSummonCreature01", "sEffectSummonCreature02", "sEffectSummonCreature03", "sEffectSummonCreature04", "sEffectSummonCreature05", "sEffectSummonFabricant", "sLevitateDisabled", "sMagicCreature01ID", "sMagicCreature02ID", "sMagicCreature03ID", "sMagicCreature04ID", "sMagicCreature05ID", "sMagicFabricantID", "sMaxSale", "sProfitValue", "sTeleportDisabled", "sWerewolfAlarmMessage", "sWerewolfPopup", "sWerewolfRefusal", "sWerewolfRestMessage", 0 }; for (int i=0; sFloats[i]; ++i) { ESM::GameSetting gmst; gmst.mId = sFloats[i]; gmst.blank(); gmst.mValue.setType (ESM::VT_Float); addOptionalGmst (gmst); } for (int i=0; sIntegers[i]; ++i) { ESM::GameSetting gmst; gmst.mId = sIntegers[i]; gmst.blank(); gmst.mValue.setType (ESM::VT_Int); addOptionalGmst (gmst); } for (int i=0; sStrings[i]; ++i) { ESM::GameSetting gmst; gmst.mId = sStrings[i]; gmst.blank(); gmst.mValue.setType (ESM::VT_String); gmst.mValue.setString (""); addOptionalGmst (gmst); } } void CSMDoc::Document::addOptionalGlobals() { static const char *sGlobals[] = { "DaysPassed", "PCWerewolf", "PCYear", 0 }; for (int i=0; sGlobals[i]; ++i) { ESM::Global global; global.mId = sGlobals[i]; global.blank(); global.mValue.setType (ESM::VT_Long); if (i==0) global.mValue.setInteger (1); // dayspassed starts counting at 1 addOptionalGlobal (global); } } void CSMDoc::Document::addOptionalMagicEffects() { for (int i=ESM::MagicEffect::SummonFabricant; i<=ESM::MagicEffect::SummonCreature05; ++i) { ESM::MagicEffect effect; effect.mIndex = i; effect.mId = ESM::MagicEffect::indexToId (i); effect.blank(); addOptionalMagicEffect (effect); } } void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst) { if (getData().getGmsts().searchId (gmst.mId)==-1) { CSMWorld::Record record; record.mBase = gmst; record.mState = CSMWorld::RecordBase::State_BaseOnly; getData().getGmsts().appendRecord (record); } } void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global) { if (getData().getGlobals().searchId (global.mId)==-1) { CSMWorld::Record record; record.mBase = global; record.mState = CSMWorld::RecordBase::State_BaseOnly; getData().getGlobals().appendRecord (record); } } void CSMDoc::Document::addOptionalMagicEffect (const ESM::MagicEffect& magicEffect) { if (getData().getMagicEffects().searchId (magicEffect.mId)==-1) { CSMWorld::Record record; record.mBase = magicEffect; record.mState = CSMWorld::RecordBase::State_BaseOnly; getData().getMagicEffects().appendRecord (record); } } void CSMDoc::Document::createBase() { static const char *sGlobals[] = { "Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0 }; for (int i=0; sGlobals[i]; ++i) { ESM::Global record; record.mId = sGlobals[i]; record.mValue.setType (i==2 ? ESM::VT_Float : ESM::VT_Long); if (i==0 || i==1) record.mValue.setInteger (1); getData().getGlobals().add (record); } addGmsts(); for (int i=0; i<27; ++i) { ESM::Skill record; record.mIndex = i; record.mId = ESM::Skill::indexToId (record.mIndex); record.blank(); getData().getSkills().add (record); } static const char *sVoice[] = { "Intruder", "Attack", "Hello", "Thief", "Alarm", "Idle", "Flee", "Hit", 0 }; for (int i=0; sVoice[i]; ++i) { ESM::Dialogue record; record.mId = sVoice[i]; record.mType = ESM::Dialogue::Voice; record.blank(); getData().getTopics().add (record); } static const char *sGreetings[] = { "Greeting 0", "Greeting 1", "Greeting 2", "Greeting 3", "Greeting 4", "Greeting 5", "Greeting 6", "Greeting 7", "Greeting 8", "Greeting 9", 0 }; for (int i=0; sGreetings[i]; ++i) { ESM::Dialogue record; record.mId = sGreetings[i]; record.mType = ESM::Dialogue::Greeting; record.blank(); getData().getTopics().add (record); } static const char *sPersuasion[] = { "Intimidate Success", "Intimidate Fail", "Service Refusal", "Admire Success", "Taunt Success", "Bribe Success", "Info Refusal", "Admire Fail", "Taunt Fail", "Bribe Fail", 0 }; for (int i=0; sPersuasion[i]; ++i) { ESM::Dialogue record; record.mId = sPersuasion[i]; record.mType = ESM::Dialogue::Persuasion; record.blank(); getData().getTopics().add (record); } for (int i=0; i& files, bool new_, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, const std::vector& blacklistedScripts) : mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager), mTools (*this, encoding), mProjectPath ((configuration.getUserDataPath() / "projects") / (savePath.filename().string() + ".project")), mSavingOperation (*this, mProjectPath, encoding), mSaving (&mSavingOperation), mResDir(resDir), mRunner (mProjectPath), mDirty (false), mIdCompletionManager(mData) { if (mContentFiles.empty()) throw std::runtime_error ("Empty content file sequence"); if (!boost::filesystem::exists (mProjectPath)) { boost::filesystem::path customFiltersPath (configuration.getUserDataPath()); customFiltersPath /= "defaultfilters"; std::ofstream destination (mProjectPath.string().c_str(), std::ios::binary); if (boost::filesystem::exists (customFiltersPath)) { destination << std::ifstream(customFiltersPath.string().c_str(), std::ios::binary).rdbuf(); } else { destination << std::ifstream(std::string(mResDir.string() + "/defaultfilters").c_str(), std::ios::binary).rdbuf(); } } if (mNew) { if (mContentFiles.size()==1) createBase(); } mBlacklist.add (CSMWorld::UniversalId::Type_Script, blacklistedScripts); addOptionalGmsts(); addOptionalGlobals(); addOptionalMagicEffects(); connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool))); connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool))); connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)), this, SIGNAL (mergeDone (CSMDoc::Document*))); connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool))); connect ( &mSaving, SIGNAL (reportMessage (const CSMDoc::Message&, int)), this, SLOT (reportMessage (const CSMDoc::Message&, int))); connect (&mRunner, SIGNAL (runStateChanged()), this, SLOT (runStateChanged())); } CSMDoc::Document::~Document() { } const VFS::Manager *CSMDoc::Document::getVFS() const { return mVFS; } QUndoStack& CSMDoc::Document::getUndoStack() { return mUndoStack; } int CSMDoc::Document::getState() const { int state = 0; if (!mUndoStack.isClean() || mDirty) state |= State_Modified; if (mSaving.isRunning()) state |= State_Locked | State_Saving | State_Operation; if (mRunner.isRunning()) state |= State_Locked | State_Running; if (int operations = mTools.getRunningOperations()) state |= State_Locked | State_Operation | operations; return state; } const boost::filesystem::path& CSMDoc::Document::getSavePath() const { return mSavePath; } const boost::filesystem::path& CSMDoc::Document::getProjectPath() const { return mProjectPath; } const std::vector& CSMDoc::Document::getContentFiles() const { return mContentFiles; } bool CSMDoc::Document::isNew() const { return mNew; } void CSMDoc::Document::save() { if (mSaving.isRunning()) throw std::logic_error ( "Failed to initiate save, because a save operation is already running."); mSaving.start(); emit stateChanged (getState(), this); } CSMWorld::UniversalId CSMDoc::Document::verify (const CSMWorld::UniversalId& reportId) { CSMWorld::UniversalId id = mTools.runVerifier (reportId); emit stateChanged (getState(), this); return id; } CSMWorld::UniversalId CSMDoc::Document::newSearch() { return mTools.newSearch(); } void CSMDoc::Document::runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search) { mTools.runSearch (searchId, search); emit stateChanged (getState(), this); } void CSMDoc::Document::runMerge (std::auto_ptr target) { mTools.runMerge (target); emit stateChanged (getState(), this); } void CSMDoc::Document::abortOperation (int type) { if (type==State_Saving) mSaving.abort(); else mTools.abortOperation (type); } void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); } void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type) { /// \todo find a better way to get these messages to the user. std::cout << message.mMessage << std::endl; } void CSMDoc::Document::operationDone (int type, bool failed) { if (type==CSMDoc::State_Saving && !failed) mDirty = false; emit stateChanged (getState(), this); } const CSMWorld::Data& CSMDoc::Document::getData() const { return mData; } CSMWorld::Data& CSMDoc::Document::getData() { return mData; } CSMTools::ReportModel *CSMDoc::Document::getReport (const CSMWorld::UniversalId& id) { return mTools.getReport (id); } bool CSMDoc::Document::isBlacklisted (const CSMWorld::UniversalId& id) const { return mBlacklist.isBlacklisted (id); } void CSMDoc::Document::startRunning (const std::string& profile, const std::string& startupInstruction) { std::vector contentFiles; for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) contentFiles.push_back (iter->filename().string()); mRunner.configure (getData().getDebugProfiles().getRecord (profile).get(), contentFiles, startupInstruction); int state = getState(); if (state & State_Modified) { // need to save first mRunner.start (true); new SaveWatcher (&mRunner, &mSaving); // no, that is not a memory leak. Qt is weird. if (!(state & State_Saving)) save(); } else mRunner.start(); } void CSMDoc::Document::stopRunning() { mRunner.stop(); } QTextDocument *CSMDoc::Document::getRunLog() { return mRunner.getLog(); } void CSMDoc::Document::runStateChanged() { emit stateChanged (getState(), this); } void CSMDoc::Document::progress (int current, int max, int type) { emit progress (current, max, type, 1, this); } CSMWorld::IdCompletionManager &CSMDoc::Document::getIdCompletionManager() { return mIdCompletionManager; } void CSMDoc::Document::flagAsDirty() { mDirty = true; } openmw-openmw-0.38.0/apps/opencs/model/doc/document.hpp000066400000000000000000000117441264522266000230540ustar00rootroot00000000000000#ifndef CSM_DOC_DOCUMENT_H #define CSM_DOC_DOCUMENT_H #include #include #include #include #include #include #include #include "../world/data.hpp" #include "../world/idcompletionmanager.hpp" #include "../tools/tools.hpp" #include "state.hpp" #include "saving.hpp" #include "blacklist.hpp" #include "runner.hpp" #include "operationholder.hpp" class QAbstractItemModel; namespace VFS { class Manager; } namespace ESM { struct GameSetting; struct Global; struct MagicEffect; } namespace Files { struct ConfigurationManager; } namespace CSMWorld { class ResourcesManager; } namespace CSMDoc { class Document : public QObject { Q_OBJECT private: const VFS::Manager* mVFS; boost::filesystem::path mSavePath; std::vector mContentFiles; bool mNew; CSMWorld::Data mData; CSMTools::Tools mTools; boost::filesystem::path mProjectPath; Saving mSavingOperation; OperationHolder mSaving; boost::filesystem::path mResDir; Blacklist mBlacklist; Runner mRunner; bool mDirty; CSMWorld::IdCompletionManager mIdCompletionManager; // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late. QUndoStack mUndoStack; // not implemented Document (const Document&); Document& operator= (const Document&); void createBase(); void addGmsts(); void addOptionalGmsts(); void addOptionalGlobals(); void addOptionalMagicEffects(); void addOptionalGmst (const ESM::GameSetting& gmst); void addOptionalGlobal (const ESM::Global& global); void addOptionalMagicEffect (const ESM::MagicEffect& effect); public: Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, bool new_, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, const std::vector& blacklistedScripts); ~Document(); const VFS::Manager* getVFS() const; QUndoStack& getUndoStack(); int getState() const; const boost::filesystem::path& getSavePath() const; const boost::filesystem::path& getProjectPath() const; const std::vector& getContentFiles() const; ///< \attention The last element in this collection is the file that is being edited, /// but with its original path instead of the save path. bool isNew() const; ///< Is this a newly created content file? void save(); CSMWorld::UniversalId verify (const CSMWorld::UniversalId& reportId = CSMWorld::UniversalId()); CSMWorld::UniversalId newSearch(); void runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search); void runMerge (std::auto_ptr target); void abortOperation (int type); const CSMWorld::Data& getData() const; CSMWorld::Data& getData(); CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id); ///< The ownership of the returned report is not transferred. bool isBlacklisted (const CSMWorld::UniversalId& id) const; void startRunning (const std::string& profile, const std::string& startupInstruction = ""); void stopRunning(); QTextDocument *getRunLog(); CSMWorld::IdCompletionManager &getIdCompletionManager(); void flagAsDirty(); signals: void stateChanged (int state, CSMDoc::Document *document); void progress (int current, int max, int type, int threads, CSMDoc::Document *document); /// \attention When this signal is emitted, *this hands over the ownership of the /// document. This signal must be handled to avoid a leak. void mergeDone (CSMDoc::Document *document); private slots: void modificationStateChanged (bool clean); void reportMessage (const CSMDoc::Message& message, int type); void operationDone (int type, bool failed); void runStateChanged(); public slots: void progress (int current, int max, int type); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/documentmanager.cpp000066400000000000000000000102151264522266000243720ustar00rootroot00000000000000#include "documentmanager.hpp" #include #include #include #ifndef Q_MOC_RUN #include #endif #include "document.hpp" CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) : mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252), mVFS(NULL) { boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects"; if (!boost::filesystem::is_directory (projectPath)) boost::filesystem::create_directories (projectPath); mLoader.moveToThread (&mLoaderThread); mLoaderThread.start(); connect (&mLoader, SIGNAL (documentLoaded (Document *)), this, SLOT (documentLoaded (Document *))); connect (&mLoader, SIGNAL (documentNotLoaded (Document *, const std::string&)), this, SLOT (documentNotLoaded (Document *, const std::string&))); connect (this, SIGNAL (loadRequest (CSMDoc::Document *)), &mLoader, SLOT (loadDocument (CSMDoc::Document *))); connect (&mLoader, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)), this, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int))); connect (&mLoader, SIGNAL (nextRecord (CSMDoc::Document *, int)), this, SIGNAL (nextRecord (CSMDoc::Document *, int))); connect (this, SIGNAL (cancelLoading (CSMDoc::Document *)), &mLoader, SLOT (abortLoading (CSMDoc::Document *))); connect (&mLoader, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)), this, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&))); } CSMDoc::DocumentManager::~DocumentManager() { mLoaderThread.quit(); mLoader.hasThingsToDo().wakeAll(); mLoaderThread.wait(); for (std::vector::iterator iter (mDocuments.begin()); iter!=mDocuments.end(); ++iter) delete *iter; } bool CSMDoc::DocumentManager::isEmpty() { return mDocuments.empty(); } void CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { Document *document = makeDocument (files, savePath, new_); insertDocument (document); } CSMDoc::Document *CSMDoc::DocumentManager::makeDocument ( const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_) { return new Document (mVFS, mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager, mBlacklistedScripts); } void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document) { mDocuments.push_back (document); connect (document, SIGNAL (mergeDone (CSMDoc::Document*)), this, SLOT (insertDocument (CSMDoc::Document*))); emit loadRequest (document); mLoader.hasThingsToDo().wakeAll(); } void CSMDoc::DocumentManager::removeDocument (CSMDoc::Document *document) { std::vector::iterator iter = std::find (mDocuments.begin(), mDocuments.end(), document); if (iter==mDocuments.end()) throw std::runtime_error ("removing invalid document"); emit documentAboutToBeRemoved (document); mDocuments.erase (iter); document->deleteLater(); if (mDocuments.empty()) emit lastDocumentDeleted(); } void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir) { mResDir = boost::filesystem::system_complete(parResDir); } void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding) { mEncoding = encoding; } void CSMDoc::DocumentManager::setBlacklistedScripts (const std::vector& scriptIds) { mBlacklistedScripts = scriptIds; } void CSMDoc::DocumentManager::documentLoaded (Document *document) { emit documentAdded (document); emit loadingStopped (document, true, ""); } void CSMDoc::DocumentManager::documentNotLoaded (Document *document, const std::string& error) { emit loadingStopped (document, false, error); if (error.empty()) // do not remove the document yet, if we have an error removeDocument (document); } void CSMDoc::DocumentManager::setVFS(const VFS::Manager *vfs) { mResourcesManager.setVFS(vfs); mVFS = vfs; } openmw-openmw-0.38.0/apps/opencs/model/doc/documentmanager.hpp000066400000000000000000000074121264522266000244040ustar00rootroot00000000000000#ifndef CSM_DOC_DOCUMENTMGR_H #define CSM_DOC_DOCUMENTMGR_H #include #include #include #include #include #include #include "../world/resourcesmanager.hpp" #include "loader.hpp" namespace VFS { class Manager; } namespace Files { struct ConfigurationManager; } namespace CSMDoc { class Document; class DocumentManager : public QObject { Q_OBJECT std::vector mDocuments; const Files::ConfigurationManager& mConfiguration; QThread mLoaderThread; Loader mLoader; ToUTF8::FromType mEncoding; CSMWorld::ResourcesManager mResourcesManager; std::vector mBlacklistedScripts; const VFS::Manager* mVFS; DocumentManager (const DocumentManager&); DocumentManager& operator= (const DocumentManager&); public: DocumentManager (const Files::ConfigurationManager& configuration); ~DocumentManager(); void addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); ///< \param new_ Do not load the last content file in \a files and instead create in an /// appropriate way. /// Create a new document. The ownership of the created document is transferred to /// the calling function. The DocumentManager does not manage it. Loading has not /// taken place at the point when the document is returned. /// /// \param new_ Do not load the last content file in \a files and instead create in an /// appropriate way. Document *makeDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); void setResourceDir (const boost::filesystem::path& parResDir); void setEncoding (ToUTF8::FromType encoding); void setBlacklistedScripts (const std::vector& scriptIds); void setVFS(const VFS::Manager* vfs); bool isEmpty(); private: boost::filesystem::path mResDir; private slots: void documentLoaded (Document *document); ///< The ownership of \a document is not transferred. void documentNotLoaded (Document *document, const std::string& error); ///< Document load has been interrupted either because of a call to abortLoading /// or a problem during loading). In the former case error will be an empty string. public slots: void removeDocument (CSMDoc::Document *document); ///< Emits the lastDocumentDeleted signal, if applicable. /// Hand over document to *this. The ownership is transferred. The DocumentManager /// will initiate the load procedure, if necessary void insertDocument (CSMDoc::Document *document); signals: void documentAdded (CSMDoc::Document *document); void documentAboutToBeRemoved (CSMDoc::Document *document); void loadRequest (CSMDoc::Document *document); void lastDocumentDeleted(); void loadingStopped (CSMDoc::Document *document, bool completed, const std::string& error); void nextStage (CSMDoc::Document *document, const std::string& name, int totalRecords); void nextRecord (CSMDoc::Document *document, int records); void cancelLoading (CSMDoc::Document *document); void loadMessage (CSMDoc::Document *document, const std::string& message); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/loader.cpp000066400000000000000000000067421264522266000225010ustar00rootroot00000000000000#include "loader.hpp" #include #include "../tools/reportmodel.hpp" #include "document.hpp" #include "state.hpp" CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLoaded (0), mRecordsLeft (false) {} CSMDoc::Loader::Loader() { QTimer *timer = new QTimer (this); connect (timer, SIGNAL (timeout()), this, SLOT (load())); timer->start(); } QWaitCondition& CSMDoc::Loader::hasThingsToDo() { return mThingsToDo; } void CSMDoc::Loader::load() { if (mDocuments.empty()) { mMutex.lock(); mThingsToDo.wait (&mMutex); mMutex.unlock(); return; } std::vector >::iterator iter = mDocuments.begin(); Document *document = iter->first; int size = static_cast (document->getContentFiles().size()); int editedIndex = size-1; // index of the file to be edited/created if (document->isNew()) --size; bool done = false; try { if (iter->second.mRecordsLeft) { Messages messages (Message::Severity_Error); const int batchingSize = 50; for (int i=0; igetData().continueLoading (messages)) { iter->second.mRecordsLeft = false; break; } else ++(iter->second.mRecordsLoaded); CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0); { // silence a g++ warning for (CSMDoc::Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter) { document->getReport (log)->add (*iter); emit loadMessage (document, iter->mMessage); } } emit nextRecord (document, iter->second.mRecordsLoaded); return; } if (iter->second.mFilegetContentFiles()[iter->second.mFile]; int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, false); iter->second.mRecordsLeft = true; iter->second.mRecordsLoaded = 0; emit nextStage (document, path.filename().string(), steps); } else if (iter->second.mFile==size) { int steps = document->getData().startLoading (document->getProjectPath(), false, true); iter->second.mRecordsLeft = true; iter->second.mRecordsLoaded = 0; emit nextStage (document, "Project File", steps); } else { done = true; } ++(iter->second.mFile); } catch (const std::exception& e) { mDocuments.erase (iter); emit documentNotLoaded (document, e.what()); return; } if (done) { mDocuments.erase (iter); emit documentLoaded (document); } } void CSMDoc::Loader::loadDocument (CSMDoc::Document *document) { mDocuments.push_back (std::make_pair (document, Stage())); } void CSMDoc::Loader::abortLoading (CSMDoc::Document *document) { for (std::vector >::iterator iter = mDocuments.begin(); iter!=mDocuments.end(); ++iter) { if (iter->first==document) { mDocuments.erase (iter); emit documentNotLoaded (document, ""); break; } } } openmw-openmw-0.38.0/apps/opencs/model/doc/loader.hpp000066400000000000000000000040671264522266000225040ustar00rootroot00000000000000#ifndef CSM_DOC_LOADER_H #define CSM_DOC_LOADER_H #include #include #include #include namespace CSMDoc { class Document; class Loader : public QObject { Q_OBJECT struct Stage { int mFile; int mRecordsLoaded; bool mRecordsLeft; Stage(); }; QMutex mMutex; QWaitCondition mThingsToDo; std::vector > mDocuments; public: Loader(); QWaitCondition& hasThingsToDo(); private slots: void load(); public slots: void loadDocument (CSMDoc::Document *document); ///< The ownership of \a document is not transferred. void abortLoading (CSMDoc::Document *document); ///< Abort loading \a docuemnt (ignored if \a document has already finished being /// loaded). Will result in a documentNotLoaded signal, once the Loader has finished /// cleaning up. signals: void documentLoaded (Document *document); ///< The ownership of \a document is not transferred. void documentNotLoaded (Document *document, const std::string& error); ///< Document load has been interrupted either because of a call to abortLoading /// or a problem during loading). In the former case error will be an empty string. void nextStage (CSMDoc::Document *document, const std::string& name, int totalRecords); void nextRecord (CSMDoc::Document *document, int records); ///< \note This signal is only given once per group of records. The group size is /// approximately the total number of records divided by the steps value of the /// previous nextStage signal. void loadMessage (CSMDoc::Document *document, const std::string& message); ///< Non-critical load error or warning }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/messages.cpp000066400000000000000000000025601264522266000230340ustar00rootroot00000000000000#include "messages.hpp" CSMDoc::Message::Message() {} CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& message, const std::string& hint, Severity severity) : mId (id), mMessage (message), mHint (hint), mSeverity (severity) {} std::string CSMDoc::Message::toString (Severity severity) { switch (severity) { case CSMDoc::Message::Severity_Info: return "Information"; case CSMDoc::Message::Severity_Warning: return "Warning"; case CSMDoc::Message::Severity_Error: return "Error"; case CSMDoc::Message::Severity_SeriousError: return "Serious Error"; case CSMDoc::Message::Severity_Default: break; } return ""; } CSMDoc::Messages::Messages (Message::Severity default_) : mDefault (default_) {} void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string& message, const std::string& hint, Message::Severity severity) { if (severity==Message::Severity_Default) severity = mDefault; mMessages.push_back (Message (id, message, hint, severity)); } void CSMDoc::Messages::push_back (const std::pair& data) { add (data.first, data.second); } CSMDoc::Messages::Iterator CSMDoc::Messages::begin() const { return mMessages.begin(); } CSMDoc::Messages::Iterator CSMDoc::Messages::end() const { return mMessages.end(); } openmw-openmw-0.38.0/apps/opencs/model/doc/messages.hpp000066400000000000000000000035141264522266000230410ustar00rootroot00000000000000#ifndef CSM_DOC_MESSAGES_H #define CSM_DOC_MESSAGES_H #include #include #include #include "../world/universalid.hpp" namespace CSMDoc { struct Message { enum Severity { Severity_Info = 0, // no problem Severity_Warning = 1, // a potential problem, but we are probably fine Severity_Error = 2, // an error; we are not fine Severity_SeriousError = 3, // an error so bad we can't even be sure if we are // reporting it correctly Severity_Default = 4 }; CSMWorld::UniversalId mId; std::string mMessage; std::string mHint; Severity mSeverity; Message(); Message (const CSMWorld::UniversalId& id, const std::string& message, const std::string& hint, Severity severity); static std::string toString (Severity severity); }; class Messages { public: // \deprecated Use CSMDoc::Message directly instead. typedef CSMDoc::Message Message; typedef std::vector Collection; typedef Collection::const_iterator Iterator; private: Collection mMessages; Message::Severity mDefault; public: Messages (Message::Severity default_); void add (const CSMWorld::UniversalId& id, const std::string& message, const std::string& hint = "", Message::Severity severity = Message::Severity_Default); /// \deprecated Use add instead. void push_back (const std::pair& data); Iterator begin() const; Iterator end() const; }; } Q_DECLARE_METATYPE (CSMDoc::Message) #endif openmw-openmw-0.38.0/apps/opencs/model/doc/operation.cpp000066400000000000000000000061141264522266000232240ustar00rootroot00000000000000#include "operation.hpp" #include #include #include #include "../world/universalid.hpp" #include "state.hpp" #include "stage.hpp" void CSMDoc::Operation::prepareStages() { mCurrentStage = mStages.begin(); mCurrentStep = 0; mCurrentStepTotal = 0; mTotalSteps = 0; mError = false; for (std::vector >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) { iter->second = iter->first->setup(); mTotalSteps += iter->second; } } CSMDoc::Operation::Operation (int type, bool ordered, bool finalAlways) : mType (type), mStages(std::vector >()), mCurrentStage(mStages.begin()), mCurrentStep(0), mCurrentStepTotal(0), mTotalSteps(0), mOrdered (ordered), mFinalAlways (finalAlways), mError(false), mConnected (false), mPrepared (false), mDefaultSeverity (Message::Severity_Error) { mTimer = new QTimer (this); } CSMDoc::Operation::~Operation() { for (std::vector >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) delete iter->first; } void CSMDoc::Operation::run() { mTimer->stop(); if (!mConnected) { connect (mTimer, SIGNAL (timeout()), this, SLOT (executeStage())); mConnected = true; } mPrepared = false; mTimer->start (0); } void CSMDoc::Operation::appendStage (Stage *stage) { mStages.push_back (std::make_pair (stage, 0)); } void CSMDoc::Operation::setDefaultSeverity (Message::Severity severity) { mDefaultSeverity = severity; } bool CSMDoc::Operation::hasError() const { return mError; } void CSMDoc::Operation::abort() { if (!mTimer->isActive()) return; mError = true; if (mFinalAlways) { if (mStages.begin()!=mStages.end() && mCurrentStage!=--mStages.end()) { mCurrentStep = 0; mCurrentStage = --mStages.end(); } } else mCurrentStage = mStages.end(); } void CSMDoc::Operation::executeStage() { if (!mPrepared) { prepareStages(); mPrepared = true; } Messages messages (mDefaultSeverity); while (mCurrentStage!=mStages.end()) { if (mCurrentStep>=mCurrentStage->second) { mCurrentStep = 0; ++mCurrentStage; } else { try { mCurrentStage->first->perform (mCurrentStep++, messages); } catch (const std::exception& e) { emit reportMessage (Message (CSMWorld::UniversalId(), e.what(), "", Message::Severity_SeriousError), mType); abort(); } ++mCurrentStepTotal; break; } } emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType); for (Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter) emit reportMessage (*iter, mType); if (mCurrentStage==mStages.end()) operationDone(); } void CSMDoc::Operation::operationDone() { mTimer->stop(); emit done (mType, mError); } openmw-openmw-0.38.0/apps/opencs/model/doc/operation.hpp000066400000000000000000000037001264522266000232270ustar00rootroot00000000000000#ifndef CSM_DOC_OPERATION_H #define CSM_DOC_OPERATION_H #include #include #include #include #include #include "messages.hpp" namespace CSMWorld { class UniversalId; } namespace CSMDoc { class Stage; class Operation : public QObject { Q_OBJECT int mType; std::vector > mStages; // stage, number of steps std::vector >::iterator mCurrentStage; int mCurrentStep; int mCurrentStepTotal; int mTotalSteps; int mOrdered; bool mFinalAlways; bool mError; bool mConnected; QTimer *mTimer; bool mPrepared; Message::Severity mDefaultSeverity; void prepareStages(); public: Operation (int type, bool ordered, bool finalAlways = false); ///< \param ordered Stages must be executed in the given order. /// \param finalAlways Execute last stage even if an error occurred during earlier stages. virtual ~Operation(); void appendStage (Stage *stage); ///< The ownership of \a stage is transferred to *this. /// /// \attention Do no call this function while this Operation is running. /// \attention Do no call this function while this Operation is running. void setDefaultSeverity (Message::Severity severity); bool hasError() const; signals: void progress (int current, int max, int type); void reportMessage (const CSMDoc::Message& message, int type); void done (int type, bool failed); public slots: void abort(); void run(); private slots: void executeStage(); protected slots: virtual void operationDone(); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/operationholder.cpp000066400000000000000000000025461264522266000244270ustar00rootroot00000000000000#include "operationholder.hpp" #include "operation.hpp" CSMDoc::OperationHolder::OperationHolder (Operation *operation) : mRunning (false) { if (operation) setOperation (operation); } void CSMDoc::OperationHolder::setOperation (Operation *operation) { mOperation = operation; mOperation->moveToThread (&mThread); connect ( mOperation, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect ( mOperation, SIGNAL (reportMessage (const CSMDoc::Message&, int)), this, SIGNAL (reportMessage (const CSMDoc::Message&, int))); connect ( mOperation, SIGNAL (done (int, bool)), this, SLOT (doneSlot (int, bool))); connect (this, SIGNAL (abortSignal()), mOperation, SLOT (abort())); connect (&mThread, SIGNAL (started()), mOperation, SLOT (run())); } bool CSMDoc::OperationHolder::isRunning() const { return mRunning; } void CSMDoc::OperationHolder::start() { mRunning = true; mThread.start(); } void CSMDoc::OperationHolder::abort() { mRunning = false; emit abortSignal(); } void CSMDoc::OperationHolder::abortAndWait() { if (mRunning) { mThread.quit(); mThread.wait(); } } void CSMDoc::OperationHolder::doneSlot (int type, bool failed) { mRunning = false; mThread.quit(); emit done (type, failed); } openmw-openmw-0.38.0/apps/opencs/model/doc/operationholder.hpp000066400000000000000000000020271264522266000244260ustar00rootroot00000000000000#ifndef CSM_DOC_OPERATIONHOLDER_H #define CSM_DOC_OPERATIONHOLDER_H #include #include #include "messages.hpp" namespace CSMWorld { class UniversalId; } namespace CSMDoc { class Operation; class OperationHolder : public QObject { Q_OBJECT QThread mThread; Operation *mOperation; bool mRunning; public: OperationHolder (Operation *operation = 0); void setOperation (Operation *operation); bool isRunning() const; void start(); void abort(); // Abort and wait until thread has finished. void abortAndWait(); private slots: void doneSlot (int type, bool failed); signals: void progress (int current, int max, int type); void reportMessage (const CSMDoc::Message& message, int type); void done (int type, bool failed); void abortSignal(); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/runner.cpp000066400000000000000000000073221264522266000225370ustar00rootroot00000000000000#include "runner.hpp" #include #include #include #include #include "operationholder.hpp" CSMDoc::Runner::Runner (const boost::filesystem::path& projectPath) : mRunning (false), mStartup (0), mProjectPath (projectPath) { connect (&mProcess, SIGNAL (finished (int, QProcess::ExitStatus)), this, SLOT (finished (int, QProcess::ExitStatus))); connect (&mProcess, SIGNAL (readyReadStandardOutput()), this, SLOT (readyReadStandardOutput())); mProcess.setProcessChannelMode (QProcess::MergedChannels); mProfile.blank(); } CSMDoc::Runner::~Runner() { if (mRunning) { disconnect (&mProcess, 0, this, 0); mProcess.kill(); mProcess.waitForFinished(); } } void CSMDoc::Runner::start (bool delayed) { if (mStartup) { delete mStartup; mStartup = 0; } if (!delayed) { mLog.clear(); QString path = "openmw"; #ifdef Q_OS_WIN path.append(QString(".exe")); #elif defined(Q_OS_MAC) QDir dir(QCoreApplication::applicationDirPath()); dir.cdUp(); dir.cdUp(); dir.cdUp(); path = dir.absoluteFilePath(path.prepend("OpenMW.app/Contents/MacOS/")); #else path.prepend(QString("./")); #endif mStartup = new QTemporaryFile (this); mStartup->open(); { QTextStream stream (mStartup); if (!mStartupInstruction.empty()) stream << QString::fromUtf8 (mStartupInstruction.c_str()) << '\n'; stream << QString::fromUtf8 (mProfile.mScriptText.c_str()); } mStartup->close(); QStringList arguments; arguments << "--skip-menu"; if (mProfile.mFlags & ESM::DebugProfile::Flag_BypassNewGame) arguments << "--new-game=0"; else arguments << "--new-game=1"; arguments << ("--script-run="+mStartup->fileName());; arguments << QString::fromUtf8 (("--data="+mProjectPath.parent_path().string()).c_str()); for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) { arguments << QString::fromUtf8 (("--content="+*iter).c_str()); } arguments << QString::fromUtf8 (("--content="+mProjectPath.filename().string()).c_str()); mProcess.start (path, arguments); } mRunning = true; emit runStateChanged(); } void CSMDoc::Runner::stop() { delete mStartup; mStartup = 0; if (mProcess.state()==QProcess::NotRunning) { mRunning = false; emit runStateChanged(); } else mProcess.kill(); } bool CSMDoc::Runner::isRunning() const { return mRunning; } void CSMDoc::Runner::configure (const ESM::DebugProfile& profile, const std::vector& contentFiles, const std::string& startupInstruction) { mProfile = profile; mContentFiles = contentFiles; mStartupInstruction = startupInstruction; } void CSMDoc::Runner::finished (int exitCode, QProcess::ExitStatus exitStatus) { mRunning = false; emit runStateChanged(); } QTextDocument *CSMDoc::Runner::getLog() { return &mLog; } void CSMDoc::Runner::readyReadStandardOutput() { mLog.setPlainText ( mLog.toPlainText() + QString::fromUtf8 (mProcess.readAllStandardOutput())); } CSMDoc::SaveWatcher::SaveWatcher (Runner *runner, OperationHolder *operation) : QObject (runner), mRunner (runner) { connect (operation, SIGNAL (done (int, bool)), this, SLOT (saveDone (int, bool))); } void CSMDoc::SaveWatcher::saveDone (int type, bool failed) { if (failed) mRunner->stop(); else mRunner->start(); deleteLater(); } openmw-openmw-0.38.0/apps/opencs/model/doc/runner.hpp000066400000000000000000000040721264522266000225430ustar00rootroot00000000000000#ifndef CSM_DOC_RUNNER_H #define CSM_DOC_RUNNER_H #include #include #include #include #include #include #include class QTemporaryFile; namespace CSMDoc { class OperationHolder; class Runner : public QObject { Q_OBJECT QProcess mProcess; bool mRunning; ESM::DebugProfile mProfile; std::vector mContentFiles; std::string mStartupInstruction; QTemporaryFile *mStartup; QTextDocument mLog; boost::filesystem::path mProjectPath; public: Runner (const boost::filesystem::path& projectPath); ~Runner(); /// \param delayed Flag as running but do not start the OpenMW process yet (the /// process must be started by another call of start with delayed==false) void start (bool delayed = false); void stop(); /// \note Running state is entered when the start function is called. This /// is not necessarily identical to the moment the child process is started. bool isRunning() const; void configure (const ESM::DebugProfile& profile, const std::vector& contentFiles, const std::string& startupInstruction); QTextDocument *getLog(); signals: void runStateChanged(); private slots: void finished (int exitCode, QProcess::ExitStatus exitStatus); void readyReadStandardOutput(); }; class Operation; /// \brief Watch for end of save operation and restart or stop runner class SaveWatcher : public QObject { Q_OBJECT Runner *mRunner; public: /// *this attaches itself to runner SaveWatcher (Runner *runner, OperationHolder *operation); private slots: void saveDone (int type, bool failed); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/saving.cpp000066400000000000000000000103171264522266000225130ustar00rootroot00000000000000#include "saving.hpp" #include "../world/data.hpp" #include "../world/idcollection.hpp" #include "state.hpp" #include "savingstages.hpp" #include "document.hpp" CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath, ToUTF8::FromType encoding) : Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath, encoding) { // save project file appendStage (new OpenSaveStage (mDocument, mState, true)); appendStage (new WriteHeaderStage (mDocument, mState, true)); appendStage (new WriteCollectionStage > ( mDocument.getData().getFilters(), mState, CSMWorld::Scope_Project)); appendStage (new WriteCollectionStage > ( mDocument.getData().getDebugProfiles(), mState, CSMWorld::Scope_Project)); appendStage (new WriteCollectionStage > ( mDocument.getData().getScripts(), mState, CSMWorld::Scope_Project)); appendStage (new CloseSaveStage (mState)); // save content file appendStage (new OpenSaveStage (mDocument, mState, false)); appendStage (new WriteHeaderStage (mDocument, mState, false)); appendStage (new WriteCollectionStage > (mDocument.getData().getGlobals(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getGmsts(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getSkills(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getClasses(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getFactions(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getRaces(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getSounds(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getScripts(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getRegions(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getBirthsigns(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getSpells(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getEnchantments(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getBodyParts(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getSoundGens(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getMagicEffects(), mState)); appendStage (new WriteCollectionStage > (mDocument.getData().getStartScripts(), mState)); appendStage (new WriteRefIdCollectionStage (mDocument, mState)); appendStage (new CollectionReferencesStage (mDocument, mState)); appendStage (new WriteCellCollectionStage (mDocument, mState)); // Dialogue can reference objects and cells so must be written after these records for vanilla-compatible files appendStage (new WriteDialogueCollectionStage (mDocument, mState, false)); appendStage (new WriteDialogueCollectionStage (mDocument, mState, true)); appendStage (new WritePathgridCollectionStage (mDocument, mState)); appendStage (new WriteLandTextureCollectionStage (mDocument, mState)); // references Land Textures appendStage (new WriteLandCollectionStage (mDocument, mState)); // close file and clean up appendStage (new CloseSaveStage (mState)); appendStage (new FinalSavingStage (mDocument, mState)); } openmw-openmw-0.38.0/apps/opencs/model/doc/saving.hpp000066400000000000000000000010061264522266000225130ustar00rootroot00000000000000#ifndef CSM_DOC_SAVING_H #define CSM_DOC_SAVING_H #include #include #include "operation.hpp" #include "savingstate.hpp" namespace CSMDoc { class Document; class Saving : public Operation { Q_OBJECT Document& mDocument; SavingState mState; public: Saving (Document& document, const boost::filesystem::path& projectPath, ToUTF8::FromType encoding); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/savingstages.cpp000066400000000000000000000365621264522266000237340ustar00rootroot00000000000000#include "savingstages.hpp" #include #include #include #include #include #include "../world/infocollection.hpp" #include "document.hpp" #include "savingstate.hpp" CSMDoc::OpenSaveStage::OpenSaveStage (Document& document, SavingState& state, bool projectFile) : mDocument (document), mState (state), mProjectFile (projectFile) {} int CSMDoc::OpenSaveStage::setup() { return 1; } void CSMDoc::OpenSaveStage::perform (int stage, Messages& messages) { mState.start (mDocument, mProjectFile); mState.getStream().open ( mProjectFile ? mState.getPath() : mState.getTmpPath(), std::ios::binary); if (!mState.getStream().is_open()) throw std::runtime_error ("failed to open stream for saving"); } CSMDoc::WriteHeaderStage::WriteHeaderStage (Document& document, SavingState& state, bool simple) : mDocument (document), mState (state), mSimple (simple) {} int CSMDoc::WriteHeaderStage::setup() { return 1; } void CSMDoc::WriteHeaderStage::perform (int stage, Messages& messages) { mState.getWriter().setVersion(); mState.getWriter().clearMaster(); if (mSimple) { mState.getWriter().setAuthor (""); mState.getWriter().setDescription (""); mState.getWriter().setRecordCount (0); mState.getWriter().setFormat (ESM::Header::CurrentFormat); } else { mDocument.getData().getMetaData().save (mState.getWriter()); mState.getWriter().setRecordCount ( mDocument.getData().count (CSMWorld::RecordBase::State_Modified) + mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) + mDocument.getData().count (CSMWorld::RecordBase::State_Deleted)); /// \todo refine dependency list (at least remove redundant dependencies) std::vector dependencies = mDocument.getContentFiles(); std::vector::const_iterator end (--dependencies.end()); for (std::vector::const_iterator iter (dependencies.begin()); iter!=end; ++iter) { std::string name = iter->filename().string(); uint64_t size = boost::filesystem::file_size (*iter); mState.getWriter().addMaster (name, size); } } mState.getWriter().save (mState.getStream()); } CSMDoc::WriteDialogueCollectionStage::WriteDialogueCollectionStage (Document& document, SavingState& state, bool journal) : mState (state), mTopics (journal ? document.getData().getJournals() : document.getData().getTopics()), mInfos (journal ? document.getData().getJournalInfos() : document.getData().getTopicInfos()) {} int CSMDoc::WriteDialogueCollectionStage::setup() { return mTopics.getSize(); } void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& messages) { ESM::ESMWriter& writer = mState.getWriter(); const CSMWorld::Record& topic = mTopics.getRecord (stage); if (topic.mState == CSMWorld::RecordBase::State_Deleted) { // if the topic is deleted, we do not need to bother with INFO records. ESM::Dialogue dialogue = topic.get(); writer.startRecord(dialogue.sRecordId); dialogue.save(writer, true); writer.endRecord(dialogue.sRecordId); return; } // Test, if we need to save anything associated info records. bool infoModified = false; CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId); for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) { if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted) { infoModified = true; break; } } if (topic.isModified() || infoModified) { if (infoModified && topic.mState != CSMWorld::RecordBase::State_Modified && topic.mState != CSMWorld::RecordBase::State_ModifiedOnly) { mState.getWriter().startRecord (topic.mBase.sRecordId); topic.mBase.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted); mState.getWriter().endRecord (topic.mBase.sRecordId); } else { mState.getWriter().startRecord (topic.mModified.sRecordId); topic.mModified.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted); mState.getWriter().endRecord (topic.mModified.sRecordId); } // write modified selected info records for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) { if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted) { ESM::DialInfo info = iter->get(); info.mId = info.mId.substr (info.mId.find_last_of ('#')+1); info.mPrev = ""; if (iter!=range.first) { CSMWorld::InfoCollection::RecordConstIterator prev = iter; --prev; info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1); } CSMWorld::InfoCollection::RecordConstIterator next = iter; ++next; info.mNext = ""; if (next!=range.second) { info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1); } writer.startRecord (info.sRecordId); info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted); writer.endRecord (info.sRecordId); } } } } CSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state) : mDocument (document), mState (state) {} int CSMDoc::WriteRefIdCollectionStage::setup() { return mDocument.getData().getReferenceables().getSize(); } void CSMDoc::WriteRefIdCollectionStage::perform (int stage, Messages& messages) { mDocument.getData().getReferenceables().save (stage, mState.getWriter()); } CSMDoc::CollectionReferencesStage::CollectionReferencesStage (Document& document, SavingState& state) : mDocument (document), mState (state) {} int CSMDoc::CollectionReferencesStage::setup() { mState.getSubRecords().clear(); int size = mDocument.getData().getReferences().getSize(); int steps = size/100; if (size%100) ++steps; return steps; } void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages) { int size = mDocument.getData().getReferences().getSize(); for (int i=stage*100; i& record = mDocument.getData().getReferences().getRecord (i); if (record.isModified() || record.mState == CSMWorld::RecordBase::State_Deleted) { std::string cellId = record.get().mOriginalCell.empty() ? record.get().mCell : record.get().mOriginalCell; std::deque& indices = mState.getSubRecords()[Misc::StringUtils::lowerCase (cellId)]; // collect moved references at the end of the container bool interior = cellId.substr (0, 1)!="#"; std::ostringstream stream; if (!interior) { // recalculate the ref's cell location std::pair index = record.get().getCellIndex(); stream << "#" << index.first << " " << index.second; } // An empty mOriginalCell is meant to indicate that it is the same as // the current cell. It is possible that a moved ref is moved again. if ((record.get().mOriginalCell.empty() ? record.get().mCell : record.get().mOriginalCell) != stream.str() && !interior) indices.push_back (i); else indices.push_front (i); } } } CSMDoc::WriteCellCollectionStage::WriteCellCollectionStage (Document& document, SavingState& state) : mDocument (document), mState (state) {} int CSMDoc::WriteCellCollectionStage::setup() { return mDocument.getData().getCells().getSize(); } void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) { ESM::ESMWriter& writer = mState.getWriter(); const CSMWorld::Record& cell = mDocument.getData().getCells().getRecord (stage); std::map >::const_iterator references = mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId)); if (cell.isModified() || cell.mState == CSMWorld::RecordBase::State_Deleted || references!=mState.getSubRecords().end()) { CSMWorld::Cell cellRecord = cell.get(); bool interior = cellRecord.mId.substr (0, 1)!="#"; // write cell data writer.startRecord (cellRecord.sRecordId); if (interior) cellRecord.mData.mFlags |= ESM::Cell::Interior; else { cellRecord.mData.mFlags &= ~ESM::Cell::Interior; std::istringstream stream (cellRecord.mId.c_str()); char ignore; stream >> ignore >> cellRecord.mData.mX >> cellRecord.mData.mY; } cellRecord.save (writer, cell.mState == CSMWorld::RecordBase::State_Deleted); // write references if (references!=mState.getSubRecords().end()) { for (std::deque::const_iterator iter (references->second.begin()); iter!=references->second.end(); ++iter) { const CSMWorld::Record& ref = mDocument.getData().getReferences().getRecord (*iter); if (ref.isModified() || ref.mState == CSMWorld::RecordBase::State_Deleted) { CSMWorld::CellRef refRecord = ref.get(); // recalculate the ref's cell location std::ostringstream stream; if (!interior) { std::pair index = refRecord.getCellIndex(); stream << "#" << index.first << " " << index.second; } // An empty mOriginalCell is meant to indicate that it is the same as // the current cell. It is possible that a moved ref is moved again. if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) != stream.str() && !interior) { ESM::MovedCellRef moved; moved.mRefNum = refRecord.mRefNum; // Need to fill mTarget with the ref's new position. std::istringstream istream (stream.str().c_str()); char ignore; istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1]; refRecord.mRefNum.save (writer, false, "MVRF"); writer.writeHNT ("CNDT", moved.mTarget, 8); } refRecord.save (writer, false, false, ref.mState == CSMWorld::RecordBase::State_Deleted); } } } writer.endRecord (cellRecord.sRecordId); } } CSMDoc::WritePathgridCollectionStage::WritePathgridCollectionStage (Document& document, SavingState& state) : mDocument (document), mState (state) {} int CSMDoc::WritePathgridCollectionStage::setup() { return mDocument.getData().getPathgrids().getSize(); } void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages) { ESM::ESMWriter& writer = mState.getWriter(); const CSMWorld::Record& pathgrid = mDocument.getData().getPathgrids().getRecord (stage); if (pathgrid.isModified() || pathgrid.mState == CSMWorld::RecordBase::State_Deleted) { CSMWorld::Pathgrid record = pathgrid.get(); if (record.mId.substr (0, 1)=="#") { std::istringstream stream (record.mId.c_str()); char ignore; stream >> ignore >> record.mData.mX >> record.mData.mY; } else record.mCell = record.mId; writer.startRecord (record.sRecordId); record.save (writer, pathgrid.mState == CSMWorld::RecordBase::State_Deleted); writer.endRecord (record.sRecordId); } } CSMDoc::WriteLandCollectionStage::WriteLandCollectionStage (Document& document, SavingState& state) : mDocument (document), mState (state) {} int CSMDoc::WriteLandCollectionStage::setup() { return mDocument.getData().getLand().getSize(); } void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages) { ESM::ESMWriter& writer = mState.getWriter(); const CSMWorld::Record& land = mDocument.getData().getLand().getRecord (stage); if (land.isModified() || land.mState == CSMWorld::RecordBase::State_Deleted) { CSMWorld::Land record = land.get(); writer.startRecord (record.sRecordId); record.save (writer, land.mState == CSMWorld::RecordBase::State_Deleted); if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes)) data->save (mState.getWriter()); writer.endRecord (record.sRecordId); } } CSMDoc::WriteLandTextureCollectionStage::WriteLandTextureCollectionStage (Document& document, SavingState& state) : mDocument (document), mState (state) {} int CSMDoc::WriteLandTextureCollectionStage::setup() { return mDocument.getData().getLandTextures().getSize(); } void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages) { ESM::ESMWriter& writer = mState.getWriter(); const CSMWorld::Record& landTexture = mDocument.getData().getLandTextures().getRecord (stage); if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted) { CSMWorld::LandTexture record = landTexture.get(); writer.startRecord (record.sRecordId); record.save (writer, landTexture.mState == CSMWorld::RecordBase::State_Deleted); writer.endRecord (record.sRecordId); } } CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state) : mState (state) {} int CSMDoc::CloseSaveStage::setup() { return 1; } void CSMDoc::CloseSaveStage::perform (int stage, Messages& messages) { mState.getStream().close(); if (!mState.getStream()) throw std::runtime_error ("saving failed"); } CSMDoc::FinalSavingStage::FinalSavingStage (Document& document, SavingState& state) : mDocument (document), mState (state) {} int CSMDoc::FinalSavingStage::setup() { return 1; } void CSMDoc::FinalSavingStage::perform (int stage, Messages& messages) { if (mState.hasError()) { mState.getWriter().close(); mState.getStream().close(); if (boost::filesystem::exists (mState.getTmpPath())) boost::filesystem::remove (mState.getTmpPath()); } else if (!mState.isProjectFile()) { if (boost::filesystem::exists (mState.getPath())) boost::filesystem::remove (mState.getPath()); boost::filesystem::rename (mState.getTmpPath(), mState.getPath()); mDocument.getUndoStack().setClean(); } } openmw-openmw-0.38.0/apps/opencs/model/doc/savingstages.hpp000066400000000000000000000165001264522266000237270ustar00rootroot00000000000000#ifndef CSM_DOC_SAVINGSTAGES_H #define CSM_DOC_SAVINGSTAGES_H #include "stage.hpp" #include "../world/record.hpp" #include "../world/idcollection.hpp" #include "../world/scope.hpp" #include #include "savingstate.hpp" namespace ESM { struct Dialogue; } namespace CSMWorld { class InfoCollection; } namespace CSMDoc { class Document; class SavingState; class OpenSaveStage : public Stage { Document& mDocument; SavingState& mState; bool mProjectFile; public: OpenSaveStage (Document& document, SavingState& state, bool projectFile); ///< \param projectFile Saving the project file instead of the content file. virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class WriteHeaderStage : public Stage { Document& mDocument; SavingState& mState; bool mSimple; public: WriteHeaderStage (Document& document, SavingState& state, bool simple); ///< \param simple Simplified header (used for project files). virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; template class WriteCollectionStage : public Stage { const CollectionT& mCollection; SavingState& mState; CSMWorld::Scope mScope; public: WriteCollectionStage (const CollectionT& collection, SavingState& state, CSMWorld::Scope scope = CSMWorld::Scope_Content); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; template WriteCollectionStage::WriteCollectionStage (const CollectionT& collection, SavingState& state, CSMWorld::Scope scope) : mCollection (collection), mState (state), mScope (scope) {} template int WriteCollectionStage::setup() { return mCollection.getSize(); } template void WriteCollectionStage::perform (int stage, Messages& messages) { if (CSMWorld::getScopeFromId (mCollection.getRecord (stage).get().mId)!=mScope) return; ESM::ESMWriter& writer = mState.getWriter(); CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState; typename CollectionT::ESXRecord record = mCollection.getRecord (stage).get(); if (state == CSMWorld::RecordBase::State_Modified || state == CSMWorld::RecordBase::State_ModifiedOnly || state == CSMWorld::RecordBase::State_Deleted) { writer.startRecord (record.sRecordId); record.save (writer, state == CSMWorld::RecordBase::State_Deleted); writer.endRecord (record.sRecordId); } } class WriteDialogueCollectionStage : public Stage { SavingState& mState; const CSMWorld::IdCollection& mTopics; CSMWorld::InfoCollection& mInfos; public: WriteDialogueCollectionStage (Document& document, SavingState& state, bool journal); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class WriteRefIdCollectionStage : public Stage { Document& mDocument; SavingState& mState; public: WriteRefIdCollectionStage (Document& document, SavingState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class CollectionReferencesStage : public Stage { Document& mDocument; SavingState& mState; public: CollectionReferencesStage (Document& document, SavingState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class WriteCellCollectionStage : public Stage { Document& mDocument; SavingState& mState; public: WriteCellCollectionStage (Document& document, SavingState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class WritePathgridCollectionStage : public Stage { Document& mDocument; SavingState& mState; public: WritePathgridCollectionStage (Document& document, SavingState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class WriteLandCollectionStage : public Stage { Document& mDocument; SavingState& mState; public: WriteLandCollectionStage (Document& document, SavingState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class WriteLandTextureCollectionStage : public Stage { Document& mDocument; SavingState& mState; public: WriteLandTextureCollectionStage (Document& document, SavingState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class CloseSaveStage : public Stage { SavingState& mState; public: CloseSaveStage (SavingState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class FinalSavingStage : public Stage { Document& mDocument; SavingState& mState; public: FinalSavingStage (Document& document, SavingState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/savingstate.cpp000066400000000000000000000025601264522266000235550ustar00rootroot00000000000000#include "savingstate.hpp" #include "operation.hpp" #include "document.hpp" CSMDoc::SavingState::SavingState (Operation& operation, const boost::filesystem::path& projectPath, ToUTF8::FromType encoding) : mOperation (operation), mEncoder (encoding), mProjectPath (projectPath), mProjectFile (false) { mWriter.setEncoder (&mEncoder); } bool CSMDoc::SavingState::hasError() const { return mOperation.hasError(); } void CSMDoc::SavingState::start (Document& document, bool project) { mProjectFile = project; if (mStream.is_open()) mStream.close(); mStream.clear(); mSubRecords.clear(); if (project) mPath = mProjectPath; else mPath = document.getSavePath(); boost::filesystem::path file (mPath.filename().string() + ".tmp"); mTmpPath = mPath.parent_path(); mTmpPath /= file; } const boost::filesystem::path& CSMDoc::SavingState::getPath() const { return mPath; } const boost::filesystem::path& CSMDoc::SavingState::getTmpPath() const { return mTmpPath; } boost::filesystem::ofstream& CSMDoc::SavingState::getStream() { return mStream; } ESM::ESMWriter& CSMDoc::SavingState::getWriter() { return mWriter; } bool CSMDoc::SavingState::isProjectFile() const { return mProjectFile; } std::map >& CSMDoc::SavingState::getSubRecords() { return mSubRecords; } openmw-openmw-0.38.0/apps/opencs/model/doc/savingstate.hpp000066400000000000000000000030111264522266000235520ustar00rootroot00000000000000#ifndef CSM_DOC_SAVINGSTATE_H #define CSM_DOC_SAVINGSTATE_H #include #include #include #include #include #include #include namespace CSMDoc { class Operation; class Document; class SavingState { Operation& mOperation; boost::filesystem::path mPath; boost::filesystem::path mTmpPath; ToUTF8::Utf8Encoder mEncoder; boost::filesystem::ofstream mStream; ESM::ESMWriter mWriter; boost::filesystem::path mProjectPath; bool mProjectFile; std::map > mSubRecords; // record ID, list of subrecords public: SavingState (Operation& operation, const boost::filesystem::path& projectPath, ToUTF8::FromType encoding); bool hasError() const; void start (Document& document, bool project); ///< \param project Save project file instead of content file. const boost::filesystem::path& getPath() const; const boost::filesystem::path& getTmpPath() const; boost::filesystem::ofstream& getStream(); ESM::ESMWriter& getWriter(); bool isProjectFile() const; ///< Currently saving project file? (instead of content file) std::map >& getSubRecords(); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/stage.cpp000066400000000000000000000000611264522266000223220ustar00rootroot00000000000000#include "stage.hpp" CSMDoc::Stage::~Stage() {} openmw-openmw-0.38.0/apps/opencs/model/doc/stage.hpp000066400000000000000000000007701264522266000223360ustar00rootroot00000000000000#ifndef CSM_DOC_STAGE_H #define CSM_DOC_STAGE_H #include #include #include "../world/universalid.hpp" #include "messages.hpp" class QString; namespace CSMDoc { class Stage { public: virtual ~Stage(); virtual int setup() = 0; ///< \return number of steps virtual void perform (int stage, Messages& messages) = 0; ///< Messages resulting from this stage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/doc/state.hpp000066400000000000000000000006501264522266000223500ustar00rootroot00000000000000#ifndef CSM_DOC_STATE_H #define CSM_DOC_STATE_H namespace CSMDoc { enum State { State_Modified = 1, State_Locked = 2, State_Operation = 4, State_Running = 8, State_Saving = 16, State_Verifying = 32, State_Merging = 64, State_Searching = 128, State_Loading = 256 // pseudo-state; can not be encountered in a loaded document }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/000077500000000000000000000000001264522266000212365ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/model/filter/andnode.cpp000066400000000000000000000006621264522266000233560ustar00rootroot00000000000000#include "andnode.hpp" #include CSMFilter::AndNode::AndNode (const std::vector >& nodes) : NAryNode (nodes, "and") {} bool CSMFilter::AndNode::test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const { int size = getSize(); for (int i=0; i >& nodes); virtual bool test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const; ///< \return Can the specified table row pass through to filter? /// \param columns column ID to column index mapping }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/booleannode.cpp000066400000000000000000000005471264522266000242350ustar00rootroot00000000000000#include "booleannode.hpp" CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {} bool CSMFilter::BooleanNode::test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const { return mTrue; } std::string CSMFilter::BooleanNode::toString (bool numericColumns) const { return mTrue ? "true" : "false"; } openmw-openmw-0.38.0/apps/opencs/model/filter/booleannode.hpp000066400000000000000000000014101264522266000242300ustar00rootroot00000000000000#ifndef CSM_FILTER_BOOLEANNODE_H #define CSM_FILTER_BOOLEANNODE_H #include "leafnode.hpp" namespace CSMFilter { class BooleanNode : public LeafNode { bool mTrue; public: BooleanNode (bool true_); virtual bool test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const; ///< \return Can the specified table row pass through to filter? /// \param columns column ID to column index mapping virtual std::string toString (bool numericColumns) const; ///< Return a string that represents this node. /// /// \param numericColumns Use numeric IDs instead of string to represent columns. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/leafnode.cpp000066400000000000000000000002001264522266000235070ustar00rootroot00000000000000#include "leafnode.hpp" std::vector CSMFilter::LeafNode::getReferencedColumns() const { return std::vector(); } openmw-openmw-0.38.0/apps/opencs/model/filter/leafnode.hpp000066400000000000000000000006761264522266000235350ustar00rootroot00000000000000#ifndef CSM_FILTER_LEAFNODE_H #define CSM_FILTER_LEAFNODE_H #include #include "node.hpp" namespace CSMFilter { class LeafNode : public Node { public: virtual std::vector getReferencedColumns() const; ///< Return a list of the IDs of the columns referenced by this node. The column mapping /// passed into test as columns must contain all columns listed here. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/narynode.cpp000066400000000000000000000022551264522266000235650ustar00rootroot00000000000000#include "narynode.hpp" #include CSMFilter::NAryNode::NAryNode (const std::vector >& nodes, const std::string& name) : mNodes (nodes), mName (name) {} int CSMFilter::NAryNode::getSize() const { return mNodes.size(); } const CSMFilter::Node& CSMFilter::NAryNode::operator[] (int index) const { return *mNodes.at (index); } std::vector CSMFilter::NAryNode::getReferencedColumns() const { std::vector columns; for (std::vector >::const_iterator iter (mNodes.begin()); iter!=mNodes.end(); ++iter) { std::vector columns2 = (*iter)->getReferencedColumns(); columns.insert (columns.end(), columns2.begin(), columns2.end()); } return columns; } std::string CSMFilter::NAryNode::toString (bool numericColumns) const { std::ostringstream stream; stream << mName << " ("; bool first = true; int size = getSize(); for (int i=0; i #include #include #include "node.hpp" namespace CSMFilter { class NAryNode : public Node { std::vector > mNodes; std::string mName; public: NAryNode (const std::vector >& nodes, const std::string& name); int getSize() const; const Node& operator[] (int index) const; virtual std::vector getReferencedColumns() const; ///< Return a list of the IDs of the columns referenced by this node. The column mapping /// passed into test as columns must contain all columns listed here. virtual std::string toString (bool numericColumns) const; ///< Return a string that represents this node. /// /// \param numericColumns Use numeric IDs instead of string to represent columns. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/node.cpp000066400000000000000000000001151264522266000226640ustar00rootroot00000000000000#include "node.hpp" CSMFilter::Node::Node() {} CSMFilter::Node::~Node() {} openmw-openmw-0.38.0/apps/opencs/model/filter/node.hpp000066400000000000000000000027311264522266000226770ustar00rootroot00000000000000#ifndef CSM_FILTER_NODE_H #define CSM_FILTER_NODE_H #include #include #include #include #include namespace CSMWorld { class IdTableBase; } namespace CSMFilter { /// \brief Root class for the filter node hierarchy /// /// \note When the function documentation for this class mentions "this node", this should be /// interpreted as "the node and all its children". class Node { // not implemented Node (const Node&); Node& operator= (const Node&); public: Node(); virtual ~Node(); virtual bool test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const = 0; ///< \return Can the specified table row pass through to filter? /// \param columns column ID to column index mapping virtual std::vector getReferencedColumns() const = 0; ///< Return a list of the IDs of the columns referenced by this node. The column mapping /// passed into test as columns must contain all columns listed here. virtual std::string toString (bool numericColumns) const = 0; ///< Return a string that represents this node. /// /// \param numericColumns Use numeric IDs instead of string to represent columns. }; } Q_DECLARE_METATYPE (boost::shared_ptr) #endif openmw-openmw-0.38.0/apps/opencs/model/filter/notnode.cpp000066400000000000000000000004431264522266000234110ustar00rootroot00000000000000#include "notnode.hpp" CSMFilter::NotNode::NotNode (boost::shared_ptr child) : UnaryNode (child, "not") {} bool CSMFilter::NotNode::test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const { return !getChild().test (table, row, columns); } openmw-openmw-0.38.0/apps/opencs/model/filter/notnode.hpp000066400000000000000000000010021264522266000234060ustar00rootroot00000000000000#ifndef CSM_FILTER_NOTNODE_H #define CSM_FILTER_NOTNODE_H #include "unarynode.hpp" namespace CSMFilter { class NotNode : public UnaryNode { public: NotNode (boost::shared_ptr child); virtual bool test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const; ///< \return Can the specified table row pass through to filter? /// \param columns column ID to column index mapping }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/ornode.cpp000066400000000000000000000006541264522266000232350ustar00rootroot00000000000000#include "ornode.hpp" #include CSMFilter::OrNode::OrNode (const std::vector >& nodes) : NAryNode (nodes, "or") {} bool CSMFilter::OrNode::test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const { int size = getSize(); for (int i=0; i >& nodes); virtual bool test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const; ///< \return Can the specified table row pass through to filter? /// \param columns column ID to column index mapping }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/parser.cpp000066400000000000000000000341051264522266000232410ustar00rootroot00000000000000#include "parser.hpp" #include #include #include #include #include "../world/columns.hpp" #include "../world/data.hpp" #include "../world/idcollection.hpp" #include "booleannode.hpp" #include "ornode.hpp" #include "andnode.hpp" #include "notnode.hpp" #include "textnode.hpp" #include "valuenode.hpp" namespace CSMFilter { struct Token { enum Type { Type_EOS, Type_None, Type_String, Type_Number, Type_Open, Type_Close, Type_OpenSquare, Type_CloseSquare, Type_Comma, Type_OneShot, Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously. Type_Keyword_False, Type_Keyword_And, Type_Keyword_Or, Type_Keyword_Not, Type_Keyword_Text, Type_Keyword_Value }; Type mType; std::string mString; double mNumber; Token (Type type = Type_None); Token (Type type, const std::string& string); ///< Non-string type that can also be interpreted as a string. Token (const std::string& string); Token (double number); operator bool() const; bool isString() const; }; Token::Token (Type type) : mType (type), mNumber(0.0) {} Token::Token (Type type, const std::string& string) : mType (type), mString (string), mNumber(0.0) {} Token::Token (const std::string& string) : mType (Type_String), mString (string), mNumber(0.0) {} Token::Token (double number) : mType (Type_Number), mNumber (number) {} bool Token::isString() const { return mType==Type_String || mType>=Type_Keyword_True; } Token::operator bool() const { return mType!=Type_None; } bool operator== (const Token& left, const Token& right) { if (left.mType!=right.mType) return false; switch (left.mType) { case Token::Type_String: return left.mString==right.mString; case Token::Type_Number: return left.mNumber==right.mNumber; default: return true; } } } CSMFilter::Token CSMFilter::Parser::getStringToken() { std::string string; int size = static_cast (mInput.size()); for (; mIndex1) { ++mIndex; break; } }; if (!string.empty()) { if (string[0]=='"' && (string[string.size()-1]!='"' || string.size()<2) ) { error(); return Token (Token::Type_None); } if (string[0]!='"' && string[string.size()-1]=='"') { error(); return Token (Token::Type_None); } if (string[0]=='"') return string.substr (1, string.size()-2); } return checkKeywords (string); } CSMFilter::Token CSMFilter::Parser::getNumberToken() { std::string string; int size = static_cast (mInput.size()); bool hasDecimalPoint = false; bool hasDigit = false; for (; mIndex> value; return value; } CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token) { static const char *sKeywords[] = { "true", "false", "and", "or", "not", "string", "value", 0 }; std::string string = Misc::StringUtils::lowerCase (token.mString); for (int i=0; sKeywords[i]; ++i) if (sKeywords[i]==string || (string.size()==1 && sKeywords[i][0]==string[0])) return Token (static_cast (i+Token::Type_Keyword_True), token.mString); return token; } CSMFilter::Token CSMFilter::Parser::getNextToken() { int size = static_cast (mInput.size()); char c = 0; for (; mIndex=size) return Token (Token::Type_EOS); switch (c) { case '(': ++mIndex; return Token (Token::Type_Open); case ')': ++mIndex; return Token (Token::Type_Close); case '[': ++mIndex; return Token (Token::Type_OpenSquare); case ']': ++mIndex; return Token (Token::Type_CloseSquare); case ',': ++mIndex; return Token (Token::Type_Comma); case '!': ++mIndex; return Token (Token::Type_OneShot); } if (c=='"' || c=='_' || std::isalpha (c) || c==':') return getStringToken(); if (c=='-' || c=='.' || std::isdigit (c)) return getNumberToken(); error(); return Token (Token::Type_None); } boost::shared_ptr CSMFilter::Parser::parseImp (bool allowEmpty, bool ignoreOneShot) { if (Token token = getNextToken()) { if (token==Token (Token::Type_OneShot)) token = getNextToken(); if (token) switch (token.mType) { case Token::Type_Keyword_True: return boost::shared_ptr (new BooleanNode (true)); case Token::Type_Keyword_False: return boost::shared_ptr (new BooleanNode (false)); case Token::Type_Keyword_And: case Token::Type_Keyword_Or: return parseNAry (token); case Token::Type_Keyword_Not: { boost::shared_ptr node = parseImp(); if (mError) return boost::shared_ptr(); return boost::shared_ptr (new NotNode (node)); } case Token::Type_Keyword_Text: return parseText(); case Token::Type_Keyword_Value: return parseValue(); case Token::Type_EOS: if (!allowEmpty) error(); return boost::shared_ptr(); default: error(); } } return boost::shared_ptr(); } boost::shared_ptr CSMFilter::Parser::parseNAry (const Token& keyword) { std::vector > nodes; Token token = getNextToken(); if (token.mType!=Token::Type_Open) { error(); return boost::shared_ptr(); } for (;;) { boost::shared_ptr node = parseImp(); if (mError) return boost::shared_ptr(); nodes.push_back (node); Token token = getNextToken(); if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma)) { error(); return boost::shared_ptr(); } if (token.mType==Token::Type_Close) break; } if (nodes.empty()) { error(); return boost::shared_ptr(); } switch (keyword.mType) { case Token::Type_Keyword_And: return boost::shared_ptr (new AndNode (nodes)); case Token::Type_Keyword_Or: return boost::shared_ptr (new OrNode (nodes)); default: error(); return boost::shared_ptr(); } } boost::shared_ptr CSMFilter::Parser::parseText() { Token token = getNextToken(); if (token.mType!=Token::Type_Open) { error(); return boost::shared_ptr(); } token = getNextToken(); if (!token) return boost::shared_ptr(); // parse column ID int columnId = -1; if (token.mType==Token::Type_Number) { if (static_cast (token.mNumber)==token.mNumber) columnId = static_cast (token.mNumber); } else if (token.isString()) { columnId = CSMWorld::Columns::getId (token.mString); } if (columnId<0) { error(); return boost::shared_ptr(); } token = getNextToken(); if (token.mType!=Token::Type_Comma) { error(); return boost::shared_ptr(); } // parse text pattern token = getNextToken(); if (!token.isString()) { error(); return boost::shared_ptr(); } std::string text = token.mString; token = getNextToken(); if (token.mType!=Token::Type_Close) { error(); return boost::shared_ptr(); } return boost::shared_ptr (new TextNode (columnId, text)); } boost::shared_ptr CSMFilter::Parser::parseValue() { Token token = getNextToken(); if (token.mType!=Token::Type_Open) { error(); return boost::shared_ptr(); } token = getNextToken(); if (!token) return boost::shared_ptr(); // parse column ID int columnId = -1; if (token.mType==Token::Type_Number) { if (static_cast (token.mNumber)==token.mNumber) columnId = static_cast (token.mNumber); } else if (token.isString()) { columnId = CSMWorld::Columns::getId (token.mString); } if (columnId<0) { error(); return boost::shared_ptr(); } token = getNextToken(); if (token.mType!=Token::Type_Comma) { error(); return boost::shared_ptr(); } // parse value double lower = 0; double upper = 0; ValueNode::Type lowerType = ValueNode::Type_Open; ValueNode::Type upperType = ValueNode::Type_Open; token = getNextToken(); if (token.mType==Token::Type_Number) { // single value lower = upper = token.mNumber; lowerType = upperType = ValueNode::Type_Closed; } else { // interval if (token.mType==Token::Type_OpenSquare) lowerType = ValueNode::Type_Closed; else if (token.mType!=Token::Type_CloseSquare && token.mType!=Token::Type_Open) { error(); return boost::shared_ptr(); } token = getNextToken(); if (token.mType==Token::Type_Number) { lower = token.mNumber; token = getNextToken(); if (token.mType!=Token::Type_Comma) { error(); return boost::shared_ptr(); } } else if (token.mType==Token::Type_Comma) { lowerType = ValueNode::Type_Infinite; } else { error(); return boost::shared_ptr(); } token = getNextToken(); if (token.mType==Token::Type_Number) { upper = token.mNumber; token = getNextToken(); } else upperType = ValueNode::Type_Infinite; if (token.mType==Token::Type_CloseSquare) { if (upperType!=ValueNode::Type_Infinite) upperType = ValueNode::Type_Closed; } else if (token.mType!=Token::Type_OpenSquare && token.mType!=Token::Type_Close) { error(); return boost::shared_ptr(); } } token = getNextToken(); if (token.mType!=Token::Type_Close) { error(); return boost::shared_ptr(); } return boost::shared_ptr (new ValueNode (columnId, lowerType, upperType, lower, upper)); } void CSMFilter::Parser::error() { mError = true; } CSMFilter::Parser::Parser (const CSMWorld::Data& data) : mIndex (0), mError (false), mData (data) {} bool CSMFilter::Parser::parse (const std::string& filter, bool allowPredefined) { // reset mFilter.reset(); mError = false; mInput = filter; mIndex = 0; Token token; if (allowPredefined) token = getNextToken(); if (allowPredefined && token==Token (Token::Type_EOS)) { mFilter.reset(); return true; } else if (!allowPredefined || token==Token (Token::Type_OneShot)) { boost::shared_ptr node = parseImp (true, token!=Token (Token::Type_OneShot)); if (mError) return false; if (getNextToken()!=Token (Token::Type_EOS)) { error(); return false; } if (node) mFilter = node; else { // Empty filter string equals to filter "true". mFilter.reset (new BooleanNode (true)); } return true; } // We do not use isString() here, because there could be a pre-defined filter with an ID that is // equal a filter keyword. else if (token.mType==Token::Type_String && allowPredefined) { if (getNextToken()!=Token (Token::Type_EOS)) { error(); return false; } int index = mData.getFilters().searchId (token.mString); if (index==-1) { error(); return false; } const CSMWorld::Record& record = mData.getFilters().getRecord (index); if (record.isDeleted()) { error(); return false; } return parse (record.get().mFilter, false); } else { error(); return false; } } boost::shared_ptr CSMFilter::Parser::getFilter() const { if (mError) throw std::logic_error ("No filter available"); return mFilter; } openmw-openmw-0.38.0/apps/opencs/model/filter/parser.hpp000066400000000000000000000025731264522266000232520ustar00rootroot00000000000000#ifndef CSM_FILTER_PARSER_H #define CSM_FILTER_PARSER_H #include #include "node.hpp" namespace CSMWorld { class Data; } namespace CSMFilter { struct Token; class Parser { boost::shared_ptr mFilter; std::string mInput; int mIndex; bool mError; const CSMWorld::Data& mData; Token getStringToken(); Token getNumberToken(); Token getNextToken(); Token checkKeywords (const Token& token); ///< Turn string token into keyword token, if possible. boost::shared_ptr parseImp (bool allowEmpty = false, bool ignoreOneShot = false); ///< Will return a null-pointer, if there is nothing more to parse. boost::shared_ptr parseNAry (const Token& keyword); boost::shared_ptr parseText(); boost::shared_ptr parseValue(); void error(); public: Parser (const CSMWorld::Data& data); bool parse (const std::string& filter, bool allowPredefined = true); ///< Discards any previous calls to parse /// /// \return Success? boost::shared_ptr getFilter() const; ///< Throws an exception if the last call to parse did not return true. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/textnode.cpp000066400000000000000000000042311264522266000235740ustar00rootroot00000000000000#include "textnode.hpp" #include #include #include #include "../world/columns.hpp" #include "../world/idtablebase.hpp" CSMFilter::TextNode::TextNode (int columnId, const std::string& text) : mColumnId (columnId), mText (text) {} bool CSMFilter::TextNode::test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const { const std::map::const_iterator iter = columns.find (mColumnId); if (iter==columns.end()) throw std::logic_error ("invalid column in text node test"); if (iter->second==-1) return true; QModelIndex index = table.index (row, iter->second); QVariant data = table.data (index); QString string; if (data.type()==QVariant::String) { string = data.toString(); } else if ((data.type()==QVariant::Int || data.type()==QVariant::UInt) && CSMWorld::Columns::hasEnums (static_cast (mColumnId))) { int value = data.toInt(); std::vector enums = CSMWorld::Columns::getEnums (static_cast (mColumnId)); if (value>=0 && value (enums.size())) string = QString::fromUtf8 (enums[value].c_str()); } else if (data.type()==QVariant::Bool) { string = data.toBool() ? "true" : "false"; } else if (mText.empty() && !data.isValid()) return true; else return false; /// \todo make pattern syntax configurable QRegExp regExp (QString::fromUtf8 (mText.c_str()), Qt::CaseInsensitive); return regExp.exactMatch (string); } std::vector CSMFilter::TextNode::getReferencedColumns() const { return std::vector (1, mColumnId); } std::string CSMFilter::TextNode::toString (bool numericColumns) const { std::ostringstream stream; stream << "text ("; if (numericColumns) stream << mColumnId; else stream << "\"" << CSMWorld::Columns::getName (static_cast (mColumnId)) << "\""; stream << ", \"" << mText << "\")"; return stream.str(); } openmw-openmw-0.38.0/apps/opencs/model/filter/textnode.hpp000066400000000000000000000020631264522266000236020ustar00rootroot00000000000000#ifndef CSM_FILTER_TEXTNODE_H #define CSM_FILTER_TEXTNODE_H #include "leafnode.hpp" namespace CSMFilter { class TextNode : public LeafNode { int mColumnId; std::string mText; public: TextNode (int columnId, const std::string& text); virtual bool test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const; ///< \return Can the specified table row pass through to filter? /// \param columns column ID to column index mapping virtual std::vector getReferencedColumns() const; ///< Return a list of the IDs of the columns referenced by this node. The column mapping /// passed into test as columns must contain all columns listed here. virtual std::string toString (bool numericColumns) const; ///< Return a string that represents this node. /// /// \param numericColumns Use numeric IDs instead of string to represent columns. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/unarynode.cpp000066400000000000000000000010631264522266000237460ustar00rootroot00000000000000#include "unarynode.hpp" CSMFilter::UnaryNode::UnaryNode (boost::shared_ptr child, const std::string& name) : mChild (child), mName (name) {} const CSMFilter::Node& CSMFilter::UnaryNode::getChild() const { return *mChild; } CSMFilter::Node& CSMFilter::UnaryNode::getChild() { return *mChild; } std::vector CSMFilter::UnaryNode::getReferencedColumns() const { return mChild->getReferencedColumns(); } std::string CSMFilter::UnaryNode::toString (bool numericColumns) const { return mName + " " + mChild->toString (numericColumns); } openmw-openmw-0.38.0/apps/opencs/model/filter/unarynode.hpp000066400000000000000000000016471264522266000237630ustar00rootroot00000000000000#ifndef CSM_FILTER_UNARYNODE_H #define CSM_FILTER_UNARYNODE_H #include #include "node.hpp" namespace CSMFilter { class UnaryNode : public Node { boost::shared_ptr mChild; std::string mName; public: UnaryNode (boost::shared_ptr child, const std::string& name); const Node& getChild() const; Node& getChild(); virtual std::vector getReferencedColumns() const; ///< Return a list of the IDs of the columns referenced by this node. The column mapping /// passed into test as columns must contain all columns listed here. virtual std::string toString (bool numericColumns) const; ///< Return a string that represents this node. /// /// \param numericColumns Use numeric IDs instead of string to represent columns. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/filter/valuenode.cpp000066400000000000000000000051001264522266000237200ustar00rootroot00000000000000#include "valuenode.hpp" #include #include #include "../world/columns.hpp" #include "../world/idtablebase.hpp" CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType, double lower, double upper) : mColumnId (columnId), mLower (lower), mUpper (upper), mLowerType (lowerType), mUpperType (upperType){} bool CSMFilter::ValueNode::test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const { const std::map::const_iterator iter = columns.find (mColumnId); if (iter==columns.end()) throw std::logic_error ("invalid column in value node test"); if (iter->second==-1) return true; QModelIndex index = table.index (row, iter->second); QVariant data = table.data (index); if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int && data.type()!=QVariant::UInt && data.type()!=static_cast (QMetaType::Float)) return false; double value = data.toDouble(); switch (mLowerType) { case Type_Closed: if (valuemUpper) return false; break; case Type_Open: if (value>=mUpper) return false; break; case Type_Infinite: break; } return true; } std::vector CSMFilter::ValueNode::getReferencedColumns() const { return std::vector (1, mColumnId); } std::string CSMFilter::ValueNode::toString (bool numericColumns) const { std::ostringstream stream; stream << "value ("; if (numericColumns) stream << mColumnId; else stream << "\"" << CSMWorld::Columns::getName (static_cast (mColumnId)) << "\""; stream << ", "; if (mLower==mUpper && mLowerType!=Type_Infinite && mUpperType!=Type_Infinite) stream << mLower; else { switch (mLowerType) { case Type_Closed: stream << "[" << mLower; break; case Type_Open: stream << "(" << mLower; break; case Type_Infinite: stream << "("; break; } stream << ", "; switch (mUpperType) { case Type_Closed: stream << mUpper << "]"; break; case Type_Open: stream << mUpper << ")"; break; case Type_Infinite: stream << ")"; break; } } stream << ")"; return stream.str(); } openmw-openmw-0.38.0/apps/opencs/model/filter/valuenode.hpp000066400000000000000000000025271264522266000237370ustar00rootroot00000000000000#ifndef CSM_FILTER_VALUENODE_H #define CSM_FILTER_VALUENODE_H #include "leafnode.hpp" namespace CSMFilter { class ValueNode : public LeafNode { public: enum Type { Type_Closed, Type_Open, Type_Infinite }; private: int mColumnId; std::string mText; double mLower; double mUpper; Type mLowerType; Type mUpperType; public: ValueNode (int columnId, Type lowerType, Type upperType, double lower, double upper); virtual bool test (const CSMWorld::IdTableBase& table, int row, const std::map& columns) const; ///< \return Can the specified table row pass through to filter? /// \param columns column ID to column index mapping virtual std::vector getReferencedColumns() const; ///< Return a list of the IDs of the columns referenced by this node. The column mapping /// passed into test as columns must contain all columns listed here. virtual std::string toString (bool numericColumns) const; ///< Return a string that represents this node. /// /// \param numericColumns Use numeric IDs instead of string to represent columns. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/prefs/000077500000000000000000000000001264522266000210705ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/model/prefs/boolsetting.cpp000066400000000000000000000024211264522266000241240ustar00rootroot00000000000000 #include "boolsetting.hpp" #include #include #include #include "category.hpp" #include "state.hpp" CSMPrefs::BoolSetting::BoolSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, bool default_) : Setting (parent, values, mutex, key, label), mDefault (default_) {} CSMPrefs::BoolSetting& CSMPrefs::BoolSetting::setTooltip (const std::string& tooltip) { mTooltip = tooltip; return *this; } std::pair CSMPrefs::BoolSetting::makeWidgets (QWidget *parent) { QCheckBox *widget = new QCheckBox (QString::fromUtf8 (getLabel().c_str()), parent); widget->setCheckState (mDefault ? Qt::Checked : Qt::Unchecked); if (!mTooltip.empty()) { QString tooltip = QString::fromUtf8 (mTooltip.c_str()); widget->setToolTip (tooltip); } connect (widget, SIGNAL (stateChanged (int)), this, SLOT (valueChanged (int))); return std::make_pair (static_cast (0), widget); } void CSMPrefs::BoolSetting::valueChanged (int value) { { QMutexLocker lock (getMutex()); getValues().setBool (getKey(), getParent()->getKey(), value); } getParent()->getState()->update (*this); } openmw-openmw-0.38.0/apps/opencs/model/prefs/boolsetting.hpp000066400000000000000000000012771264522266000241410ustar00rootroot00000000000000#ifndef CSM_PREFS_BOOLSETTING_H #define CSM_PREFS_BOOLSETTING_H #include "setting.hpp" namespace CSMPrefs { class BoolSetting : public Setting { Q_OBJECT std::string mTooltip; bool mDefault; public: BoolSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, bool default_); BoolSetting& setTooltip (const std::string& tooltip); /// Return label, input widget. virtual std::pair makeWidgets (QWidget *parent); private slots: void valueChanged (int value); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/prefs/category.cpp000066400000000000000000000020361264522266000234120ustar00rootroot00000000000000 #include "category.hpp" #include #include "setting.hpp" #include "state.hpp" CSMPrefs::Category::Category (State *parent, const std::string& key) : mParent (parent), mKey (key) {} const std::string& CSMPrefs::Category::getKey() const { return mKey; } CSMPrefs::State *CSMPrefs::Category::getState() const { return mParent; } void CSMPrefs::Category::addSetting (Setting *setting) { mSettings.push_back (setting); } CSMPrefs::Category::Iterator CSMPrefs::Category::begin() { return mSettings.begin(); } CSMPrefs::Category::Iterator CSMPrefs::Category::end() { return mSettings.end(); } CSMPrefs::Setting& CSMPrefs::Category::operator[] (const std::string& key) { for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter) if ((*iter)->getKey()==key) return **iter; throw std::logic_error ("Invalid user setting: " + key); } void CSMPrefs::Category::update() { for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter) mParent->update (**iter); } openmw-openmw-0.38.0/apps/opencs/model/prefs/category.hpp000066400000000000000000000014251264522266000234200ustar00rootroot00000000000000#ifndef CSM_PREFS_CATEGORY_H #define CSM_PREFS_CATEGORY_H #include #include namespace CSMPrefs { class State; class Setting; class Category { public: typedef std::vector Container; typedef Container::iterator Iterator; private: State *mParent; std::string mKey; Container mSettings; public: Category (State *parent, const std::string& key); const std::string& getKey() const; State *getState() const; void addSetting (Setting *setting); Iterator begin(); Iterator end(); Setting& operator[] (const std::string& key); void update(); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/prefs/coloursetting.cpp000066400000000000000000000027201264522266000244760ustar00rootroot00000000000000 #include "coloursetting.hpp" #include #include #include #include "../../view/widget/coloreditor.hpp" #include "category.hpp" #include "state.hpp" CSMPrefs::ColourSetting::ColourSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, QColor default_) : Setting (parent, values, mutex, key, label), mDefault (default_) {} CSMPrefs::ColourSetting& CSMPrefs::ColourSetting::setTooltip (const std::string& tooltip) { mTooltip = tooltip; return *this; } std::pair CSMPrefs::ColourSetting::makeWidgets (QWidget *parent) { QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent); CSVWidget::ColorEditor *widget = new CSVWidget::ColorEditor (mDefault, parent); if (!mTooltip.empty()) { QString tooltip = QString::fromUtf8 (mTooltip.c_str()); label->setToolTip (tooltip); widget->setToolTip (tooltip); } connect (widget, SIGNAL (pickingFinished()), this, SLOT (valueChanged())); return std::make_pair (label, widget); } void CSMPrefs::ColourSetting::valueChanged() { CSVWidget::ColorEditor& widget = dynamic_cast (*sender()); { QMutexLocker lock (getMutex()); getValues().setString (getKey(), getParent()->getKey(), widget.color().name().toUtf8().data()); } getParent()->getState()->update (*this); } openmw-openmw-0.38.0/apps/opencs/model/prefs/coloursetting.hpp000066400000000000000000000013461264522266000245060ustar00rootroot00000000000000#ifndef CSM_PREFS_COLOURSETTING_H #define CSM_PREFS_COLOURSETTING_H #include "setting.hpp" #include namespace CSMPrefs { class ColourSetting : public Setting { Q_OBJECT std::string mTooltip; QColor mDefault; public: ColourSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, QColor default_); ColourSetting& setTooltip (const std::string& tooltip); /// Return label, input widget. virtual std::pair makeWidgets (QWidget *parent); private slots: void valueChanged(); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/prefs/doublesetting.cpp000066400000000000000000000034661264522266000244550ustar00rootroot00000000000000 #include "doublesetting.hpp" #include #include #include #include #include #include "category.hpp" #include "state.hpp" CSMPrefs::DoubleSetting::DoubleSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, double default_) : Setting (parent, values, mutex, key, label), mMin (0), mMax (std::numeric_limits::max()), mDefault (default_) {} CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setRange (double min, double max) { mMin = min; mMax = max; return *this; } CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMin (double min) { mMin = min; return *this; } CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMax (double max) { mMax = max; return *this; } CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setTooltip (const std::string& tooltip) { mTooltip = tooltip; return *this; } std::pair CSMPrefs::DoubleSetting::makeWidgets (QWidget *parent) { QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent); QDoubleSpinBox *widget = new QDoubleSpinBox (parent); widget->setRange (mMin, mMax); widget->setValue (mDefault); if (!mTooltip.empty()) { QString tooltip = QString::fromUtf8 (mTooltip.c_str()); label->setToolTip (tooltip); widget->setToolTip (tooltip); } connect (widget, SIGNAL (valueChanged (double)), this, SLOT (valueChanged (double))); return std::make_pair (label, widget); } void CSMPrefs::DoubleSetting::valueChanged (double value) { { QMutexLocker lock (getMutex()); getValues().setFloat (getKey(), getParent()->getKey(), value); } getParent()->getState()->update (*this); } openmw-openmw-0.38.0/apps/opencs/model/prefs/doublesetting.hpp000066400000000000000000000017661264522266000244630ustar00rootroot00000000000000#ifndef CSM_PREFS_DOUBLESETTING_H #define CSM_PREFS_DOUBLESETTING_H #include "setting.hpp" namespace CSMPrefs { class DoubleSetting : public Setting { Q_OBJECT double mMin; double mMax; std::string mTooltip; double mDefault; public: DoubleSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, double default_); // defaults to [0, std::numeric_limits::max()] DoubleSetting& setRange (double min, double max); DoubleSetting& setMin (double min); DoubleSetting& setMax (double max); DoubleSetting& setTooltip (const std::string& tooltip); /// Return label, input widget. virtual std::pair makeWidgets (QWidget *parent); private slots: void valueChanged (double value); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/prefs/enumsetting.cpp000066400000000000000000000055331264522266000241440ustar00rootroot00000000000000 #include "enumsetting.hpp" #include #include #include #include #include "category.hpp" #include "state.hpp" CSMPrefs::EnumValue::EnumValue (const std::string& value, const std::string& tooltip) : mValue (value), mTooltip (tooltip) {} CSMPrefs::EnumValue::EnumValue (const char *value) : mValue (value) {} CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValues& values) { mValues.insert (mValues.end(), values.mValues.begin(), values.mValues.end()); return *this; } CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValue& value) { mValues.push_back (value); return *this; } CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const std::string& value, const std::string& tooltip) { mValues.push_back (EnumValue (value, tooltip)); return *this; } CSMPrefs::EnumSetting::EnumSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, const EnumValue& default_) : Setting (parent, values, mutex, key, label), mDefault (default_) {} CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::setTooltip (const std::string& tooltip) { mTooltip = tooltip; return *this; } CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValues (const EnumValues& values) { mValues.add (values); return *this; } CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const EnumValue& value) { mValues.add (value); return *this; } CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const std::string& value, const std::string& tooltip) { mValues.add (value, tooltip); return *this; } std::pair CSMPrefs::EnumSetting::makeWidgets (QWidget *parent) { QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent); QComboBox *widget = new QComboBox (parent); int index = 0; for (int i=0; i (mValues.mValues.size()); ++i) { if (mDefault.mValue==mValues.mValues[i].mValue) index = i; widget->addItem (QString::fromUtf8 (mValues.mValues[i].mValue.c_str())); if (!mValues.mValues[i].mTooltip.empty()) widget->setItemData (i, QString::fromUtf8 (mValues.mValues[i].mTooltip.c_str()), Qt::ToolTipRole); } widget->setCurrentIndex (index); if (!mTooltip.empty()) { QString tooltip = QString::fromUtf8 (mTooltip.c_str()); label->setToolTip (tooltip); } connect (widget, SIGNAL (currentIndexChanged (int)), this, SLOT (valueChanged (int))); return std::make_pair (label, widget); } void CSMPrefs::EnumSetting::valueChanged (int value) { { QMutexLocker lock (getMutex()); getValues().setString (getKey(), getParent()->getKey(), mValues.mValues.at (value).mValue); } getParent()->getState()->update (*this); } openmw-openmw-0.38.0/apps/opencs/model/prefs/enumsetting.hpp000066400000000000000000000027051264522266000241470ustar00rootroot00000000000000#ifndef CSM_PREFS_ENUMSETTING_H #define CSM_PREFS_ENUMSETTING_H #include #include "setting.hpp" namespace CSMPrefs { struct EnumValue { std::string mValue; std::string mTooltip; EnumValue (const std::string& value, const std::string& tooltip = ""); EnumValue (const char *value); }; struct EnumValues { std::vector mValues; EnumValues& add (const EnumValues& values); EnumValues& add (const EnumValue& value); EnumValues& add (const std::string& value, const std::string& tooltip); }; class EnumSetting : public Setting { Q_OBJECT std::string mTooltip; EnumValue mDefault; EnumValues mValues; public: EnumSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, const EnumValue& default_); EnumSetting& setTooltip (const std::string& tooltip); EnumSetting& addValues (const EnumValues& values); EnumSetting& addValue (const EnumValue& value); EnumSetting& addValue (const std::string& value, const std::string& tooltip); /// Return label, input widget. virtual std::pair makeWidgets (QWidget *parent); private slots: void valueChanged (int value); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/prefs/intsetting.cpp000066400000000000000000000033361264522266000237710ustar00rootroot00000000000000 #include "intsetting.hpp" #include #include #include #include #include #include "category.hpp" #include "state.hpp" CSMPrefs::IntSetting::IntSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, int default_) : Setting (parent, values, mutex, key, label), mMin (0), mMax (std::numeric_limits::max()), mDefault (default_) {} CSMPrefs::IntSetting& CSMPrefs::IntSetting::setRange (int min, int max) { mMin = min; mMax = max; return *this; } CSMPrefs::IntSetting& CSMPrefs::IntSetting::setMin (int min) { mMin = min; return *this; } CSMPrefs::IntSetting& CSMPrefs::IntSetting::setMax (int max) { mMax = max; return *this; } CSMPrefs::IntSetting& CSMPrefs::IntSetting::setTooltip (const std::string& tooltip) { mTooltip = tooltip; return *this; } std::pair CSMPrefs::IntSetting::makeWidgets (QWidget *parent) { QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent); QSpinBox *widget = new QSpinBox (parent); widget->setRange (mMin, mMax); widget->setValue (mDefault); if (!mTooltip.empty()) { QString tooltip = QString::fromUtf8 (mTooltip.c_str()); label->setToolTip (tooltip); widget->setToolTip (tooltip); } connect (widget, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); return std::make_pair (label, widget); } void CSMPrefs::IntSetting::valueChanged (int value) { { QMutexLocker lock (getMutex()); getValues().setInt (getKey(), getParent()->getKey(), value); } getParent()->getState()->update (*this); } openmw-openmw-0.38.0/apps/opencs/model/prefs/intsetting.hpp000066400000000000000000000016601264522266000237740ustar00rootroot00000000000000#ifndef CSM_PREFS_INTSETTING_H #define CSM_PREFS_INTSETTING_H #include "setting.hpp" namespace CSMPrefs { class IntSetting : public Setting { Q_OBJECT int mMin; int mMax; std::string mTooltip; int mDefault; public: IntSetting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label, int default_); // defaults to [0, std::numeric_limits::max()] IntSetting& setRange (int min, int max); IntSetting& setMin (int min); IntSetting& setMax (int max); IntSetting& setTooltip (const std::string& tooltip); /// Return label, input widget. virtual std::pair makeWidgets (QWidget *parent); private slots: void valueChanged (int value); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/prefs/setting.cpp000066400000000000000000000040731264522266000232550ustar00rootroot00000000000000 #include "setting.hpp" #include #include #include "category.hpp" #include "state.hpp" Settings::Manager& CSMPrefs::Setting::getValues() { return *mValues; } QMutex *CSMPrefs::Setting::getMutex() { return mMutex; } CSMPrefs::Setting::Setting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label) : QObject (parent->getState()), mParent (parent), mValues (values), mMutex (mutex), mKey (key), mLabel (label) {} CSMPrefs::Setting:: ~Setting() {} std::pair CSMPrefs::Setting::makeWidgets (QWidget *parent) { return std::pair (0, 0); } const CSMPrefs::Category *CSMPrefs::Setting::getParent() const { return mParent; } const std::string& CSMPrefs::Setting::getKey() const { return mKey; } const std::string& CSMPrefs::Setting::getLabel() const { return mLabel; } int CSMPrefs::Setting::toInt() const { QMutexLocker lock (mMutex); return mValues->getInt (mKey, mParent->getKey()); } double CSMPrefs::Setting::toDouble() const { QMutexLocker lock (mMutex); return mValues->getFloat (mKey, mParent->getKey()); } std::string CSMPrefs::Setting::toString() const { QMutexLocker lock (mMutex); return mValues->getString (mKey, mParent->getKey()); } bool CSMPrefs::Setting::isTrue() const { QMutexLocker lock (mMutex); return mValues->getBool (mKey, mParent->getKey()); } QColor CSMPrefs::Setting::toColor() const { // toString() handles lock return QColor (QString::fromUtf8 (toString().c_str())); } bool CSMPrefs::operator== (const Setting& setting, const std::string& key) { std::string fullKey = setting.getParent()->getKey() + "/" + setting.getKey(); return fullKey==key; } bool CSMPrefs::operator== (const std::string& key, const Setting& setting) { return setting==key; } bool CSMPrefs::operator!= (const Setting& setting, const std::string& key) { return !(setting==key); } bool CSMPrefs::operator!= (const std::string& key, const Setting& setting) { return !(key==setting); } openmw-openmw-0.38.0/apps/opencs/model/prefs/setting.hpp000066400000000000000000000032661264522266000232650ustar00rootroot00000000000000#ifndef CSM_PREFS_SETTING_H #define CSM_PREFS_SETTING_H #include #include #include class QWidget; class QColor; class QMutex; namespace Settings { class Manager; } namespace CSMPrefs { class Category; class Setting : public QObject { Q_OBJECT Category *mParent; Settings::Manager *mValues; QMutex *mMutex; std::string mKey; std::string mLabel; protected: Settings::Manager& getValues(); QMutex *getMutex(); public: Setting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label); virtual ~Setting(); /// Return label, input widget. /// /// \note first can be a 0-pointer, which means that the label is part of the input /// widget. virtual std::pair makeWidgets (QWidget *parent); const Category *getParent() const; const std::string& getKey() const; const std::string& getLabel() const; int toInt() const; double toDouble() const; std::string toString() const; bool isTrue() const; QColor toColor() const; }; // note: fullKeys have the format categoryKey/settingKey bool operator== (const Setting& setting, const std::string& fullKey); bool operator== (const std::string& fullKey, const Setting& setting); bool operator!= (const Setting& setting, const std::string& fullKey); bool operator!= (const std::string& fullKey, const Setting& setting); } #endif openmw-openmw-0.38.0/apps/opencs/model/prefs/state.cpp000066400000000000000000000375211264522266000227240ustar00rootroot00000000000000 #include "state.hpp" #include #include #include #include "intsetting.hpp" #include "doublesetting.hpp" #include "boolsetting.hpp" #include "coloursetting.hpp" CSMPrefs::State *CSMPrefs::State::sThis = 0; void CSMPrefs::State::load() { // default settings file boost::filesystem::path local = mConfigurationManager.getLocalPath() / mConfigFile; boost::filesystem::path global = mConfigurationManager.getGlobalPath() / mConfigFile; if (boost::filesystem::exists (local)) mSettings.loadDefault (local.string()); else if (boost::filesystem::exists (global)) mSettings.loadDefault (global.string()); else throw std::runtime_error ("No default settings file found! Make sure the file \"openmw-cs.cfg\" was properly installed."); // user settings file boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile; if (boost::filesystem::exists (user)) mSettings.loadUser (user.string()); } void CSMPrefs::State::declare() { declareCategory ("Windows"); declareInt ("default-width", "Default window width", 800). setTooltip ("Newly opened top-level windows will open with this width."). setMin (80); declareInt ("default-height", "Default window height", 600). setTooltip ("Newly opened top-level windows will open with this height."). setMin (80); declareBool ("show-statusbar", "Show Status Bar", true). setTooltip ("If a newly open top level window is showing status bars or not. " " Note that this does not affect existing windows."); declareSeparator(); declareBool ("reuse", "Reuse Subviews", true). setTooltip ("When a new subview is requested and a matching subview already " " exist, do not open a new subview and use the existing one instead."); declareInt ("max-subviews", "Maximum number of subviews per top-level window", 256). setTooltip ("If the maximum number is reached and a new subview is opened " "it will be placed into a new top-level window."). setRange (1, 256); declareBool ("hide-subview", "Hide single subview", false). setTooltip ("When a view contains only a single subview, hide the subview title " "bar and if this subview is closed also close the view (unless it is the last " "view for this document)"); declareInt ("minimum-width", "Minimum subview width", 325). setTooltip ("Minimum width of subviews."). setRange (50, 10000); declareSeparator(); EnumValue scrollbarOnly ("Scrollbar Only", "Simple addition of scrollbars, the view window " "does not grow automatically."); declareEnum ("mainwindow-scrollbar", "Horizontal scrollbar mode for main window.", scrollbarOnly). addValue (scrollbarOnly). addValue ("Grow Only", "The view window grows as subviews are added. No scrollbars."). addValue ("Grow then Scroll", "The view window grows. The scrollbar appears once it cannot grow any further."); declareBool ("grow-limit", "Grow Limit Screen", false). setTooltip ("When \"Grow then Scroll\" option is selected, the window size grows to" " the width of the virtual desktop. \nIf this option is selected the the window growth" "is limited to the current screen."); declareCategory ("Records"); EnumValue iconAndText ("Icon and Text"); EnumValues recordValues; recordValues.add (iconAndText).add ("Icon Only").add ("Text Only"); declareEnum ("status-format", "Modification status display format", iconAndText). addValues (recordValues); declareEnum ("type-format", "ID type display format", iconAndText). addValues (recordValues); declareCategory ("ID Tables"); EnumValue inPlaceEdit ("Edit in Place", "Edit the clicked cell"); EnumValue editRecord ("Edit Record", "Open a dialogue subview for the clicked record"); EnumValue view ("View", "Open a scene subview for the clicked record (not available everywhere)"); EnumValue editRecordAndClose ("Edit Record and Close"); EnumValues doubleClickValues; doubleClickValues.add (inPlaceEdit).add (editRecord).add (view).add ("Revert"). add ("Delete").add (editRecordAndClose). add ("View and Close", "Open a scene subview for the clicked record and close the table subview"); declareEnum ("double", "Double Click", inPlaceEdit).addValues (doubleClickValues); declareEnum ("double-s", "Shift Double Click", editRecord).addValues (doubleClickValues); declareEnum ("double-c", "Control Double Click", view).addValues (doubleClickValues); declareEnum ("double-sc", "Shift Control Double Click", editRecordAndClose).addValues (doubleClickValues); declareSeparator(); EnumValue jumpAndSelect ("Jump and Select", "Scroll new record into view and make it the selection"); declareEnum ("jump-to-added", "Action on adding or cloning a record", jumpAndSelect). addValue (jumpAndSelect). addValue ("Jump Only", "Scroll new record into view"). addValue ("No Jump", "No special action"); declareBool ("extended-config", "Manually specify affected record types for an extended delete/revert", false). setTooltip ("Delete and revert commands have an extended form that also affects " "associated records.\n\n" "If this option is enabled, types of affected records are selected " "manually before a command execution.\nOtherwise, all associated " "records are deleted/reverted immediately."); declareCategory ("ID Dialogues"); declareBool ("toolbar", "Show toolbar", true); declareCategory ("Reports"); EnumValue actionNone ("None"); EnumValue actionEdit ("Edit", "Open a table or dialogue suitable for addressing the listed report"); EnumValue actionRemove ("Remove", "Remove the report from the report table"); EnumValue actionEditAndRemove ("Edit And Remove", "Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table"); EnumValues reportValues; reportValues.add (actionNone).add (actionEdit).add (actionRemove).add (actionEditAndRemove); declareEnum ("double", "Double Click", actionEdit).addValues (reportValues); declareEnum ("double-s", "Shift Double Click", actionRemove).addValues (reportValues); declareEnum ("double-c", "Control Double Click", actionEditAndRemove).addValues (reportValues); declareEnum ("double-sc", "Shift Control Double Click", actionNone).addValues (reportValues); declareCategory ("Search & Replace"); declareInt ("char-before", "Characters before search string", 10). setTooltip ("Maximum number of character to display in search result before the searched text"); declareInt ("char-after", "Characters after search string", 10). setTooltip ("Maximum number of character to display in search result after the searched text"); declareBool ("auto-delete", "Delete row from result table after a successful replace", true); declareCategory ("Scripts"); declareBool ("show-linenum", "Show Line Numbers", true). setTooltip ("Show line numbers to the left of the script editor window." "The current row and column numbers of the text cursor are shown at the bottom."); declareBool ("mono-font", "Use monospace font", true); EnumValue warningsNormal ("Normal", "Report warnings as warning"); declareEnum ("warnings", "Warning Mode", warningsNormal). addValue ("Ignore", "Do not report warning"). addValue (warningsNormal). addValue ("Strcit", "Promote warning to an error"); declareBool ("toolbar", "Show toolbar", true); declareInt ("compile-delay", "Delay between updating of source errors", 100). setTooltip ("Delay in milliseconds"). setRange (0, 10000); declareInt ("error-height", "Initial height of the error panel", 100). setRange (100, 10000); declareSeparator(); declareColour ("colour-int", "Highlight Colour: Integer Literals", QColor ("darkmagenta")); declareColour ("colour-float", "Highlight Colour: Float Literals", QColor ("magenta")); declareColour ("colour-name", "Highlight Colour: Names", QColor ("grey")); declareColour ("colour-keyword", "Highlight Colour: Keywords", QColor ("red")); declareColour ("colour-special", "Highlight Colour: Special Characters", QColor ("darkorange")); declareColour ("colour-comment", "Highlight Colour: Comments", QColor ("green")); declareColour ("colour-id", "Highlight Colour: IDs", QColor ("blue")); declareCategory ("General Input"); declareBool ("cycle", "Cyclic next/previous", false). setTooltip ("When using next/previous functions at the last/first item of a " "list go to the first/last item"); declareCategory ("3D Scene Input"); EnumValue left ("Left Mouse-Button"); EnumValue cLeft ("Ctrl-Left Mouse-Button"); EnumValue right ("Right Mouse-Button"); EnumValue cRight ("Ctrl-Right Mouse-Button"); EnumValue middle ("Middle Mouse-Button"); EnumValue cMiddle ("Ctrl-Middle Mouse-Button"); EnumValues inputButtons; inputButtons.add (left).add (cLeft).add (right).add (cRight).add (middle).add (cMiddle); declareEnum ("p-navi", "Primary Camera Navigation Button", left).addValues (inputButtons); declareEnum ("s-navi", "Secondary Camera Navigation Button", cLeft).addValues (inputButtons); declareEnum ("p-edit", "Primary Editing Button", right).addValues (inputButtons); declareEnum ("s-edit", "Secondary Editing Button", cRight).addValues (inputButtons); declareEnum ("p-select", "Primary Selection Button", middle).addValues (inputButtons); declareEnum ("s-select", "Secondary Selection Button", cMiddle).addValues (inputButtons); declareSeparator(); declareBool ("context-select", "Context Sensitive Selection", false); declareDouble ("drag-factor", "Mouse sensitivity during drag operations", 1.0). setRange (0.001, 100.0); declareDouble ("drag-wheel-factor", "Mouse wheel sensitivity during drag operations", 1.0). setRange (0.001, 100.0); declareDouble ("drag-shift-factor", "Shift-acceleration factor during drag operations", 4.0). setTooltip ("Acceleration factor during drag operations while holding down shift"). setRange (0.001, 100.0); declareCategory ("Tooltips"); declareBool ("scene", "Show Tooltips in 3D scenes", true); declareBool ("scene-hide-basic", "Hide basic 3D scenes tooltips", false); declareInt ("scene-delay", "Tooltip delay in milliseconds", 500). setMin (1); } void CSMPrefs::State::declareCategory (const std::string& key) { std::map::iterator iter = mCategories.find (key); if (iter!=mCategories.end()) { mCurrentCategory = iter; } else { mCurrentCategory = mCategories.insert (std::make_pair (key, Category (this, key))).first; } } CSMPrefs::IntSetting& CSMPrefs::State::declareInt (const std::string& key, const std::string& label, int default_) { if (mCurrentCategory==mCategories.end()) throw std::logic_error ("no category for setting"); std::ostringstream stream; stream << default_; setDefault (key, stream.str()); default_ = mSettings.getInt (key, mCurrentCategory->second.getKey()); CSMPrefs::IntSetting *setting = new CSMPrefs::IntSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label, default_); mCurrentCategory->second.addSetting (setting); return *setting; } CSMPrefs::DoubleSetting& CSMPrefs::State::declareDouble (const std::string& key, const std::string& label, double default_) { if (mCurrentCategory==mCategories.end()) throw std::logic_error ("no category for setting"); std::ostringstream stream; stream << default_; setDefault (key, stream.str()); default_ = mSettings.getFloat (key, mCurrentCategory->second.getKey()); CSMPrefs::DoubleSetting *setting = new CSMPrefs::DoubleSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label, default_); mCurrentCategory->second.addSetting (setting); return *setting; } CSMPrefs::BoolSetting& CSMPrefs::State::declareBool (const std::string& key, const std::string& label, bool default_) { if (mCurrentCategory==mCategories.end()) throw std::logic_error ("no category for setting"); setDefault (key, default_ ? "true" : "false"); default_ = mSettings.getBool (key, mCurrentCategory->second.getKey()); CSMPrefs::BoolSetting *setting = new CSMPrefs::BoolSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label, default_); mCurrentCategory->second.addSetting (setting); return *setting; } CSMPrefs::EnumSetting& CSMPrefs::State::declareEnum (const std::string& key, const std::string& label, EnumValue default_) { if (mCurrentCategory==mCategories.end()) throw std::logic_error ("no category for setting"); setDefault (key, default_.mValue); default_.mValue = mSettings.getString (key, mCurrentCategory->second.getKey()); CSMPrefs::EnumSetting *setting = new CSMPrefs::EnumSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label, default_); mCurrentCategory->second.addSetting (setting); return *setting; } CSMPrefs::ColourSetting& CSMPrefs::State::declareColour (const std::string& key, const std::string& label, QColor default_) { if (mCurrentCategory==mCategories.end()) throw std::logic_error ("no category for setting"); setDefault (key, default_.name().toUtf8().data()); default_.setNamedColor (QString::fromUtf8 (mSettings.getString (key, mCurrentCategory->second.getKey()).c_str())); CSMPrefs::ColourSetting *setting = new CSMPrefs::ColourSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label, default_); mCurrentCategory->second.addSetting (setting); return *setting; } void CSMPrefs::State::declareSeparator() { if (mCurrentCategory==mCategories.end()) throw std::logic_error ("no category for setting"); CSMPrefs::Setting *setting = new CSMPrefs::Setting (&mCurrentCategory->second, &mSettings, &mMutex, "", ""); mCurrentCategory->second.addSetting (setting); } void CSMPrefs::State::setDefault (const std::string& key, const std::string& default_) { Settings::CategorySetting fullKey (mCurrentCategory->second.getKey(), key); Settings::CategorySettingValueMap::iterator iter = mSettings.mDefaultSettings.find (fullKey); if (iter==mSettings.mDefaultSettings.end()) mSettings.mDefaultSettings.insert (std::make_pair (fullKey, default_)); } CSMPrefs::State::State (const Files::ConfigurationManager& configurationManager) : mConfigFile ("openmw-cs.cfg"), mConfigurationManager (configurationManager), mCurrentCategory (mCategories.end()) { if (sThis) throw std::logic_error ("An instance of CSMPRefs::State already exists"); load(); declare(); sThis = this; } CSMPrefs::State::~State() { sThis = 0; } void CSMPrefs::State::save() { boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile; mSettings.saveUser (user.string()); } CSMPrefs::State::Iterator CSMPrefs::State::begin() { return mCategories.begin(); } CSMPrefs::State::Iterator CSMPrefs::State::end() { return mCategories.end(); } CSMPrefs::Category& CSMPrefs::State::operator[] (const std::string& key) { Iterator iter = mCategories.find (key); if (iter==mCategories.end()) throw std::logic_error ("Invalid user settings category: " + key); return iter->second; } void CSMPrefs::State::update (const Setting& setting) { emit (settingChanged (&setting)); } CSMPrefs::State& CSMPrefs::State::get() { if (!sThis) throw std::logic_error ("No instance of CSMPrefs::State"); return *sThis; } CSMPrefs::State& CSMPrefs::get() { return State::get(); } openmw-openmw-0.38.0/apps/opencs/model/prefs/state.hpp000066400000000000000000000047331264522266000227300ustar00rootroot00000000000000#ifndef CSV_PREFS_STATE_H #define CSM_PREFS_STATE_H #include #include #include #include #ifndef Q_MOC_RUN #include #endif #include #include "category.hpp" #include "setting.hpp" #include "enumsetting.hpp" class QColor; namespace CSMPrefs { class IntSetting; class DoubleSetting; class BoolSetting; class ColourSetting; /// \brief User settings state /// /// \note Access to the user settings is thread-safe once all declarations and loading has /// been completed. class State : public QObject { Q_OBJECT static State *sThis; public: typedef std::map Collection; typedef Collection::iterator Iterator; private: const std::string mConfigFile; const Files::ConfigurationManager& mConfigurationManager; Settings::Manager mSettings; Collection mCategories; Iterator mCurrentCategory; QMutex mMutex; // not implemented State (const State&); State& operator= (const State&); private: void load(); void declare(); void declareCategory (const std::string& key); IntSetting& declareInt (const std::string& key, const std::string& label, int default_); DoubleSetting& declareDouble (const std::string& key, const std::string& label, double default_); BoolSetting& declareBool (const std::string& key, const std::string& label, bool default_); EnumSetting& declareEnum (const std::string& key, const std::string& label, EnumValue default_); ColourSetting& declareColour (const std::string& key, const std::string& label, QColor default_); void declareSeparator(); void setDefault (const std::string& key, const std::string& default_); public: State (const Files::ConfigurationManager& configurationManager); ~State(); void save(); Iterator begin(); Iterator end(); Category& operator[](const std::string& key); void update (const Setting& setting); static State& get(); signals: void settingChanged (const CSMPrefs::Setting *setting); }; // convenience function State& get(); } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/000077500000000000000000000000001264522266000211115ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/model/tools/birthsigncheck.cpp000066400000000000000000000023731264522266000246110ustar00rootroot00000000000000#include "birthsigncheck.hpp" #include #include #include #include "../world/universalid.hpp" CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns) : mBirthsigns (birthsigns) {} int CSMTools::BirthsignCheckStage::setup() { return mBirthsigns.getSize(); } void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mBirthsigns.getRecord (stage); if (record.isDeleted()) return; const ESM::BirthSign& birthsign = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); // test for empty name, description and texture if (birthsign.mName.empty()) messages.push_back (std::make_pair (id, birthsign.mId + " has an empty name")); if (birthsign.mDescription.empty()) messages.push_back (std::make_pair (id, birthsign.mId + " has an empty description")); if (birthsign.mTexture.empty()) messages.push_back (std::make_pair (id, birthsign.mId + " is missing a texture")); /// \todo test if the texture exists /// \todo check data members that can't be edited in the table view } openmw-openmw-0.38.0/apps/opencs/model/tools/birthsigncheck.hpp000066400000000000000000000014111264522266000246060ustar00rootroot00000000000000#ifndef CSM_TOOLS_BIRTHSIGNCHECK_H #define CSM_TOOLS_BIRTHSIGNCHECK_H #include #include "../world/idcollection.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that birthsign records are internally consistent class BirthsignCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mBirthsigns; public: BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/bodypartcheck.cpp000066400000000000000000000033341264522266000244420ustar00rootroot00000000000000#include "bodypartcheck.hpp" CSMTools::BodyPartCheckStage::BodyPartCheckStage( const CSMWorld::IdCollection &bodyParts, const CSMWorld::Resources &meshes, const CSMWorld::IdCollection &races ) : mBodyParts(bodyParts), mMeshes(meshes), mRaces(races) { } int CSMTools::BodyPartCheckStage::setup() { return mBodyParts.getSize(); } void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &messages) { const CSMWorld::Record &record = mBodyParts.getRecord(stage); if ( record.isDeleted() ) return; const ESM::BodyPart &bodyPart = record.get(); CSMWorld::UniversalId id( CSMWorld::UniversalId::Type_BodyPart, bodyPart.mId ); // Check BYDT if (bodyPart.mData.mPart > 14 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." )); if (bodyPart.mData.mFlags > 3 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." )); if (bodyPart.mData.mType > 2 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." )); // Check MODL if ( bodyPart.mModel.empty() ) messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." )); else if ( mMeshes.searchId( bodyPart.mModel ) == -1 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." )); // Check FNAM if ( bodyPart.mRace.empty() ) messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." )); else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." )); } openmw-openmw-0.38.0/apps/opencs/model/tools/bodypartcheck.hpp000066400000000000000000000020671264522266000244510ustar00rootroot00000000000000#ifndef CSM_TOOLS_BODYPARTCHECK_H #define CSM_TOOLS_BODYPARTCHECK_H #include #include #include "../world/resources.hpp" #include "../world/idcollection.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that body part records are internally consistent class BodyPartCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection &mBodyParts; const CSMWorld::Resources &mMeshes; const CSMWorld::IdCollection &mRaces; public: BodyPartCheckStage( const CSMWorld::IdCollection &bodyParts, const CSMWorld::Resources &meshes, const CSMWorld::IdCollection &races ); virtual int setup(); ///< \return number of steps virtual void perform( int stage, CSMDoc::Messages &messages ); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/classcheck.cpp000066400000000000000000000037201264522266000237220ustar00rootroot00000000000000#include "classcheck.hpp" #include #include #include #include #include "../world/universalid.hpp" CSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection& classes) : mClasses (classes) {} int CSMTools::ClassCheckStage::setup() { return mClasses.getSize(); } void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mClasses.getRecord (stage); if (record.isDeleted()) return; const ESM::Class& class_ = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId); // test for empty name and description if (class_.mName.empty()) messages.push_back (std::make_pair (id, class_.mId + " has an empty name")); if (class_.mDescription.empty()) messages.push_back (std::make_pair (id, class_.mId + " has an empty description")); // test for invalid attributes for (int i=0; i<2; ++i) if (class_.mData.mAttribute[i]==-1) { std::ostringstream stream; stream << "Attribute #" << i << " of " << class_.mId << " is not set"; messages.push_back (std::make_pair (id, stream.str())); } if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { messages.push_back (std::make_pair (id, "Class lists same attribute twice")); } // test for non-unique skill std::map skills; // ID, number of occurrences for (int i=0; i<5; ++i) for (int i2=0; i2<2; ++i2) ++skills[class_.mData.mSkills[i][i2]]; for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) if (iter->second>1) { messages.push_back (std::make_pair (id, ESM::Skill::indexToId (iter->first) + " is listed more than once")); } } openmw-openmw-0.38.0/apps/opencs/model/tools/classcheck.hpp000066400000000000000000000013471264522266000237320ustar00rootroot00000000000000#ifndef CSM_TOOLS_CLASSCHECK_H #define CSM_TOOLS_CLASSCHECK_H #include #include "../world/idcollection.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that class records are internally consistent class ClassCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mClasses; public: ClassCheckStage (const CSMWorld::IdCollection& classes); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/factioncheck.cpp000066400000000000000000000032171264522266000242410ustar00rootroot00000000000000#include "factioncheck.hpp" #include #include #include #include #include "../world/universalid.hpp" CSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection& factions) : mFactions (factions) {} int CSMTools::FactionCheckStage::setup() { return mFactions.getSize(); } void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mFactions.getRecord (stage); if (record.isDeleted()) return; const ESM::Faction& faction = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId); // test for empty name if (faction.mName.empty()) messages.push_back (std::make_pair (id, faction.mId + " has an empty name")); // test for invalid attributes if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) { messages.push_back (std::make_pair (id , "Faction lists same attribute twice")); } // test for non-unique skill std::map skills; // ID, number of occurrences for (int i=0; i<7; ++i) if (faction.mData.mSkills[i]!=-1) ++skills[faction.mData.mSkills[i]]; for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) if (iter->second>1) { messages.push_back (std::make_pair (id, ESM::Skill::indexToId (iter->first) + " is listed more than once")); } /// \todo check data members that can't be edited in the table view } openmw-openmw-0.38.0/apps/opencs/model/tools/factioncheck.hpp000066400000000000000000000013671264522266000242520ustar00rootroot00000000000000#ifndef CSM_TOOLS_FACTIONCHECK_H #define CSM_TOOLS_FACTIONCHECK_H #include #include "../world/idcollection.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that faction records are internally consistent class FactionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mFactions; public: FactionCheckStage (const CSMWorld::IdCollection& factions); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/magiceffectcheck.cpp000066400000000000000000000110111264522266000250420ustar00rootroot00000000000000#include "magiceffectcheck.hpp" #include #include "../world/resources.hpp" #include "../world/data.hpp" namespace { void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) { if (!text.empty()) { messages.push_back(std::make_pair(id, text)); } } } bool CSMTools::MagicEffectCheckStage::isTextureExists(const std::string &texture, bool isIcon) const { const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; bool exists = false; if (textures.searchId(texture) != -1) { exists = true; } else { std::string ddsTexture = texture; if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1) { exists = true; } } return exists; } std::string CSMTools::MagicEffectCheckStage::checkReferenceable(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const { std::string error; if (!id.empty()) { CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id); if (index.first == -1) { error = "No such " + column + " '" + id + "'"; } else if (index.second != type.getType()) { error = column + " is not of type " + type.getTypeName(); } } return error; } std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const { std::string error; if (!id.empty() && mSounds.searchId(id) == -1) { error = "No such " + column + " '" + id + "'"; } return error; } CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, const CSMWorld::RefIdCollection &referenceables, const CSMWorld::Resources &icons, const CSMWorld::Resources &textures) : mMagicEffects(effects), mSounds(sounds), mReferenceables(referenceables), mIcons(icons), mTextures(textures) {} int CSMTools::MagicEffectCheckStage::setup() { return mMagicEffects.getSize(); } void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messages) { ESM::MagicEffect effect = mMagicEffects.getRecord(stage).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId); if (effect.mData.mBaseCost < 0.0f) { messages.push_back(std::make_pair(id, "Base Cost is negative")); } if (effect.mIcon.empty()) { messages.push_back(std::make_pair(id, "Icon is not specified")); } else if (!isTextureExists(effect.mIcon, true)) { messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'")); } if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false)) { messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'")); } addMessageIfNotEmpty(messages, id, checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object")); addMessageIfNotEmpty(messages, id, checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object")); addMessageIfNotEmpty(messages, id, checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object")); addMessageIfNotEmpty(messages, id, checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object")); addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound")); addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound")); addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound")); addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound")); if (effect.mDescription.empty()) { messages.push_back(std::make_pair(id, "Description is empty")); } } openmw-openmw-0.38.0/apps/opencs/model/tools/magiceffectcheck.hpp000066400000000000000000000035011264522266000250540ustar00rootroot00000000000000#ifndef CSM_TOOLS_MAGICEFFECTCHECK_HPP #define CSM_TOOLS_MAGICEFFECTCHECK_HPP #include #include #include "../world/idcollection.hpp" #include "../world/refidcollection.hpp" #include "../doc/stage.hpp" namespace CSMWorld { class Resources; } namespace CSMTools { /// \brief VerifyStage: make sure that magic effect records are internally consistent class MagicEffectCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection &mMagicEffects; const CSMWorld::IdCollection &mSounds; const CSMWorld::RefIdCollection &mReferenceables; const CSMWorld::Resources &mIcons; const CSMWorld::Resources &mTextures; private: bool isTextureExists(const std::string &texture, bool isIcon) const; std::string checkReferenceable(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const; std::string checkSound(const std::string &id, const std::string &column) const; public: MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, const CSMWorld::RefIdCollection &referenceables, const CSMWorld::Resources &icons, const CSMWorld::Resources &textures); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages &messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/mandatoryid.cpp000066400000000000000000000013101264522266000241230ustar00rootroot00000000000000#include "mandatoryid.hpp" #include "../world/collectionbase.hpp" #include "../world/record.hpp" CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::CollectionBase& idCollection, const CSMWorld::UniversalId& collectionId, const std::vector& ids) : mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids) {} int CSMTools::MandatoryIdStage::setup() { return mIds.size(); } void CSMTools::MandatoryIdStage::perform (int stage, CSMDoc::Messages& messages) { if (mIdCollection.searchId (mIds.at (stage))==-1 || mIdCollection.getRecord (mIds.at (stage)).isDeleted()) messages.add (mCollectionId, "Missing mandatory record: " + mIds.at (stage)); } openmw-openmw-0.38.0/apps/opencs/model/tools/mandatoryid.hpp000066400000000000000000000017051264522266000241400ustar00rootroot00000000000000#ifndef CSM_TOOLS_MANDATORYID_H #define CSM_TOOLS_MANDATORYID_H #include #include #include "../world/universalid.hpp" #include "../doc/stage.hpp" namespace CSMWorld { class CollectionBase; } namespace CSMTools { /// \brief Verify stage: make sure that records with specific IDs exist. class MandatoryIdStage : public CSMDoc::Stage { const CSMWorld::CollectionBase& mIdCollection; CSMWorld::UniversalId mCollectionId; std::vector mIds; public: MandatoryIdStage (const CSMWorld::CollectionBase& idCollection, const CSMWorld::UniversalId& collectionId, const std::vector& ids); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/mergeoperation.cpp000066400000000000000000000067111264522266000246420ustar00rootroot00000000000000 #include "mergeoperation.hpp" #include "../doc/state.hpp" #include "../doc/document.hpp" #include "mergestages.hpp" CSMTools::MergeOperation::MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding) : CSMDoc::Operation (CSMDoc::State_Merging, true), mState (document) { appendStage (new StartMergeStage (mState)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getGlobals)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getGmsts)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getSkills)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getClasses)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getFactions)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getRaces)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getSounds)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getScripts)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getRegions)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getBirthsigns)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getSpells)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getTopics)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getJournals)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getCells)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getFilters)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getEnchantments)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getBodyParts)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getDebugProfiles)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getSoundGens)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getMagicEffects)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getStartScripts)); appendStage (new MergeIdCollectionStage > (mState, &CSMWorld::Data::getPathgrids)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getTopicInfos)); appendStage (new MergeIdCollectionStage (mState, &CSMWorld::Data::getJournalInfos)); appendStage (new MergeRefIdsStage (mState)); appendStage (new MergeReferencesStage (mState)); appendStage (new MergeReferencesStage (mState)); appendStage (new ListLandTexturesMergeStage (mState)); appendStage (new MergeLandTexturesStage (mState)); appendStage (new MergeLandStage (mState)); appendStage (new FinishMergedDocumentStage (mState, encoding)); } void CSMTools::MergeOperation::setTarget (std::auto_ptr document) { mState.mTarget = document; } void CSMTools::MergeOperation::operationDone() { CSMDoc::Operation::operationDone(); if (mState.mCompleted) emit mergeDone (mState.mTarget.release()); } openmw-openmw-0.38.0/apps/opencs/model/tools/mergeoperation.hpp000066400000000000000000000016641264522266000246510ustar00rootroot00000000000000#ifndef CSM_TOOLS_MERGEOPERATION_H #define CSM_TOOLS_MERGEOPERATION_H #include #include #include "../doc/operation.hpp" #include "mergestate.hpp" namespace CSMDoc { class Document; } namespace CSMTools { class MergeOperation : public CSMDoc::Operation { Q_OBJECT MergeState mState; public: MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding); /// \attention Do not call this function while a merge is running. void setTarget (std::auto_ptr document); protected slots: virtual void operationDone(); signals: /// \attention When this signal is emitted, *this hands over the ownership of the /// document. This signal must be handled to avoid a leak. void mergeDone (CSMDoc::Document *document); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/mergestages.cpp000066400000000000000000000163031264522266000241260ustar00rootroot00000000000000 #include "mergestages.hpp" #include #include #include "mergestate.hpp" #include "../doc/document.hpp" #include "../world/data.hpp" CSMTools::StartMergeStage::StartMergeStage (MergeState& state) : mState (state) {} int CSMTools::StartMergeStage::setup() { return 1; } void CSMTools::StartMergeStage::perform (int stage, CSMDoc::Messages& messages) { mState.mCompleted = false; mState.mTextureIndices.clear(); } CSMTools::FinishMergedDocumentStage::FinishMergedDocumentStage (MergeState& state, ToUTF8::FromType encoding) : mState (state), mEncoder (encoding) {} int CSMTools::FinishMergedDocumentStage::setup() { return 1; } void CSMTools::FinishMergedDocumentStage::perform (int stage, CSMDoc::Messages& messages) { // We know that the content file list contains at least two entries and that the first one // does exist on disc (otherwise it would have been impossible to initiate a merge on that // document). boost::filesystem::path path = mState.mSource.getContentFiles()[0]; ESM::ESMReader reader; reader.setEncoder (&mEncoder); reader.open (path.string()); CSMWorld::MetaData source; source.mId = "sys::meta"; source.load (reader); CSMWorld::MetaData target = mState.mTarget->getData().getMetaData(); target.mAuthor = source.mAuthor; target.mDescription = source.mDescription; mState.mTarget->getData().setMetaData (target); mState.mCompleted = true; } CSMTools::MergeRefIdsStage::MergeRefIdsStage (MergeState& state) : mState (state) {} int CSMTools::MergeRefIdsStage::setup() { return mState.mSource.getData().getReferenceables().getSize(); } void CSMTools::MergeRefIdsStage::perform (int stage, CSMDoc::Messages& messages) { mState.mSource.getData().getReferenceables().copyTo ( stage, mState.mTarget->getData().getReferenceables()); } CSMTools::MergeReferencesStage::MergeReferencesStage (MergeState& state) : mState (state) {} int CSMTools::MergeReferencesStage::setup() { mIndex.clear(); return mState.mSource.getData().getReferences().getSize(); } void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mState.mSource.getData().getReferences().getRecord (stage); if (!record.isDeleted()) { CSMWorld::CellRef ref = record.get(); ref.mOriginalCell = ref.mCell; ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++; ref.mRefNum.mContentFile = 0; CSMWorld::Record newRecord ( CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref); mState.mTarget->getData().getReferences().appendRecord (newRecord); } } CSMTools::ListLandTexturesMergeStage::ListLandTexturesMergeStage (MergeState& state) : mState (state) {} int CSMTools::ListLandTexturesMergeStage::setup() { return mState.mSource.getData().getLand().getSize(); } void CSMTools::ListLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mState.mSource.getData().getLand().getRecord (stage); if (!record.isDeleted()) { const CSMWorld::Land& land = record.get(); // make sure record is loaded land.loadData (ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM); if (const ESM::Land::LandData *data = land.getLandData (ESM::Land::DATA_VTEX)) { // list texture indices std::pair key; key.second = land.mPlugin; for (int i=0; imTextures[i]; mState.mTextureIndices[key] = -1; } } } } CSMTools::MergeLandTexturesStage::MergeLandTexturesStage (MergeState& state) : mState (state), mNext (mState.mTextureIndices.end()) {} int CSMTools::MergeLandTexturesStage::setup() { // Should use the size of mState.mTextureIndices instead, but that is not available at this // point. Unless there are any errors in the land and land texture records this will not // make a difference. return mState.mSource.getData().getLandTextures().getSize(); } void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& messages) { if (stage==0) mNext = mState.mTextureIndices.begin(); bool found = false; do { if (mNext==mState.mTextureIndices.end()) return; mNext->second = stage+1; std::ostringstream stream; stream << mNext->first.first-1 << "_" << mNext->first.second; int index = mState.mSource.getData().getLandTextures().searchId (stream.str()); if (index!=-1) { CSMWorld::LandTexture texture = mState.mSource.getData().getLandTextures().getRecord (index).get(); std::ostringstream stream; stream << mNext->second-1 << "_0"; texture.mIndex = mNext->second-1; texture.mId = stream.str(); CSMWorld::Record newRecord ( CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture); mState.mTarget->getData().getLandTextures().appendRecord (newRecord); found = true; } ++mNext; } while (!found); } CSMTools::MergeLandStage::MergeLandStage (MergeState& state) : mState (state) {} int CSMTools::MergeLandStage::setup() { return mState.mSource.getData().getLand().getSize(); } void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mState.mSource.getData().getLand().getRecord (stage); if (!record.isDeleted()) { const CSMWorld::Land& land = record.get(); land.loadData (ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM); CSMWorld::Land newLand (land); newLand.mEsm = 0; // avoid potential dangling pointer (ESMReader isn't needed anyway, // because record is already fully loaded) newLand.mPlugin = 0; if (land.mDataTypes & ESM::Land::DATA_VTEX) { // adjust land texture references if (ESM::Land::LandData *data = newLand.getLandData()) { std::pair key; key.second = land.mPlugin; for (int i=0; imTextures[i]; std::map, int>::const_iterator iter = mState.mTextureIndices.find (key); if (iter!=mState.mTextureIndices.end()) data->mTextures[i] = iter->second; else data->mTextures[i] = 0; } } } CSMWorld::Record newRecord ( CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand); mState.mTarget->getData().getLand().appendRecord (newRecord); } } openmw-openmw-0.38.0/apps/opencs/model/tools/mergestages.hpp000066400000000000000000000114371264522266000241360ustar00rootroot00000000000000#ifndef CSM_TOOLS_MERGESTAGES_H #define CSM_TOOLS_MERGESTAGES_H #include #include #include #include "../doc/stage.hpp" #include "../world/data.hpp" #include "mergestate.hpp" namespace CSMTools { class StartMergeStage : public CSMDoc::Stage { MergeState& mState; public: StartMergeStage (MergeState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class FinishMergedDocumentStage : public CSMDoc::Stage { MergeState& mState; ToUTF8::Utf8Encoder mEncoder; public: FinishMergedDocumentStage (MergeState& state, ToUTF8::FromType encoding); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; template > class MergeIdCollectionStage : public CSMDoc::Stage { MergeState& mState; Collection& (CSMWorld::Data::*mAccessor)(); public: MergeIdCollectionStage (MergeState& state, Collection& (CSMWorld::Data::*accessor)()); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; template MergeIdCollectionStage::MergeIdCollectionStage (MergeState& state, Collection& (CSMWorld::Data::*accessor)()) : mState (state), mAccessor (accessor) {} template int MergeIdCollectionStage::setup() { return (mState.mSource.getData().*mAccessor)().getSize(); } template void MergeIdCollectionStage::perform (int stage, CSMDoc::Messages& messages) { const Collection& source = (mState.mSource.getData().*mAccessor)(); Collection& target = (mState.mTarget->getData().*mAccessor)(); const CSMWorld::Record& record = source.getRecord (stage); if (!record.isDeleted()) target.appendRecord (CSMWorld::Record (CSMWorld::RecordBase::State_ModifiedOnly, 0, &record.get())); } class MergeRefIdsStage : public CSMDoc::Stage { MergeState& mState; public: MergeRefIdsStage (MergeState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class MergeReferencesStage : public CSMDoc::Stage { MergeState& mState; std::map mIndex; public: MergeReferencesStage (MergeState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class ListLandTexturesMergeStage : public CSMDoc::Stage { MergeState& mState; public: ListLandTexturesMergeStage (MergeState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class MergeLandTexturesStage : public CSMDoc::Stage { MergeState& mState; std::map, int>::iterator mNext; public: MergeLandTexturesStage (MergeState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; class MergeLandStage : public CSMDoc::Stage { MergeState& mState; public: MergeLandStage (MergeState& state); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/mergestate.hpp000066400000000000000000000010111264522266000237530ustar00rootroot00000000000000#ifndef CSM_TOOLS_MERGESTATE_H #define CSM_TOOLS_MERGESTATE_H #include #include #include #include "../doc/document.hpp" namespace CSMTools { struct MergeState { std::auto_ptr mTarget; CSMDoc::Document& mSource; bool mCompleted; std::map, int> mTextureIndices; // (texture, content file) -> new texture MergeState (CSMDoc::Document& source) : mSource (source), mCompleted (false) {} }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/pathgridcheck.cpp000066400000000000000000000135051264522266000244210ustar00rootroot00000000000000#include "pathgridcheck.hpp" #include #include #include "../world/universalid.hpp" #include "../world/idcollection.hpp" #include "../world/subcellcollection.hpp" #include "../world/pathgrid.hpp" CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection& pathgrids) : mPathgrids (pathgrids) {} int CSMTools::PathgridCheckStage::setup() { return mPathgrids.getSize(); } void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mPathgrids.getRecord (stage); if (record.isDeleted()) return; const CSMWorld::Pathgrid& pathgrid = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Pathgrid, pathgrid.mId); // check the number of pathgrid points if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) messages.add (id, pathgrid.mId + " has less points than expected", "", CSMDoc::Message::Severity_Error); else if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) messages.add (id, pathgrid.mId + " has more points than expected", "", CSMDoc::Message::Severity_Error); std::vector pointList(pathgrid.mPoints.size()); std::vector duplList; for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i) { if (pathgrid.mEdges[i].mV0 < static_cast(pathgrid.mPoints.size()) && pathgrid.mEdges[i].mV0 >= 0) { pointList[pathgrid.mEdges[i].mV0].mConnectionNum++; // first check for duplicate edges unsigned int j = 0; for (; j < pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size(); ++j) { if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) { std::ostringstream ss; ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0 << " and " << pathgrid.mEdges[i].mV1; messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); break; } } // only add if not a duplicate if (j == pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size()) pointList[pathgrid.mEdges[i].mV0].mOtherIndex.push_back(pathgrid.mEdges[i].mV1); } else { std::ostringstream ss; ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0; messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); } } for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i) { // check the connection number for each point matches the edge connections if (pathgrid.mPoints[i].mConnectionNum > pointList[i].mConnectionNum) { std::ostringstream ss; ss << " has has less edges than expected for point " << i; messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); } else if (pathgrid.mPoints[i].mConnectionNum < pointList[i].mConnectionNum) { std::ostringstream ss; ss << " has has more edges than expected for point " << i; messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); } // check that edges are bidirectional bool foundReverse = false; for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j) { for (unsigned int k = 0; k < pointList[pointList[i].mOtherIndex[j]].mOtherIndex.size(); ++k) { if (pointList[pointList[i].mOtherIndex[j]].mOtherIndex[k] == static_cast(i)) { foundReverse = true; break; } } if (!foundReverse) { std::ostringstream ss; ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); } } // check duplicate points // FIXME: how to do this efficiently? for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) { if (j == i) continue; if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX && pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) { std::vector::const_iterator it = find(duplList.begin(), duplList.end(), i); if (it == duplList.end()) { std::ostringstream ss; ss << " has a duplicated point (" << i << ") x=" << pathgrid.mPoints[i].mX << ", y=" << pathgrid.mPoints[i].mY << ", z=" << pathgrid.mPoints[i].mZ; messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); duplList.push_back(i); break; } } } } // check pathgrid points that are not connected to anything for (unsigned int i = 0; i < pointList.size(); ++i) { if (pointList[i].mConnectionNum == 0) { std::ostringstream ss; ss << " has an orphaned point (" << i << ") x=" << pathgrid.mPoints[i].mX << ", y=" << pathgrid.mPoints[i].mY << ", z=" << pathgrid.mPoints[i].mZ; messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); } } // TODO: check whether there are disconnected graphs } openmw-openmw-0.38.0/apps/opencs/model/tools/pathgridcheck.hpp000066400000000000000000000016421264522266000244250ustar00rootroot00000000000000#ifndef CSM_TOOLS_PATHGRIDCHECK_H #define CSM_TOOLS_PATHGRIDCHECK_H #include "../world/collection.hpp" #include "../doc/stage.hpp" namespace CSMWorld { struct Pathgrid; template class SubCellCollection; } namespace CSMTools { struct Point { unsigned char mConnectionNum; std::vector mOtherIndex; Point() : mConnectionNum(0), mOtherIndex(0) {} }; class PathgridCheckStage : public CSMDoc::Stage { const CSMWorld::SubCellCollection >& mPathgrids; public: PathgridCheckStage (const CSMWorld::SubCellCollection >& pathgrids); virtual int setup(); virtual void perform (int stage, CSMDoc::Messages& messages); }; } #endif // CSM_TOOLS_PATHGRIDCHECK_H openmw-openmw-0.38.0/apps/opencs/model/tools/racecheck.cpp000066400000000000000000000042001264522266000235210ustar00rootroot00000000000000#include "racecheck.hpp" #include #include #include "../world/universalid.hpp" void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mRaces.getRecord (stage); if (record.isDeleted()) return; const ESM::Race& race = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); // test for empty name and description if (race.mName.empty()) messages.push_back (std::make_pair (id, race.mId + " has an empty name")); if (race.mDescription.empty()) messages.push_back (std::make_pair (id, race.mId + " has an empty description")); // test for positive height if (race.mData.mHeight.mMale<=0) messages.push_back (std::make_pair (id, "male " + race.mId + " has non-positive height")); if (race.mData.mHeight.mFemale<=0) messages.push_back (std::make_pair (id, "female " + race.mId + " has non-positive height")); // test for non-negative weight if (race.mData.mWeight.mMale<0) messages.push_back (std::make_pair (id, "male " + race.mId + " has negative weight")); if (race.mData.mWeight.mFemale<0) messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight")); // remember playable flag if (race.mData.mFlags & 0x1) mPlayable = true; /// \todo check data members that can't be edited in the table view } void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races); if (!mPlayable) messages.push_back (std::make_pair (id, "No playable race")); } CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection& races) : mRaces (races), mPlayable (false) {} int CSMTools::RaceCheckStage::setup() { mPlayable = false; return mRaces.getSize()+1; } void CSMTools::RaceCheckStage::perform (int stage, CSMDoc::Messages& messages) { if (stage==mRaces.getSize()) performFinal (messages); else performPerRecord (stage, messages); } openmw-openmw-0.38.0/apps/opencs/model/tools/racecheck.hpp000066400000000000000000000016011264522266000235300ustar00rootroot00000000000000#ifndef CSM_TOOLS_RACECHECK_H #define CSM_TOOLS_RACECHECK_H #include #include "../world/idcollection.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that race records are internally consistent class RaceCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mRaces; bool mPlayable; void performPerRecord (int stage, CSMDoc::Messages& messages); void performFinal (CSMDoc::Messages& messages); public: RaceCheckStage (const CSMWorld::IdCollection& races); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/referenceablecheck.cpp000066400000000000000000001046771264522266000254140ustar00rootroot00000000000000#include "referenceablecheck.hpp" #include #include "../world/record.hpp" #include "../world/universalid.hpp" CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& faction, const CSMWorld::IdCollection& scripts) : mReferencables(referenceable), mRaces(races), mClasses(classes), mFactions(faction), mScripts(scripts), mPlayerPresent(false) { } void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. const int bookSize(mReferencables.getBooks().getSize()); if (stage < bookSize) { bookCheck(stage, mReferencables.getBooks(), messages); return; } stage -= bookSize; const int activatorSize(mReferencables.getActivators().getSize()); if (stage < activatorSize) { activatorCheck(stage, mReferencables.getActivators(), messages); return; } stage -= activatorSize; const int potionSize(mReferencables.getPotions().getSize()); if (stage < potionSize) { potionCheck(stage, mReferencables.getPotions(), messages); return; } stage -= potionSize; const int apparatusSize(mReferencables.getApparati().getSize()); if (stage < apparatusSize) { apparatusCheck(stage, mReferencables.getApparati(), messages); return; } stage -= apparatusSize; const int armorSize(mReferencables.getArmors().getSize()); if (stage < armorSize) { armorCheck(stage, mReferencables.getArmors(), messages); return; } stage -= armorSize; const int clothingSize(mReferencables.getClothing().getSize()); if (stage < clothingSize) { clothingCheck(stage, mReferencables.getClothing(), messages); return; } stage -= clothingSize; const int containerSize(mReferencables.getContainers().getSize()); if (stage < containerSize) { containerCheck(stage, mReferencables.getContainers(), messages); return; } stage -= containerSize; const int doorSize(mReferencables.getDoors().getSize()); if (stage < doorSize) { doorCheck(stage, mReferencables.getDoors(), messages); return; } stage -= doorSize; const int ingredientSize(mReferencables.getIngredients().getSize()); if (stage < ingredientSize) { ingredientCheck(stage, mReferencables.getIngredients(), messages); return; } stage -= ingredientSize; const int creatureLevListSize(mReferencables.getCreatureLevelledLists().getSize()); if (stage < creatureLevListSize) { creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages); return; } stage -= creatureLevListSize; const int itemLevelledListSize(mReferencables.getItemLevelledList().getSize()); if (stage < itemLevelledListSize) { itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); return; } stage -= itemLevelledListSize; const int lightSize(mReferencables.getLights().getSize()); if (stage < lightSize) { lightCheck(stage, mReferencables.getLights(), messages); return; } stage -= lightSize; const int lockpickSize(mReferencables.getLocpicks().getSize()); if (stage < lockpickSize) { lockpickCheck(stage, mReferencables.getLocpicks(), messages); return; } stage -= lockpickSize; const int miscSize(mReferencables.getMiscellaneous().getSize()); if (stage < miscSize) { miscCheck(stage, mReferencables.getMiscellaneous(), messages); return; } stage -= miscSize; const int npcSize(mReferencables.getNPCs().getSize()); if (stage < npcSize) { npcCheck(stage, mReferencables.getNPCs(), messages); return; } stage -= npcSize; const int weaponSize(mReferencables.getWeapons().getSize()); if (stage < weaponSize) { weaponCheck(stage, mReferencables.getWeapons(), messages); return; } stage -= weaponSize; const int probeSize(mReferencables.getProbes().getSize()); if (stage < probeSize) { probeCheck(stage, mReferencables.getProbes(), messages); return; } stage -= probeSize; const int repairSize(mReferencables.getRepairs().getSize()); if (stage < repairSize) { repairCheck(stage, mReferencables.getRepairs(), messages); return; } stage -= repairSize; const int staticSize(mReferencables.getStatics().getSize()); if (stage < staticSize) { staticCheck(stage, mReferencables.getStatics(), messages); return; } stage -= staticSize; const int creatureSize(mReferencables.getCreatures().getSize()); if (stage < creatureSize) { creatureCheck(stage, mReferencables.getCreatures(), messages); return; } // if we come that far, we are about to perform our last, final check. finalCheck(messages); return; } int CSMTools::ReferenceableCheckStage::setup() { mPlayerPresent = false; return mReferencables.getSize() + 1; } void CSMTools::ReferenceableCheckStage::bookCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Book& book = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId); inventoryItemCheck(book, messages, id.toString(), true); // Check that mentioned scripts exist scriptCheck(book, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::activatorCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Activator& activator = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId); //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) messages.push_back (std::make_pair (id, activator.mId + " has no model")); // Check that mentioned scripts exist scriptCheck(activator, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::potionCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Potion >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Potion& potion = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); inventoryItemCheck(potion, messages, id.toString()); //IIRC potion can have empty effects list just fine. // Check that mentioned scripts exist scriptCheck(potion, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::apparatusCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Apparatus >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Apparatus& apparatus = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Apparatus, apparatus.mId); inventoryItemCheck(apparatus, messages, id.toString()); toolCheck(apparatus, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(apparatus, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::armorCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Armor >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Armor& armor = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Armor, armor.mId); inventoryItemCheck(armor, messages, id.toString(), true); //checking for armor class, armor should have poistive armor class, but 0 is considered legal if (armor.mData.mArmor < 0) messages.push_back (std::make_pair (id, armor.mId + " has negative armor class")); //checking for health. Only positive numbers are allowed, or 0 is illegal if (armor.mData.mHealth <= 0) messages.push_back (std::make_pair (id, armor.mId + " has non positive health")); // Check that mentioned scripts exist scriptCheck(armor, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::clothingCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Clothing >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Clothing& clothing = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId); inventoryItemCheck(clothing, messages, id.toString(), true); // Check that mentioned scripts exist scriptCheck(clothing, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::containerCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Container >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Container& container = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); //Checking for model, IIRC all containers should have a model if (container.mModel.empty()) messages.push_back (std::make_pair (id, container.mId + " has no model")); //Checking for capacity (weight) if (container.mWeight < 0) //0 is allowed messages.push_back (std::make_pair (id, container.mId + " has negative weight (capacity)")); //checking for name if (container.mName.empty()) messages.push_back (std::make_pair (id, container.mId + " has an empty name")); //checking contained items inventoryListCheck(container.mInventory.mList, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(container, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::creatureCheck ( int stage, const CSMWorld::RefIdDataContainer< ESM::Creature >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) return; const ESM::Creature& creature = (dynamic_cast&>(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); if (creature.mModel.empty()) messages.push_back (std::make_pair (id, creature.mId + " has no model")); if (creature.mName.empty()) messages.push_back (std::make_pair (id, creature.mId + " has an empty name")); //stats checks if (creature.mData.mLevel < 1) messages.push_back (std::make_pair (id, creature.mId + " has non-postive level")); if (creature.mData.mStrength < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative strength")); if (creature.mData.mIntelligence < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative intelligence")); if (creature.mData.mWillpower < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative willpower")); if (creature.mData.mAgility < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative agility")); if (creature.mData.mSpeed < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative speed")); if (creature.mData.mEndurance < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative endurance")); if (creature.mData.mPersonality < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative personality")); if (creature.mData.mLuck < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative luck")); if (creature.mData.mHealth < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative health")); if (creature.mData.mSoul < 0) messages.push_back (std::make_pair (id, creature.mId + " has negative soul value")); for (int i = 0; i < 6; ++i) { if (creature.mData.mAttack[i] < 0) { messages.push_back (std::make_pair (id, creature.mId + " has negative attack strength")); break; } } //TODO, find meaning of other values if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures messages.push_back (std::make_pair (id, creature.mId + " has negative gold ")); if (creature.mScale == 0) messages.push_back (std::make_pair (id, creature.mId + " has zero scale value")); // Check inventory inventoryListCheck(creature.mInventory.mList, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(creature, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::doorCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Door >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) return; const ESM::Door& door = (dynamic_cast&>(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, door.mId); //usual, name or model if (door.mName.empty()) messages.push_back (std::make_pair (id, door.mId + " has an empty name")); if (door.mModel.empty()) messages.push_back (std::make_pair (id, door.mId + " has no model")); // Check that mentioned scripts exist scriptCheck(door, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::ingredientCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Ingredient >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Ingredient& ingredient = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, ingredient.mId); inventoryItemCheck(ingredient, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(ingredient, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::creaturesLevListCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::CreatureLevList& CreatureLevList = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/ listCheck(CreatureLevList, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::itemLevelledListCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::ItemLevList& ItemLevList = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId); listCheck(ItemLevList, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::lightCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Light >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) return; const ESM::Light& light = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); if (light.mData.mRadius < 0) messages.push_back (std::make_pair (id, light.mId + " has negative light radius")); if (light.mData.mFlags & ESM::Light::Carry) { inventoryItemCheck(light, messages, id.toString()); if (light.mData.mTime == 0) messages.push_back (std::make_pair (id, light.mId + " has zero duration")); } // Check that mentioned scripts exist scriptCheck(light, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::lockpickCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Lockpick >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Lockpick& lockpick = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Lockpick, lockpick.mId); inventoryItemCheck(lockpick, messages, id.toString()); toolCheck(lockpick, messages, id.toString(), true); // Check that mentioned scripts exist scriptCheck(lockpick, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::miscCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Miscellaneous& miscellaneous = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId); inventoryItemCheck(miscellaneous, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(miscellaneous, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::npcCheck ( int stage, const CSMWorld::RefIdDataContainer< ESM::NPC >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) return; const ESM::NPC& npc = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Npc, npc.mId); short level(npc.mNpdt52.mLevel); char disposition(npc.mNpdt52.mDisposition); char reputation(npc.mNpdt52.mReputation); char rank(npc.mNpdt52.mRank); //Don't know what unknown is for int gold(npc.mNpdt52.mGold); //Detect if player is present if (Misc::StringUtils::ciEqual(npc.mId, "player")) //Happy now, scrawl? mPlayerPresent = true; if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag { messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happend? return; } level = npc.mNpdt12.mLevel; disposition = npc.mNpdt12.mDisposition; reputation = npc.mNpdt12.mReputation; rank = npc.mNpdt12.mRank; gold = npc.mNpdt12.mGold; } else { if (npc.mNpdt52.mAgility == 0) messages.push_back (std::make_pair (id, npc.mId + " agility has zero value")); if (npc.mNpdt52.mEndurance == 0) messages.push_back (std::make_pair (id, npc.mId + " endurance has zero value")); if (npc.mNpdt52.mIntelligence == 0) messages.push_back (std::make_pair (id, npc.mId + " intelligence has zero value")); if (npc.mNpdt52.mLuck == 0) messages.push_back (std::make_pair (id, npc.mId + " luck has zero value")); if (npc.mNpdt52.mPersonality == 0) messages.push_back (std::make_pair (id, npc.mId + " personality has zero value")); if (npc.mNpdt52.mStrength == 0) messages.push_back (std::make_pair (id, npc.mId + " strength has zero value")); if (npc.mNpdt52.mSpeed == 0) messages.push_back (std::make_pair (id, npc.mId + " speed has zero value")); if (npc.mNpdt52.mWillpower == 0) messages.push_back (std::make_pair (id, npc.mId + " willpower has zero value")); } if (level < 1) messages.push_back (std::make_pair (id, npc.mId + " level is non positive")); if (gold < 0) messages.push_back (std::make_pair (id, npc.mId + " gold has negative value")); if (npc.mName.empty()) messages.push_back (std::make_pair (id, npc.mId + " has any empty name")); if (npc.mClass.empty()) { messages.push_back (std::make_pair (id, npc.mId + " has any empty class")); } else if (mClasses.searchId (npc.mClass) == -1) { messages.push_back (std::make_pair (id, npc.mId + " has invalid class")); } if (npc.mRace.empty()) { messages.push_back (std::make_pair (id, npc.mId + " has any empty race")); } else if (mRaces.searchId (npc.mRace) == -1) { messages.push_back (std::make_pair (id, npc.mId + " has invalid race")); } if (disposition < 0) messages.push_back (std::make_pair (id, npc.mId + " has negative disposition")); if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid { messages.push_back (std::make_pair (id, npc.mId + " has negative reputation")); } if (!npc.mFaction.empty()) { if (rank < 0) messages.push_back (std::make_pair (id, npc.mId + " has negative rank")); if (mFactions.searchId(npc.mFaction) == -1) messages.push_back (std::make_pair (id, npc.mId + " has invalid faction")); } if (npc.mHead.empty()) messages.push_back (std::make_pair (id, npc.mId + " has no head")); if (npc.mHair.empty()) messages.push_back (std::make_pair (id, npc.mId + " has no hair")); //TODO: reputation, Disposition, rank, everything else // Check inventory inventoryListCheck(npc.mInventory.mList, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(npc, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::weaponCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Weapon >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord (stage); if (baseRecord.isDeleted()) return; const ESM::Weapon& weapon = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Weapon, weapon.mId); //TODO, It seems that this stuff for spellcasting is obligatory and In fact We should check if records are present if ( //THOSE ARE HARDCODED! !(weapon.mId == "VFX_Hands" || weapon.mId == "VFX_Absorb" || weapon.mId == "VFX_Reflect" || weapon.mId == "VFX_DefaultBolt" || //TODO I don't know how to get full list of effects :/ //DANGER!, ACHTUNG! FIXME! The following is the list of the magical bolts, valid for Morrowind.esm. However those are not hardcoded. weapon.mId == "magic_bolt" || weapon.mId == "shock_bolt" || weapon.mId == "shield_bolt" || weapon.mId == "VFX_DestructBolt" || weapon.mId == "VFX_PoisonBolt" || weapon.mId == "VFX_RestoreBolt" || weapon.mId == "VFX_AlterationBolt" || weapon.mId == "VFX_ConjureBolt" || weapon.mId == "VFX_FrostBolt" || weapon.mId == "VFX_MysticismBolt" || weapon.mId == "VFX_IllusionBolt" || weapon.mId == "VFX_Multiple2" || weapon.mId == "VFX_Multiple3" || weapon.mId == "VFX_Multiple4" || weapon.mId == "VFX_Multiple5" || weapon.mId == "VFX_Multiple6" || weapon.mId == "VFX_Multiple7" || weapon.mId == "VFX_Multiple8" || weapon.mId == "VFX_Multiple9")) { inventoryItemCheck(weapon, messages, id.toString(), true); if (!(weapon.mData.mType == ESM::Weapon::MarksmanBow || weapon.mData.mType == ESM::Weapon::MarksmanCrossbow || weapon.mData.mType == ESM::Weapon::MarksmanThrown || weapon.mData.mType == ESM::Weapon::Arrow || weapon.mData.mType == ESM::Weapon::Bolt)) { if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) messages.push_back (std::make_pair (id, weapon.mId + " has minimum slash damage higher than maximum")); if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) messages.push_back (std::make_pair (id, weapon.mId + " has minimum thrust damage higher than maximum")); } if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) messages.push_back (std::make_pair (id, weapon.mId + " has minimum chop damage higher than maximum")); if (!(weapon.mData.mType == ESM::Weapon::Arrow || weapon.mData.mType == ESM::Weapon::Bolt || weapon.mData.mType == ESM::Weapon::MarksmanThrown)) { //checking of health if (weapon.mData.mHealth <= 0) messages.push_back (std::make_pair (id, weapon.mId + " has non-positivie health")); if (weapon.mData.mReach < 0) messages.push_back (std::make_pair (id, weapon.mId + " has negative reach")); } } // Check that mentioned scripts exist scriptCheck(weapon, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::probeCheck( int stage, const CSMWorld::RefIdDataContainer< ESM::Probe >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); if (baseRecord.isDeleted()) { return; } const ESM::Probe& probe = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Probe, probe.mId); inventoryItemCheck(probe, messages, id.toString()); toolCheck(probe, messages, id.toString(), true); // Check that mentioned scripts exist scriptCheck(probe, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::repairCheck ( int stage, const CSMWorld::RefIdDataContainer< ESM::Repair >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord (stage); if (baseRecord.isDeleted()) return; const ESM::Repair& repair = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Repair, repair.mId); inventoryItemCheck (repair, messages, id.toString()); toolCheck (repair, messages, id.toString(), true); // Check that mentioned scripts exist scriptCheck(repair, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::staticCheck ( int stage, const CSMWorld::RefIdDataContainer< ESM::Static >& records, CSMDoc::Messages& messages) { const CSMWorld::RecordBase& baseRecord = records.getRecord (stage); if (baseRecord.isDeleted()) return; const ESM::Static& staticElement = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId); if (staticElement.mModel.empty()) messages.push_back (std::make_pair (id, staticElement.mId + " has no model")); } //final check void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages) { if (!mPlayerPresent) messages.push_back (std::make_pair (CSMWorld::UniversalId::Type_Referenceables, "There is no player record")); } void CSMTools::ReferenceableCheckStage::inventoryListCheck( const std::vector& itemList, CSMDoc::Messages& messages, const std::string& id) { for (size_t i = 0; i < itemList.size(); ++i) { std::string itemName = itemList[i].mItem.toString(); CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); if (localIndex.first == -1) messages.push_back (std::make_pair (id, id + " contains non-existing item (" + itemName + ")")); else { // Needs to accomodate Containers, Creatures, and NPCs switch (localIndex.second) { case CSMWorld::UniversalId::Type_Potion: case CSMWorld::UniversalId::Type_Apparatus: case CSMWorld::UniversalId::Type_Armor: case CSMWorld::UniversalId::Type_Book: case CSMWorld::UniversalId::Type_Clothing: case CSMWorld::UniversalId::Type_Ingredient: case CSMWorld::UniversalId::Type_Light: case CSMWorld::UniversalId::Type_Lockpick: case CSMWorld::UniversalId::Type_Miscellaneous: case CSMWorld::UniversalId::Type_Probe: case CSMWorld::UniversalId::Type_Repair: case CSMWorld::UniversalId::Type_Weapon: case CSMWorld::UniversalId::Type_ItemLevelledList: break; default: messages.push_back (std::make_pair(id, id + " contains item of invalid type (" + itemName + ")")); } } } } //Templates begins here template void CSMTools::ReferenceableCheckStage::inventoryItemCheck ( const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable) { if (someItem.mName.empty()) messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); //Checking for weight if (someItem.mData.mWeight < 0) messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); //Checking for value if (someItem.mData.mValue < 0) messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); //checking for model if (someItem.mModel.empty()) messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); //checking for icon if (someItem.mIcon.empty()) messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); if (enchantable && someItem.mData.mEnchant < 0) messages.push_back (std::make_pair (someID, someItem.mId + " has negative enchantment")); } template void CSMTools::ReferenceableCheckStage::inventoryItemCheck ( const Item& someItem, CSMDoc::Messages& messages, const std::string& someID) { if (someItem.mName.empty()) messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); //Checking for weight if (someItem.mData.mWeight < 0) messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); //Checking for value if (someItem.mData.mValue < 0) messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); //checking for model if (someItem.mModel.empty()) messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); //checking for icon if (someItem.mIcon.empty()) messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken) { if (someTool.mData.mQuality <= 0) messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); if (canBeBroken && someTool.mData.mUses<=0) messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive uses count")); } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) { if (someTool.mData.mQuality <= 0) messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); } template void CSMTools::ReferenceableCheckStage::listCheck ( const List& someList, CSMDoc::Messages& messages, const std::string& someID) { for (unsigned i = 0; i < someList.mList.size(); ++i) { if (mReferencables.searchId(someList.mList[i].mId).first == -1) messages.push_back (std::make_pair (someID, someList.mId + " contains item without referencable")); if (someList.mList[i].mLevel < 1) messages.push_back (std::make_pair (someID, someList.mId + " contains item with non-positive level")); } } template void CSMTools::ReferenceableCheckStage::scriptCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) { if (!someTool.mScript.empty()) { if (mScripts.searchId(someTool.mScript) == -1) messages.push_back (std::make_pair (someID, someTool.mId + " refers to an unknown script \""+someTool.mScript+"\"")); } } openmw-openmw-0.38.0/apps/opencs/model/tools/referenceablecheck.hpp000066400000000000000000000131661264522266000254110ustar00rootroot00000000000000#ifndef REFERENCEABLECHECKSTAGE_H #define REFERENCEABLECHECKSTAGE_H #include "../world/universalid.hpp" #include "../doc/stage.hpp" #include "../world/data.hpp" #include "../world/refiddata.hpp" namespace CSMTools { class ReferenceableCheckStage : public CSMDoc::Stage { public: ReferenceableCheckStage (const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& factions, const CSMWorld::IdCollection& scripts); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); private: //CONCRETE CHECKS void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, CSMDoc::Messages& messages); void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, CSMDoc::Messages& messages); void potionCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void armorCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void clothingCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void containerCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void creatureCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void doorCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void lightCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void miscCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void npcCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void weaponCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void probeCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void repairCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); void staticCheck(int stage, const CSMWorld::RefIdDataContainer& records, CSMDoc::Messages& messages); //FINAL CHECK void finalCheck (CSMDoc::Messages& messages); //Convenience functions void inventoryListCheck(const std::vector& itemList, CSMDoc::Messages& messages, const std::string& id); template void inventoryItemCheck(const ITEM& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable); //for all enchantable items. template void inventoryItemCheck(const ITEM& someItem, CSMDoc::Messages& messages, const std::string& someID); //for non-enchantable items. template void toolCheck(const TOOL& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canbebroken); //for tools with uses. template void toolCheck(const TOOL& someTool, CSMDoc::Messages& messages, const std::string& someID); //for tools without uses. template void listCheck(const LIST& someList, CSMDoc::Messages& messages, const std::string& someID); template void scriptCheck(const TOOL& someTool, CSMDoc::Messages& messages, const std::string& someID); const CSMWorld::RefIdData& mReferencables; const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; const CSMWorld::IdCollection& mFactions; const CSMWorld::IdCollection& mScripts; bool mPlayerPresent; }; } #endif // REFERENCEABLECHECKSTAGE_H openmw-openmw-0.38.0/apps/opencs/model/tools/referencecheck.cpp000066400000000000000000000106621264522266000245560ustar00rootroot00000000000000#include "referencecheck.hpp" #include CSMTools::ReferenceCheckStage::ReferenceCheckStage( const CSMWorld::RefCollection& references, const CSMWorld::RefIdCollection& referencables, const CSMWorld::IdCollection& cells, const CSMWorld::IdCollection& factions) : mReferences(references), mReferencables(referencables), mDataSet(referencables.getDataSet()), mCells(cells), mFactions(factions) { } void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &messages) { const CSMWorld::Record& record = mReferences.getRecord(stage); if (record.isDeleted()) return; const CSMWorld::CellRef& cellRef = record.get(); const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId); // Check for empty reference id if (cellRef.mRefID.empty()) { messages.push_back(std::make_pair(id, " is an empty instance (not based on an object)")); } else { // Check for non existing referenced object if (mReferencables.searchId(cellRef.mRefID) == -1) { messages.push_back(std::make_pair(id, " is referencing non existing object " + cellRef.mRefID)); } else { // Check if reference charge is valid for it's proper referenced type CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID); bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light; if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) { std::string str = " has invalid charge "; if (localIndex.second == CSMWorld::UniversalId::Type_Light) str += boost::lexical_cast(cellRef.mChargeFloat); else str += boost::lexical_cast(cellRef.mChargeInt); messages.push_back(std::make_pair(id, id.getId() + str)); } } } // If object have owner, check if that owner reference is valid if (!cellRef.mOwner.empty() && mReferencables.searchId(cellRef.mOwner) == -1) messages.push_back(std::make_pair(id, " has non existing owner " + cellRef.mOwner)); // If object have creature soul trapped, check if that creature reference is valid if (!cellRef.mSoul.empty()) if (mReferencables.searchId(cellRef.mSoul) == -1) messages.push_back(std::make_pair(id, " has non existing trapped soul " + cellRef.mSoul)); bool hasFaction = !cellRef.mFaction.empty(); // If object have faction, check if that faction reference is valid if (hasFaction) if (mFactions.searchId(cellRef.mFaction) == -1) messages.push_back(std::make_pair(id, " has non existing faction " + cellRef.mFaction)); // Check item's faction rank if (hasFaction && cellRef.mFactionRank < -1) messages.push_back(std::make_pair(id, " has faction set but has invalid faction rank " + boost::lexical_cast(cellRef.mFactionRank))); else if (!hasFaction && cellRef.mFactionRank != -2) messages.push_back(std::make_pair(id, " has invalid faction rank " + boost::lexical_cast(cellRef.mFactionRank))); // If door have destination cell, check if that reference is valid if (!cellRef.mDestCell.empty()) if (mCells.searchId(cellRef.mDestCell) == -1) messages.push_back(std::make_pair(id, " has non existing destination cell " + cellRef.mDestCell)); // Check if scale isn't negative if (cellRef.mScale < 0) { std::string str = " has negative scale "; str += boost::lexical_cast(cellRef.mScale); messages.push_back(std::make_pair(id, id.getId() + str)); } // Check if enchantement points aren't negative or are at full (-1) if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1) { std::string str = " has negative enchantment points "; str += boost::lexical_cast(cellRef.mEnchantmentCharge); messages.push_back(std::make_pair(id, id.getId() + str)); } // Check if gold value isn't negative if (cellRef.mGoldValue < 0) { std::string str = " has negative gold value "; str += cellRef.mGoldValue; messages.push_back(std::make_pair(id, id.getId() + str)); } } int CSMTools::ReferenceCheckStage::setup() { return mReferences.getSize(); } openmw-openmw-0.38.0/apps/opencs/model/tools/referencecheck.hpp000066400000000000000000000017221264522266000245600ustar00rootroot00000000000000#ifndef CSM_TOOLS_REFERENCECHECK_H #define CSM_TOOLS_REFERENCECHECK_H #include "../doc/state.hpp" #include "../doc/document.hpp" namespace CSMTools { class ReferenceCheckStage : public CSMDoc::Stage { public: ReferenceCheckStage (const CSMWorld::RefCollection& references, const CSMWorld::RefIdCollection& referencables, const CSMWorld::IdCollection& cells, const CSMWorld::IdCollection& factions); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); private: const CSMWorld::RefCollection& mReferences; const CSMWorld::RefIdCollection& mReferencables; const CSMWorld::RefIdData& mDataSet; const CSMWorld::IdCollection& mCells; const CSMWorld::IdCollection& mFactions; }; } #endif // CSM_TOOLS_REFERENCECHECK_H openmw-openmw-0.38.0/apps/opencs/model/tools/regioncheck.cpp000066400000000000000000000016571264522266000241070ustar00rootroot00000000000000#include "regioncheck.hpp" #include #include #include #include "../world/universalid.hpp" CSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection& regions) : mRegions (regions) {} int CSMTools::RegionCheckStage::setup() { return mRegions.getSize(); } void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mRegions.getRecord (stage); if (record.isDeleted()) return; const ESM::Region& region = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId); // test for empty name if (region.mName.empty()) messages.push_back (std::make_pair (id, region.mId + " has an empty name")); /// \todo test that the ID in mSleeplist exists /// \todo check data members that can't be edited in the table view } openmw-openmw-0.38.0/apps/opencs/model/tools/regioncheck.hpp000066400000000000000000000013561264522266000241100ustar00rootroot00000000000000#ifndef CSM_TOOLS_REGIONCHECK_H #define CSM_TOOLS_REGIONCHECK_H #include #include "../world/idcollection.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that region records are internally consistent class RegionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mRegions; public: RegionCheckStage (const CSMWorld::IdCollection& regions); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/reportmodel.cpp000066400000000000000000000104561264522266000241570ustar00rootroot00000000000000#include "reportmodel.hpp" #include #include #include "../world/columns.hpp" CSMTools::ReportModel::ReportModel (bool fieldColumn, bool severityColumn) : mColumnField (-1), mColumnSeverity (-1) { int index = 3; if (severityColumn) mColumnSeverity = index++; if (fieldColumn) mColumnField = index++; mColumnDescription = index; } int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const { if (parent.isValid()) return 0; return mRows.size(); } int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const { if (parent.isValid()) return 0; return mColumnDescription+1; } QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const { if (role!=Qt::DisplayRole) return QVariant(); switch (index.column()) { case Column_Type: return static_cast (mRows.at (index.row()).mId.getType()); case Column_Id: { CSMWorld::UniversalId id = mRows.at (index.row()).mId; if (id.getArgumentType()==CSMWorld::UniversalId::ArgumentType_Id) return QString::fromUtf8 (id.getId().c_str()); return QString ("-"); } case Column_Hint: return QString::fromUtf8 (mRows.at (index.row()).mHint.c_str()); } if (index.column()==mColumnDescription) return QString::fromUtf8 (mRows.at (index.row()).mMessage.c_str()); if (index.column()==mColumnField) { std::string field; std::istringstream stream (mRows.at (index.row()).mHint); char type, ignore; int fieldIndex; if ((stream >> type >> ignore >> fieldIndex) && (type=='r' || type=='R')) { field = CSMWorld::Columns::getName ( static_cast (fieldIndex)); } return QString::fromUtf8 (field.c_str()); } if (index.column()==mColumnSeverity) { return QString::fromUtf8 ( CSMDoc::Message::toString (mRows.at (index.row()).mSeverity).c_str()); } return QVariant(); } QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orientation, int role) const { if (role!=Qt::DisplayRole) return QVariant(); if (orientation==Qt::Vertical) return QVariant(); switch (section) { case Column_Type: return "Type"; case Column_Id: return "ID"; } if (section==mColumnDescription) return "Description"; if (section==mColumnField) return "Field"; if (section==mColumnSeverity) return "Severity"; return "-"; } bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& parent) { if (parent.isValid()) return false; if (count>0) { beginRemoveRows (parent, row, row+count-1); mRows.erase (mRows.begin()+row, mRows.begin()+row+count); endRemoveRows(); } return true; } void CSMTools::ReportModel::add (const CSMDoc::Message& message) { beginInsertRows (QModelIndex(), mRows.size(), mRows.size()); mRows.push_back (message); endInsertRows(); } void CSMTools::ReportModel::flagAsReplaced (int index) { CSMDoc::Message& line = mRows.at (index); std::string hint = line.mHint; if (hint.empty() || hint[0]!='R') throw std::logic_error ("trying to flag message as replaced that is not replaceable"); hint[0] = 'r'; line.mHint = hint; emit dataChanged (this->index (index, 0), this->index (index, columnCount())); } const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const { return mRows.at (row).mId; } std::string CSMTools::ReportModel::getHint (int row) const { return mRows.at (row).mHint; } void CSMTools::ReportModel::clear() { if (!mRows.empty()) { beginRemoveRows (QModelIndex(), 0, mRows.size()-1); mRows.clear(); endRemoveRows(); } } int CSMTools::ReportModel::countErrors() const { int count = 0; for (std::vector::const_iterator iter (mRows.begin()); iter!=mRows.end(); ++iter) if (iter->mSeverity==CSMDoc::Message::Severity_Error || iter->mSeverity==CSMDoc::Message::Severity_SeriousError) ++count; return count; } openmw-openmw-0.38.0/apps/opencs/model/tools/reportmodel.hpp000066400000000000000000000031731264522266000241620ustar00rootroot00000000000000#ifndef CSM_TOOLS_REPORTMODEL_H #define CSM_TOOLS_REPORTMODEL_H #include #include #include #include "../doc/messages.hpp" #include "../world/universalid.hpp" namespace CSMTools { class ReportModel : public QAbstractTableModel { Q_OBJECT std::vector mRows; // Fixed columns enum Columns { Column_Type = 0, Column_Id = 1, Column_Hint = 2 }; // Configurable columns int mColumnDescription; int mColumnField; int mColumnSeverity; public: ReportModel (bool fieldColumn = false, bool severityColumn = true); virtual int rowCount (const QModelIndex & parent = QModelIndex()) const; virtual int columnCount (const QModelIndex & parent = QModelIndex()) const; virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); void add (const CSMDoc::Message& message); void flagAsReplaced (int index); const CSMWorld::UniversalId& getUniversalId (int row) const; std::string getHint (int row) const; void clear(); // Return number of messages with Error or SeriousError severity. int countErrors() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/scriptcheck.cpp000066400000000000000000000071511264522266000241230ustar00rootroot00000000000000#include "scriptcheck.hpp" #include #include #include #include #include #include "../doc/document.hpp" #include "../world/data.hpp" #include "../prefs/state.hpp" CSMDoc::Message::Severity CSMTools::ScriptCheckStage::getSeverity (Type type) { switch (type) { case WarningMessage: return CSMDoc::Message::Severity_Warning; case ErrorMessage: return CSMDoc::Message::Severity_Error; } return CSMDoc::Message::Severity_SeriousError; } void CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc, Type type) { std::ostringstream stream; CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); stream << "script " << mFile << ", line " << loc.mLine << ", column " << loc.mColumn << " (" << loc.mLiteral << "): " << message; std::ostringstream hintStream; hintStream << "l:" << loc.mLine << " " << loc.mColumn; mMessages->add (id, stream.str(), hintStream.str(), getSeverity (type)); } void CSMTools::ScriptCheckStage::report (const std::string& message, Type type) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); std::ostringstream stream; stream << "script " << mFile << ": " << message; mMessages->add (id, stream.str(), "", getSeverity (type)); } CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMDoc::Document& document) : mDocument (document), mContext (document.getData()), mMessages (0), mWarningMode (Mode_Ignore) { /// \todo add an option to configure warning mode setWarningsMode (0); Compiler::registerExtensions (mExtensions); mContext.setExtensions (&mExtensions); } int CSMTools::ScriptCheckStage::setup() { std::string warnings = CSMPrefs::get()["Scripts"]["warnings"].toString(); if (warnings=="Ignore") mWarningMode = Mode_Ignore; else if (warnings=="Normal") mWarningMode = Mode_Normal; else if (warnings=="Strict") mWarningMode = Mode_Strict; mContext.clear(); mMessages = 0; mId.clear(); Compiler::ErrorHandler::reset(); return mDocument.getData().getScripts().getSize(); } void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages) { mId = mDocument.getData().getScripts().getId (stage); if (mDocument.isBlacklisted ( CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, mId))) return; mMessages = &messages; switch (mWarningMode) { case Mode_Ignore: setWarningsMode (0); break; case Mode_Normal: setWarningsMode (1); break; case Mode_Strict: setWarningsMode (2); break; } try { const CSMWorld::Data& data = mDocument.getData(); mFile = data.getScripts().getRecord (stage).get().mId; std::istringstream input (data.getScripts().getRecord (stage).get().mScriptText); Compiler::Scanner scanner (*this, input, mContext.getExtensions()); Compiler::FileParser parser (*this, mContext); scanner.scan (parser); } catch (const Compiler::SourceException&) { // error has already been reported via error handler } catch (const std::exception& error) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); std::ostringstream stream; stream << "script " << mFile << ": " << error.what(); messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError); } mMessages = 0; } openmw-openmw-0.38.0/apps/opencs/model/tools/scriptcheck.hpp000066400000000000000000000027661264522266000241370ustar00rootroot00000000000000#ifndef CSM_TOOLS_SCRIPTCHECK_H #define CSM_TOOLS_SCRIPTCHECK_H #include #include #include "../doc/stage.hpp" #include "../world/scriptcontext.hpp" namespace CSMDoc { class Document; } namespace CSMTools { /// \brief VerifyStage: make sure that scripts compile class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler { enum WarningMode { Mode_Ignore, Mode_Normal, Mode_Strict }; const CSMDoc::Document& mDocument; Compiler::Extensions mExtensions; CSMWorld::ScriptContext mContext; std::string mId; std::string mFile; CSMDoc::Messages *mMessages; WarningMode mWarningMode; CSMDoc::Message::Severity getSeverity (Type type); virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type); ///< Report error to the user. virtual void report (const std::string& message, Type type); ///< Report a file related error public: ScriptCheckStage (const CSMDoc::Document& document); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/search.cpp000066400000000000000000000212141264522266000230620ustar00rootroot00000000000000#include "search.hpp" #include #include #include "../doc/messages.hpp" #include "../doc/document.hpp" #include "../world/idtablebase.hpp" #include "../world/columnbase.hpp" #include "../world/universalid.hpp" #include "../world/commands.hpp" void CSMTools::Search::searchTextCell (const CSMWorld::IdTableBase *model, const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const { // using QString here for easier handling of case folding. QString search = QString::fromUtf8 (mText.c_str()); QString text = model->data (index).toString(); int pos = 0; while ((pos = text.indexOf (search, pos, Qt::CaseInsensitive))!=-1) { std::ostringstream hint; hint << (writable ? 'R' : 'r') <<": " << model->getColumnId (index.column()) << " " << pos << " " << search.length(); messages.add (id, formatDescription (text, pos, search.length()).toUtf8().data(), hint.str()); pos += search.length(); } } void CSMTools::Search::searchRegExCell (const CSMWorld::IdTableBase *model, const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const { QString text = model->data (index).toString(); int pos = 0; while ((pos = mRegExp.indexIn (text, pos))!=-1) { int length = mRegExp.matchedLength(); std::ostringstream hint; hint << (writable ? 'R' : 'r') <<": " << model->getColumnId (index.column()) << " " << pos << " " << length; messages.add (id, formatDescription (text, pos, length).toUtf8().data(), hint.str()); pos += length; } } void CSMTools::Search::searchRecordStateCell (const CSMWorld::IdTableBase *model, const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const { if (writable) throw std::logic_error ("Record state can not be modified by search and replace"); int data = model->data (index).toInt(); if (data==mValue) { std::vector states = CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification); std::ostringstream message; message << states.at (data); std::ostringstream hint; hint << "r: " << model->getColumnId (index.column()); messages.add (id, message.str(), hint.str()); } } QString CSMTools::Search::formatDescription (const QString& description, int pos, int length) const { QString text (description); // split QString highlight = flatten (text.mid (pos, length)); QString before = flatten (mPaddingBefore>=pos ? text.mid (0, pos) : text.mid (pos-mPaddingBefore, mPaddingBefore)); QString after = flatten (text.mid (pos+length, mPaddingAfter)); // compensate for Windows nonsense text.remove ('\r'); // join text = before + "" + highlight + "" + after; // improve layout for single line display text.replace ("\n", "<CR>"); text.replace ('\t', ' '); return text; } QString CSMTools::Search::flatten (const QString& text) const { QString flat (text); flat.replace ("&", "&"); flat.replace ("<", "<"); return flat; } CSMTools::Search::Search() : mType (Type_None), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) {} CSMTools::Search::Search (Type type, const std::string& value) : mType (type), mText (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) { if (type!=Type_Text && type!=Type_Id) throw std::logic_error ("Invalid search parameter (string)"); } CSMTools::Search::Search (Type type, const QRegExp& value) : mType (type), mRegExp (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) { if (type!=Type_TextRegEx && type!=Type_IdRegEx) throw std::logic_error ("Invalid search parameter (RegExp)"); } CSMTools::Search::Search (Type type, int value) : mType (type), mValue (value), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) { if (type!=Type_RecordState) throw std::logic_error ("invalid search parameter (int)"); } void CSMTools::Search::configure (const CSMWorld::IdTableBase *model) { mColumns.clear(); int columns = model->columnCount(); for (int i=0; i ( model->headerData ( i, Qt::Horizontal, static_cast (CSMWorld::ColumnBase::Role_Display)).toInt()); bool consider = false; switch (mType) { case Type_Text: case Type_TextRegEx: if (CSMWorld::ColumnBase::isText (display) || CSMWorld::ColumnBase::isScript (display)) { consider = true; } break; case Type_Id: case Type_IdRegEx: if (CSMWorld::ColumnBase::isId (display) || CSMWorld::ColumnBase::isScript (display)) { consider = true; } break; case Type_RecordState: if (display==CSMWorld::ColumnBase::Display_RecordState) consider = true; break; case Type_None: break; } if (consider) mColumns.insert (i); } mIdColumn = model->findColumnIndex (CSMWorld::Columns::ColumnId_Id); mTypeColumn = model->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType); } void CSMTools::Search::searchRow (const CSMWorld::IdTableBase *model, int row, CSMDoc::Messages& messages) const { for (std::set::const_iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter) { QModelIndex index = model->index (row, *iter); CSMWorld::UniversalId::Type type = static_cast ( model->data (model->index (row, mTypeColumn)).toInt()); CSMWorld::UniversalId id ( type, model->data (model->index (row, mIdColumn)).toString().toUtf8().data()); bool writable = model->flags (index) & Qt::ItemIsEditable; switch (mType) { case Type_Text: case Type_Id: searchTextCell (model, index, id, writable, messages); break; case Type_TextRegEx: case Type_IdRegEx: searchRegExCell (model, index, id, writable, messages); break; case Type_RecordState: searchRecordStateCell (model, index, id, writable, messages); break; case Type_None: break; } } } void CSMTools::Search::setPadding (int before, int after) { mPaddingBefore = before; mPaddingAfter = after; } void CSMTools::Search::replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model, const CSMWorld::UniversalId& id, const std::string& messageHint, const std::string& replaceText) const { std::istringstream stream (messageHint.c_str()); char hint, ignore; int columnId, pos, length; if (stream >> hint >> ignore >> columnId >> pos >> length) { int column = model->findColumnIndex (static_cast (columnId)); QModelIndex index = model->getModelIndex (id.getId(), column); std::string text = model->data (index).toString().toUtf8().constData(); std::string before = text.substr (0, pos); std::string after = text.substr (pos+length); std::string newText = before + replaceText + after; document.getUndoStack().push ( new CSMWorld::ModifyCommand (*model, index, QString::fromUtf8 (newText.c_str()))); } } bool CSMTools::Search::verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model, const CSMWorld::UniversalId& id, const std::string& messageHint) const { CSMDoc::Messages messages (CSMDoc::Message::Severity_Info); int row = model->getModelIndex (id.getId(), model->findColumnIndex (CSMWorld::Columns::ColumnId_Id)).row(); searchRow (model, row, messages); for (CSMDoc::Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter) if (iter->mHint==messageHint) return true; return false; } openmw-openmw-0.38.0/apps/opencs/model/tools/search.hpp000066400000000000000000000055361264522266000231000ustar00rootroot00000000000000#ifndef CSM_TOOLS_SEARCH_H #define CSM_TOOLS_SEARCH_H #include #include #include #include class QModelIndex; namespace CSMDoc { class Messages; class Document; } namespace CSMWorld { class IdTableBase; class UniversalId; } namespace CSMTools { class Search { public: enum Type { Type_Text = 0, Type_TextRegEx = 1, Type_Id = 2, Type_IdRegEx = 3, Type_RecordState = 4, Type_None }; private: Type mType; std::string mText; QRegExp mRegExp; int mValue; std::set mColumns; int mIdColumn; int mTypeColumn; int mPaddingBefore; int mPaddingAfter; void searchTextCell (const CSMWorld::IdTableBase *model, const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const; void searchRegExCell (const CSMWorld::IdTableBase *model, const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const; void searchRecordStateCell (const CSMWorld::IdTableBase *model, const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const; QString formatDescription (const QString& description, int pos, int length) const; QString flatten (const QString& text) const; public: Search(); Search (Type type, const std::string& value); Search (Type type, const QRegExp& value); Search (Type type, int value); // Configure search for the specified model. void configure (const CSMWorld::IdTableBase *model); // Search row in \a model and store results in \a messages. // // \attention *this needs to be configured for \a model. void searchRow (const CSMWorld::IdTableBase *model, int row, CSMDoc::Messages& messages) const; void setPadding (int before, int after); // Configuring *this for the model is not necessary when calling this function. void replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model, const CSMWorld::UniversalId& id, const std::string& messageHint, const std::string& replaceText) const; // Check if model still matches search results. bool verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model, const CSMWorld::UniversalId& id, const std::string& messageHint) const; }; } Q_DECLARE_METATYPE (CSMTools::Search) #endif openmw-openmw-0.38.0/apps/opencs/model/tools/searchoperation.cpp000066400000000000000000000022141264522266000250020ustar00rootroot00000000000000#include "searchoperation.hpp" #include "../doc/state.hpp" #include "../doc/document.hpp" #include "../world/data.hpp" #include "../world/idtablebase.hpp" #include "searchstage.hpp" CSMTools::SearchOperation::SearchOperation (CSMDoc::Document& document) : CSMDoc::Operation (CSMDoc::State_Searching, false) { std::vector types = CSMWorld::UniversalId::listTypes ( CSMWorld::UniversalId::Class_RecordList | CSMWorld::UniversalId::Class_ResourceList ); for (std::vector::const_iterator iter (types.begin()); iter!=types.end(); ++iter) appendStage (new SearchStage (&dynamic_cast ( *document.getData().getTableModel (*iter)))); setDefaultSeverity (CSMDoc::Message::Severity_Info); } void CSMTools::SearchOperation::configure (const Search& search) { mSearch = search; } void CSMTools::SearchOperation::appendStage (SearchStage *stage) { CSMDoc::Operation::appendStage (stage); stage->setOperation (this); } const CSMTools::Search& CSMTools::SearchOperation::getSearch() const { return mSearch; } openmw-openmw-0.38.0/apps/opencs/model/tools/searchoperation.hpp000066400000000000000000000014701264522266000250120ustar00rootroot00000000000000#ifndef CSM_TOOLS_SEARCHOPERATION_H #define CSM_TOOLS_SEARCHOPERATION_H #include "../doc/operation.hpp" #include "search.hpp" namespace CSMDoc { class Document; } namespace CSMTools { class SearchStage; class SearchOperation : public CSMDoc::Operation { Search mSearch; public: SearchOperation (CSMDoc::Document& document); /// \attention Do not call this function while a search is running. void configure (const Search& search); void appendStage (SearchStage *stage); ///< The ownership of \a stage is transferred to *this. /// /// \attention Do no call this function while this Operation is running. const Search& getSearch() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/searchstage.cpp000066400000000000000000000011541264522266000241070ustar00rootroot00000000000000#include "searchstage.hpp" #include "../world/idtablebase.hpp" #include "searchoperation.hpp" CSMTools::SearchStage::SearchStage (const CSMWorld::IdTableBase *model) : mModel (model), mOperation (0) {} int CSMTools::SearchStage::setup() { if (mOperation) mSearch = mOperation->getSearch(); mSearch.configure (mModel); return mModel->rowCount(); } void CSMTools::SearchStage::perform (int stage, CSMDoc::Messages& messages) { mSearch.searchRow (mModel, stage, messages); } void CSMTools::SearchStage::setOperation (const SearchOperation *operation) { mOperation = operation; } openmw-openmw-0.38.0/apps/opencs/model/tools/searchstage.hpp000066400000000000000000000014271264522266000241170ustar00rootroot00000000000000#ifndef CSM_TOOLS_SEARCHSTAGE_H #define CSM_TOOLS_SEARCHSTAGE_H #include "../doc/stage.hpp" #include "search.hpp" namespace CSMWorld { class IdTableBase; } namespace CSMTools { class SearchOperation; class SearchStage : public CSMDoc::Stage { const CSMWorld::IdTableBase *mModel; Search mSearch; const SearchOperation *mOperation; public: SearchStage (const CSMWorld::IdTableBase *model); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this stage will be appended to \a messages. void setOperation (const SearchOperation *operation); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/skillcheck.cpp000066400000000000000000000020341264522266000237300ustar00rootroot00000000000000#include "skillcheck.hpp" #include #include #include "../world/universalid.hpp" CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection& skills) : mSkills (skills) {} int CSMTools::SkillCheckStage::setup() { return mSkills.getSize(); } void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mSkills.getRecord (stage); if (record.isDeleted()) return; const ESM::Skill& skill = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId); for (int i=0; i<4; ++i) if (skill.mData.mUseValue[i]<0) { std::ostringstream stream; stream << "Use value #" << i << " of " << skill.mId << " is negative"; messages.push_back (std::make_pair (id, stream.str())); } if (skill.mDescription.empty()) messages.push_back (std::make_pair (id, skill.mId + " has an empty description")); } openmw-openmw-0.38.0/apps/opencs/model/tools/skillcheck.hpp000066400000000000000000000013451264522266000237410ustar00rootroot00000000000000#ifndef CSM_TOOLS_SKILLCHECK_H #define CSM_TOOLS_SKILLCHECK_H #include #include "../world/idcollection.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that skill records are internally consistent class SkillCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSkills; public: SkillCheckStage (const CSMWorld::IdCollection& skills); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/soundcheck.cpp000066400000000000000000000015051264522266000237440ustar00rootroot00000000000000#include "soundcheck.hpp" #include #include #include "../world/universalid.hpp" CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection& sounds) : mSounds (sounds) {} int CSMTools::SoundCheckStage::setup() { return mSounds.getSize(); } void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mSounds.getRecord (stage); if (record.isDeleted()) return; const ESM::Sound& sound = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); if (sound.mData.mMinRange>sound.mData.mMaxRange) messages.push_back (std::make_pair (id, "Maximum range larger than minimum range")); /// \todo check, if the sound file exists } openmw-openmw-0.38.0/apps/opencs/model/tools/soundcheck.hpp000066400000000000000000000013451264522266000237530ustar00rootroot00000000000000#ifndef CSM_TOOLS_SOUNDCHECK_H #define CSM_TOOLS_SOUNDCHECK_H #include #include "../world/idcollection.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that sound records are internally consistent class SoundCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSounds; public: SoundCheckStage (const CSMWorld::IdCollection& sounds); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/soundgencheck.cpp000066400000000000000000000033561264522266000244440ustar00rootroot00000000000000#include "soundgencheck.hpp" #include #include "../world/refiddata.hpp" #include "../world/universalid.hpp" CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, const CSMWorld::IdCollection &sounds, const CSMWorld::RefIdCollection &referenceables) : mSoundGens(soundGens), mSounds(sounds), mReferenceables(referenceables) {} int CSMTools::SoundGenCheckStage::setup() { return mSoundGens.getSize(); } void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages) { const CSMWorld::Record &record = mSoundGens.getRecord(stage); if (record.isDeleted()) { return; } const ESM::SoundGenerator& soundGen = record.get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId); if (!soundGen.mCreature.empty()) { CSMWorld::RefIdData::LocalIndex creatureIndex = mReferenceables.getDataSet().searchId(soundGen.mCreature); if (creatureIndex.first == -1) { messages.push_back(std::make_pair(id, "No such creature '" + soundGen.mCreature + "'")); } else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature) { messages.push_back(std::make_pair(id, "'" + soundGen.mCreature + "' is not a creature")); } } if (soundGen.mSound.empty()) { messages.push_back(std::make_pair(id, "Sound is not specified")); } else if (mSounds.searchId(soundGen.mSound) == -1) { messages.push_back(std::make_pair(id, "No such sound '" + soundGen.mSound + "'")); } } openmw-openmw-0.38.0/apps/opencs/model/tools/soundgencheck.hpp000066400000000000000000000017751264522266000244540ustar00rootroot00000000000000#ifndef CSM_TOOLS_SOUNDGENCHECK_HPP #define CSM_TOOLS_SOUNDGENCHECK_HPP #include "../world/data.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that sound gen records are internally consistent class SoundGenCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection &mSoundGens; const CSMWorld::IdCollection &mSounds; const CSMWorld::RefIdCollection &mReferenceables; public: SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, const CSMWorld::IdCollection &sounds, const CSMWorld::RefIdCollection &referenceables); virtual int setup(); ///< \return number of steps virtual void perform(int stage, CSMDoc::Messages &messages); ///< Messages resulting from this stage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/spellcheck.cpp000066400000000000000000000020271264522266000237330ustar00rootroot00000000000000#include "spellcheck.hpp" #include #include #include #include "../world/universalid.hpp" CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection& spells) : mSpells (spells) {} int CSMTools::SpellCheckStage::setup() { return mSpells.getSize(); } void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mSpells.getRecord (stage); if (record.isDeleted()) return; const ESM::Spell& spell = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); // test for empty name and description if (spell.mName.empty()) messages.push_back (std::make_pair (id, spell.mId + " has an empty name")); // test for invalid cost values if (spell.mData.mCost<0) messages.push_back (std::make_pair (id, spell.mId + " has a negative spell costs")); /// \todo check data members that can't be edited in the table view } openmw-openmw-0.38.0/apps/opencs/model/tools/spellcheck.hpp000066400000000000000000000013451264522266000237420ustar00rootroot00000000000000#ifndef CSM_TOOLS_SPELLCHECK_H #define CSM_TOOLS_SPELLCHECK_H #include #include "../world/idcollection.hpp" #include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that spell records are internally consistent class SpellCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSpells; public: SpellCheckStage (const CSMWorld::IdCollection& spells); virtual int setup(); ///< \return number of steps virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/startscriptcheck.cpp000066400000000000000000000016431264522266000252010ustar00rootroot00000000000000#include "startscriptcheck.hpp" #include CSMTools::StartScriptCheckStage::StartScriptCheckStage ( const CSMWorld::IdCollection& startScripts, const CSMWorld::IdCollection& scripts) : mStartScripts (startScripts), mScripts (scripts) {} void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messages) { const CSMWorld::Record& record = mStartScripts.getRecord (stage); if (record.isDeleted()) return; std::string scriptId = record.get().mId; CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId); if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1) messages.push_back ( std::make_pair (id, "Start script " + scriptId + " does not exist")); } int CSMTools::StartScriptCheckStage::setup() { return mStartScripts.getSize(); } openmw-openmw-0.38.0/apps/opencs/model/tools/startscriptcheck.hpp000066400000000000000000000013571264522266000252100ustar00rootroot00000000000000#ifndef CSM_TOOLS_STARTSCRIPTCHECK_H #define CSM_TOOLS_STARTSCRIPTCHECK_H #include #include #include "../doc/stage.hpp" #include "../world/idcollection.hpp" namespace CSMTools { class StartScriptCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mStartScripts; const CSMWorld::IdCollection& mScripts; public: StartScriptCheckStage (const CSMWorld::IdCollection& startScripts, const CSMWorld::IdCollection& scripts); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/tools/tools.cpp000066400000000000000000000216071264522266000227630ustar00rootroot00000000000000#include "tools.hpp" #include #include "../doc/state.hpp" #include "../doc/operation.hpp" #include "../doc/document.hpp" #include "../world/data.hpp" #include "../world/universalid.hpp" #include "reportmodel.hpp" #include "mandatoryid.hpp" #include "skillcheck.hpp" #include "classcheck.hpp" #include "factioncheck.hpp" #include "racecheck.hpp" #include "soundcheck.hpp" #include "regioncheck.hpp" #include "birthsigncheck.hpp" #include "spellcheck.hpp" #include "referenceablecheck.hpp" #include "scriptcheck.hpp" #include "bodypartcheck.hpp" #include "referencecheck.hpp" #include "startscriptcheck.hpp" #include "searchoperation.hpp" #include "pathgridcheck.hpp" #include "soundgencheck.hpp" #include "magiceffectcheck.hpp" #include "mergeoperation.hpp" CSMDoc::OperationHolder *CSMTools::Tools::get (int type) { switch (type) { case CSMDoc::State_Verifying: return &mVerifier; case CSMDoc::State_Searching: return &mSearch; case CSMDoc::State_Merging: return &mMerge; } return 0; } const CSMDoc::OperationHolder *CSMTools::Tools::get (int type) const { return const_cast (this)->get (type); } CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() { if (!mVerifierOperation) { mVerifierOperation = new CSMDoc::Operation (CSMDoc::State_Verifying, false); connect (&mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect (&mVerifier, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool))); connect (&mVerifier, SIGNAL (reportMessage (const CSMDoc::Message&, int)), this, SLOT (verifierMessage (const CSMDoc::Message&, int))); std::vector mandatoryIds; // I want C++11, damn it! mandatoryIds.push_back ("Day"); mandatoryIds.push_back ("DaysPassed"); mandatoryIds.push_back ("GameHour"); mandatoryIds.push_back ("Month"); mandatoryIds.push_back ("PCRace"); mVerifierOperation->appendStage (new MandatoryIdStage (mData.getGlobals(), CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds)); mVerifierOperation->appendStage (new SkillCheckStage (mData.getSkills())); mVerifierOperation->appendStage (new ClassCheckStage (mData.getClasses())); mVerifierOperation->appendStage (new FactionCheckStage (mData.getFactions())); mVerifierOperation->appendStage (new RaceCheckStage (mData.getRaces())); mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds())); mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions())); mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts())); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); mVerifierOperation->appendStage (new ScriptCheckStage (mDocument)); mVerifierOperation->appendStage (new StartScriptCheckStage (mData.getStartScripts(), mData.getScripts())); mVerifierOperation->appendStage( new BodyPartCheckStage( mData.getBodyParts(), mData.getResources( CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )), mData.getRaces() )); mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids())); mVerifierOperation->appendStage (new SoundGenCheckStage (mData.getSoundGens(), mData.getSounds(), mData.getReferenceables())); mVerifierOperation->appendStage (new MagicEffectCheckStage (mData.getMagicEffects(), mData.getSounds(), mData.getReferenceables(), mData.getResources (CSMWorld::UniversalId::Type_Icons), mData.getResources (CSMWorld::UniversalId::Type_Textures))); mVerifier.setOperation (mVerifierOperation); } return &mVerifier; } CSMTools::Tools::Tools (CSMDoc::Document& document, ToUTF8::FromType encoding) : mDocument (document), mData (document.getData()), mVerifierOperation (0), mSearchOperation (0), mMergeOperation (0), mNextReportNumber (0), mEncoding (encoding) { // index 0: load error log mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel)); mActiveReports.insert (std::make_pair (CSMDoc::State_Loading, 0)); connect (&mSearch, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect (&mSearch, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool))); connect (&mSearch, SIGNAL (reportMessage (const CSMDoc::Message&, int)), this, SLOT (verifierMessage (const CSMDoc::Message&, int))); connect (&mMerge, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect (&mMerge, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool))); // don't need to connect report message, since there are no messages for merge } CSMTools::Tools::~Tools() { if (mVerifierOperation) { mVerifier.abortAndWait(); delete mVerifierOperation; } if (mSearchOperation) { mSearch.abortAndWait(); delete mSearchOperation; } if (mMergeOperation) { mMerge.abortAndWait(); delete mMergeOperation; } for (std::map::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter) delete iter->second; } CSMWorld::UniversalId CSMTools::Tools::runVerifier (const CSMWorld::UniversalId& reportId) { int reportNumber = reportId.getType()==CSMWorld::UniversalId::Type_VerificationResults ? reportId.getIndex() : mNextReportNumber++; if (mReports.find (reportNumber)==mReports.end()) mReports.insert (std::make_pair (reportNumber, new ReportModel)); mActiveReports[CSMDoc::State_Verifying] = reportNumber; getVerifier()->start(); return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, reportNumber); } CSMWorld::UniversalId CSMTools::Tools::newSearch() { mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true, false))); return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1); } void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Search& search) { mActiveReports[CSMDoc::State_Searching] = searchId.getIndex(); if (!mSearchOperation) { mSearchOperation = new SearchOperation (mDocument); mSearch.setOperation (mSearchOperation); } mSearchOperation->configure (search); mSearch.start(); } void CSMTools::Tools::runMerge (std::auto_ptr target) { // not setting an active report, because merge does not produce messages if (!mMergeOperation) { mMergeOperation = new MergeOperation (mDocument, mEncoding); mMerge.setOperation (mMergeOperation); connect (mMergeOperation, SIGNAL (mergeDone (CSMDoc::Document*)), this, SIGNAL (mergeDone (CSMDoc::Document*))); } target->flagAsDirty(); mMergeOperation->setTarget (target); mMerge.start(); } void CSMTools::Tools::abortOperation (int type) { if (CSMDoc::OperationHolder *operation = get (type)) operation->abort(); } int CSMTools::Tools::getRunningOperations() const { static const int sOperations[] = { CSMDoc::State_Verifying, CSMDoc::State_Searching, CSMDoc::State_Merging, -1 }; int result = 0; for (int i=0; sOperations[i]!=-1; ++i) if (const CSMDoc::OperationHolder *operation = get (sOperations[i])) if (operation->isRunning()) result |= sOperations[i]; return result; } CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id) { if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults && id.getType()!=CSMWorld::UniversalId::Type_LoadErrorLog && id.getType()!=CSMWorld::UniversalId::Type_Search) throw std::logic_error ("invalid request for report model: " + id.toString()); return mReports.at (id.getIndex()); } void CSMTools::Tools::verifierMessage (const CSMDoc::Message& message, int type) { std::map::iterator iter = mActiveReports.find (type); if (iter!=mActiveReports.end()) mReports[iter->second]->add (message); } openmw-openmw-0.38.0/apps/opencs/model/tools/tools.hpp000066400000000000000000000057211264522266000227670ustar00rootroot00000000000000#ifndef CSM_TOOLS_TOOLS_H #define CSM_TOOLS_TOOLS_H #include #include #include #include #include #include "../doc/operationholder.hpp" namespace CSMWorld { class Data; class UniversalId; } namespace CSMDoc { class Operation; class Document; } namespace CSMTools { class ReportModel; class Search; class SearchOperation; class MergeOperation; class Tools : public QObject { Q_OBJECT CSMDoc::Document& mDocument; CSMWorld::Data& mData; CSMDoc::Operation *mVerifierOperation; CSMDoc::OperationHolder mVerifier; SearchOperation *mSearchOperation; CSMDoc::OperationHolder mSearch; MergeOperation *mMergeOperation; CSMDoc::OperationHolder mMerge; std::map mReports; int mNextReportNumber; std::map mActiveReports; // type, report number ToUTF8::FromType mEncoding; // not implemented Tools (const Tools&); Tools& operator= (const Tools&); CSMDoc::OperationHolder *getVerifier(); CSMDoc::OperationHolder *get (int type); ///< Returns a 0-pointer, if operation hasn't been used yet. const CSMDoc::OperationHolder *get (int type) const; ///< Returns a 0-pointer, if operation hasn't been used yet. public: Tools (CSMDoc::Document& document, ToUTF8::FromType encoding); virtual ~Tools(); /// \param reportId If a valid VerificationResults ID, run verifier for the /// specified report instead of creating a new one. /// /// \return ID of the report for this verification run CSMWorld::UniversalId runVerifier (const CSMWorld::UniversalId& reportId = CSMWorld::UniversalId()); /// Return ID of the report for this search. CSMWorld::UniversalId newSearch(); void runSearch (const CSMWorld::UniversalId& searchId, const Search& search); void runMerge (std::auto_ptr target); void abortOperation (int type); ///< \attention The operation is not aborted immediately. int getRunningOperations() const; ReportModel *getReport (const CSMWorld::UniversalId& id); ///< The ownership of the returned report is not transferred. private slots: void verifierMessage (const CSMDoc::Message& message, int type); signals: void progress (int current, int max, int type); void done (int type, bool failed); /// \attention When this signal is emitted, *this hands over the ownership of the /// document. This signal must be handled to avoid a leak. void mergeDone (CSMDoc::Document *document); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/000077500000000000000000000000001264522266000211005ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/model/world/cell.cpp000066400000000000000000000005041264522266000225220ustar00rootroot00000000000000#include "cell.hpp" #include void CSMWorld::Cell::load (ESM::ESMReader &esm, bool &isDeleted) { ESM::Cell::load (esm, isDeleted, false); mId = mName; if (isExterior()) { std::ostringstream stream; stream << "#" << mData.mX << " " << mData.mY; mId = stream.str(); } } openmw-openmw-0.38.0/apps/opencs/model/world/cell.hpp000066400000000000000000000007531264522266000225350ustar00rootroot00000000000000#ifndef CSM_WOLRD_CELL_H #define CSM_WOLRD_CELL_H #include #include #include namespace CSMWorld { /// \brief Wrapper for Cell record /// /// \attention The mData.mX and mData.mY fields of the ESM::Cell struct are not used. /// Exterior cell coordinates are encoded in the cell ID. struct Cell : public ESM::Cell { std::string mId; void load (ESM::ESMReader &esm, bool &isDeleted); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/cellcoordinates.cpp000066400000000000000000000035351264522266000247640ustar00rootroot00000000000000#include "cellcoordinates.hpp" #include #include CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} int CSMWorld::CellCoordinates::getX() const { return mX; } int CSMWorld::CellCoordinates::getY() const { return mY; } CSMWorld::CellCoordinates CSMWorld::CellCoordinates::move (int x, int y) const { return CellCoordinates (mX + x, mY + y); } std::string CSMWorld::CellCoordinates::getId (const std::string& worldspace) const { // we ignore the worldspace for now, since there is only one (will change in 1.1) std::ostringstream stream; stream << "#" << mX << " " << mY; return stream.str(); } std::pair CSMWorld::CellCoordinates::fromId ( const std::string& id) { // no worldspace for now, needs to be changed for 1.1 if (!id.empty() && id[0]=='#') { int x, y; char ignore; std::istringstream stream (id); if (stream >> ignore >> x >> y) return std::make_pair (CellCoordinates (x, y), true); } return std::make_pair (CellCoordinates(), false); } bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right) { return left.getX()==right.getX() && left.getY()==right.getY(); } bool CSMWorld::operator!= (const CellCoordinates& left, const CellCoordinates& right) { return !(left==right); } bool CSMWorld::operator< (const CellCoordinates& left, const CellCoordinates& right) { if (left.getX()right.getX()) return false; return left.getY() #include #include namespace CSMWorld { class CellCoordinates { int mX; int mY; public: CellCoordinates(); CellCoordinates (int x, int y); int getX() const; int getY() const; CellCoordinates move (int x, int y) const; ///< Return a copy of *this, moved by the given offset. std::string getId (const std::string& worldspace) const; ///< Return the ID for the cell at these coordinates. /// \return first: CellCoordinates (or 0, 0 if cell does not have coordinates), /// second: is cell paged? /// /// \note The worldspace part of \a id is ignored static std::pair fromId (const std::string& id); }; bool operator== (const CellCoordinates& left, const CellCoordinates& right); bool operator!= (const CellCoordinates& left, const CellCoordinates& right); bool operator< (const CellCoordinates& left, const CellCoordinates& right); std::ostream& operator<< (std::ostream& stream, const CellCoordinates& coordiantes); } Q_DECLARE_METATYPE (CSMWorld::CellCoordinates) #endif openmw-openmw-0.38.0/apps/opencs/model/world/cellselection.cpp000066400000000000000000000033341264522266000244340ustar00rootroot00000000000000#include "cellselection.hpp" #include #include #include CSMWorld::CellSelection::Iterator CSMWorld::CellSelection::begin() const { return mCells.begin(); } CSMWorld::CellSelection::Iterator CSMWorld::CellSelection::end() const { return mCells.end(); } bool CSMWorld::CellSelection::add (const CellCoordinates& coordinates) { return mCells.insert (coordinates).second; } void CSMWorld::CellSelection::remove (const CellCoordinates& coordinates) { mCells.erase (coordinates); } bool CSMWorld::CellSelection::has (const CellCoordinates& coordinates) const { return mCells.find (coordinates)!=end(); } int CSMWorld::CellSelection::getSize() const { return mCells.size(); } CSMWorld::CellCoordinates CSMWorld::CellSelection::getCentre() const { if (mCells.empty()) throw std::logic_error ("call of getCentre on empty cell selection"); double x = 0; double y = 0; for (Iterator iter = begin(); iter!=end(); ++iter) { x += iter->getX(); y += iter->getY(); } x /= mCells.size(); y /= mCells.size(); Iterator closest = begin(); double distance = std::numeric_limits::max(); for (Iterator iter (begin()); iter!=end(); ++iter) { double deltaX = x - iter->getX(); double deltaY = y - iter->getY(); double delta = std::sqrt (deltaX * deltaX + deltaY * deltaY); if (deltamove (x, y)); mCells.swap (moved); } openmw-openmw-0.38.0/apps/opencs/model/world/cellselection.hpp000066400000000000000000000030531264522266000244370ustar00rootroot00000000000000#ifndef CSM_WOLRD_CELLSELECTION_H #define CSM_WOLRD_CELLSELECTION_H #include #include #include "cellcoordinates.hpp" namespace CSMWorld { /// \brief Selection of cells in a paged worldspace /// /// \note The CellSelection does not specify the worldspace it applies to. class CellSelection { public: typedef std::set Container; typedef Container::const_iterator Iterator; private: Container mCells; public: Iterator begin() const; Iterator end() const; bool add (const CellCoordinates& coordinates); ///< Ignored if the cell specified by \a coordinates is already part of the selection. /// /// \return Was a cell added to the collection? void remove (const CellCoordinates& coordinates); ///< ignored if the cell specified by \a coordinates is not part of the selection. bool has (const CellCoordinates& coordinates) const; ///< \return Is the cell specified by \a coordinates part of the selection? int getSize() const; ///< Return number of cells. CellCoordinates getCentre() const; ///< Return the selected cell that is closest to the geometric centre of the selection. /// /// \attention This function must not be called on selections that are empty. void move (int x, int y); }; } Q_DECLARE_METATYPE (CSMWorld::CellSelection) #endif openmw-openmw-0.38.0/apps/opencs/model/world/collection.hpp000066400000000000000000000377751264522266000237670ustar00rootroot00000000000000#ifndef CSM_WOLRD_COLLECTION_H #define CSM_WOLRD_COLLECTION_H #include #include #include #include #include #include #include #include #include "columnbase.hpp" #include "collectionbase.hpp" namespace CSMWorld { /// \brief Access to ID field in records template struct IdAccessor { std::string& getId (ESXRecordT& record); const std::string getId (const ESXRecordT& record) const; }; template std::string& IdAccessor::getId (ESXRecordT& record) { return record.mId; } template const std::string IdAccessor::getId (const ESXRecordT& record) const { return record.mId; } /// \brief Single-type record collection template > class Collection : public CollectionBase { public: typedef ESXRecordT ESXRecord; private: std::vector > mRecords; std::map mIndex; std::vector *> mColumns; // not implemented Collection (const Collection&); Collection& operator= (const Collection&); protected: const std::map& getIdMap() const; const std::vector >& getRecords() const; bool reorderRowsImp (int baseIndex, const std::vector& newOrder); ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// /// \return Success? public: Collection(); virtual ~Collection(); void add (const ESXRecordT& record); ///< Add a new record (modified) virtual int getSize() const; virtual std::string getId (int index) const; virtual int getIndex (const std::string& id) const; virtual int getColumns() const; virtual QVariant getData (int index, int column) const; virtual void setData (int index, int column, const QVariant& data); virtual const ColumnBase& getColumn (int column) const; virtual void merge(); ///< Merge modified into base. virtual void purge(); ///< Remove records that are flagged as erased. virtual void removeRows (int index, int count) ; virtual void appendBlankRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type); virtual int searchId (const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) virtual void replace (int index, const RecordBase& record); ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. virtual void appendRecord (const RecordBase& record, UniversalId::Type type = UniversalId::Type_None); ///< If the record type does not match, an exception is thrown. ///< \param type Will be ignored, unless the collection supports multiple record types virtual const Record& getRecord (const std::string& id) const; virtual const Record& getRecord (int index) const; virtual int getAppendIndex (const std::string& id, UniversalId::Type type = UniversalId::Type_None) const; ///< \param type Will be ignored, unless the collection supports multiple record types virtual std::vector getIds (bool listDeleted = true) const; ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list virtual void insertRecord (const RecordBase& record, int index, UniversalId::Type type = UniversalId::Type_None); ///< Insert record before index. /// /// If the record type does not match, an exception is thrown. /// /// If the index is invalid either generally (by being out of range) or for the particular /// record, an exception is thrown. virtual bool reorderRows (int baseIndex, const std::vector& newOrder); ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// /// \return Success? void addColumn (Column *column); void setRecord (int index, const Record& record); ///< \attention This function must not change the ID. NestableColumn *getNestableColumn (int column) const; }; template const std::map& Collection::getIdMap() const { return mIndex; } template const std::vector >& Collection::getRecords() const { return mRecords; } template bool Collection::reorderRowsImp (int baseIndex, const std::vector& newOrder) { if (!newOrder.empty()) { int size = static_cast (newOrder.size()); // check that all indices are present std::vector test (newOrder); std::sort (test.begin(), test.end()); if (*test.begin()!=0 || *--test.end()!=size-1) return false; // reorder records std::vector > buffer (size); for (int i=0; i::iterator iter (mIndex.begin()); iter!=mIndex.end(); ++iter) if (iter->second>=baseIndex && iter->secondsecond = newOrder.at (iter->second-baseIndex)+baseIndex; } return true; } template void Collection::cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type) { Record copy; copy.mModified = getRecord(origin).get(); copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; insertRecord(copy, getAppendIndex(destination, type)); } template Collection::Collection() {} template Collection::~Collection() { for (typename std::vector *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter) delete *iter; } template void Collection::add (const ESXRecordT& record) { std::string id = Misc::StringUtils::lowerCase (IdAccessorT().getId (record)); std::map::iterator iter = mIndex.find (id); if (iter==mIndex.end()) { Record record2; record2.mState = Record::State_ModifiedOnly; record2.mModified = record; insertRecord (record2, getAppendIndex (id)); } else { mRecords[iter->second].setModified (record); } } template int Collection::getSize() const { return mRecords.size(); } template std::string Collection::getId (int index) const { return IdAccessorT().getId (mRecords.at (index).get()); } template int Collection::getIndex (const std::string& id) const { int index = searchId (id); if (index==-1) throw std::runtime_error ("invalid ID: " + id); return index; } template int Collection::getColumns() const { return mColumns.size(); } template QVariant Collection::getData (int index, int column) const { return mColumns.at (column)->get (mRecords.at (index)); } template void Collection::setData (int index, int column, const QVariant& data) { return mColumns.at (column)->set (mRecords.at (index), data); } template const ColumnBase& Collection::getColumn (int column) const { return *mColumns.at (column); } template NestableColumn *Collection::getNestableColumn (int column) const { if (column < 0 || column >= static_cast(mColumns.size())) throw std::runtime_error("column index out of range"); return mColumns.at (column); } template void Collection::addColumn (Column *column) { mColumns.push_back (column); } template void Collection::merge() { for (typename std::vector >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter) iter->merge(); purge(); } template void Collection::purge() { int i = 0; while (i (mRecords.size())) { if (mRecords[i].isErased()) removeRows (i, 1); else ++i; } } template void Collection::removeRows (int index, int count) { mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count); typename std::map::iterator iter = mIndex.begin(); while (iter!=mIndex.end()) { if (iter->second>=index) { if (iter->second>=index+count) { iter->second -= count; ++iter; } else { mIndex.erase (iter++); } } else ++iter; } } template void Collection::appendBlankRecord (const std::string& id, UniversalId::Type type) { ESXRecordT record; IdAccessorT().getId (record) = id; record.blank(); Record record2; record2.mState = Record::State_ModifiedOnly; record2.mModified = record; insertRecord (record2, getAppendIndex (id, type), type); } template int Collection::searchId (const std::string& id) const { std::string id2 = Misc::StringUtils::lowerCase(id); std::map::const_iterator iter = mIndex.find (id2); if (iter==mIndex.end()) return -1; return iter->second; } template void Collection::replace (int index, const RecordBase& record) { mRecords.at (index) = dynamic_cast&> (record); } template void Collection::appendRecord (const RecordBase& record, UniversalId::Type type) { insertRecord (record, getAppendIndex (IdAccessorT().getId ( dynamic_cast&> (record).get()), type), type); } template int Collection::getAppendIndex (const std::string& id, UniversalId::Type type) const { return static_cast (mRecords.size()); } template std::vector Collection::getIds (bool listDeleted) const { std::vector ids; for (typename std::map::const_iterator iter = mIndex.begin(); iter!=mIndex.end(); ++iter) { if (listDeleted || !mRecords[iter->second].isDeleted()) ids.push_back (IdAccessorT().getId (mRecords[iter->second].get())); } return ids; } template const Record& Collection::getRecord (const std::string& id) const { int index = getIndex (id); return mRecords.at (index); } template const Record& Collection::getRecord (int index) const { return mRecords.at (index); } template void Collection::insertRecord (const RecordBase& record, int index, UniversalId::Type type) { if (index<0 || index>static_cast (mRecords.size())) throw std::runtime_error ("index out of range"); const Record& record2 = dynamic_cast&> (record); mRecords.insert (mRecords.begin()+index, record2); if (index (mRecords.size())-1) { for (std::map::iterator iter (mIndex.begin()); iter!=mIndex.end(); ++iter) if (iter->second>=index) ++(iter->second); } mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( record2.get())), index)); } template void Collection::setRecord (int index, const Record& record) { if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index).get()))!= Misc::StringUtils::lowerCase (IdAccessorT().getId (record.get()))) throw std::runtime_error ("attempt to change the ID of a record"); mRecords.at (index) = record; } template bool Collection::reorderRows (int baseIndex, const std::vector& newOrder) { return false; } } #endif openmw-openmw-0.38.0/apps/opencs/model/world/collectionbase.cpp000066400000000000000000000011521264522266000245710ustar00rootroot00000000000000#include "collectionbase.hpp" #include #include "columnbase.hpp" CSMWorld::CollectionBase::CollectionBase() {} CSMWorld::CollectionBase::~CollectionBase() {} int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const { int columns = getColumns(); for (int i=0; i #include #include "universalid.hpp" #include "columns.hpp" class QVariant; namespace CSMWorld { struct ColumnBase; struct RecordBase; /// \brief Base class for record collections /// /// \attention Modifying records through the interface does not update connected views. /// Such modifications should be done through the table model interface instead unless no views /// are connected to the model or special precautions have been taken to send update signals /// manually. class CollectionBase { // not implemented CollectionBase (const CollectionBase&); CollectionBase& operator= (const CollectionBase&); public: CollectionBase(); virtual ~CollectionBase(); virtual int getSize() const = 0; virtual std::string getId (int index) const = 0; virtual int getIndex (const std::string& id) const = 0; virtual int getColumns() const = 0; virtual const ColumnBase& getColumn (int column) const = 0; virtual QVariant getData (int index, int column) const = 0; virtual void setData (int index, int column, const QVariant& data) = 0; // Not in use. Temporarily removed so that the implementation of RefIdCollection can continue without // these functions for now. // virtual void merge() = 0; ///< Merge modified into base. // virtual void purge() = 0; ///< Remove records that are flagged as erased. virtual void removeRows (int index, int count) = 0; virtual void appendBlankRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None) = 0; ///< \param type Will be ignored, unless the collection supports multiple record types virtual int searchId (const std::string& id) const = 0; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) virtual void replace (int index, const RecordBase& record) = 0; ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. ///< \param type Will be ignored, unless the collection supports multiple record types virtual void appendRecord (const RecordBase& record, UniversalId::Type type = UniversalId::Type_None) = 0; ///< If the record type does not match, an exception is thrown. virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type) = 0; virtual const RecordBase& getRecord (const std::string& id) const = 0; virtual const RecordBase& getRecord (int index) const = 0; virtual int getAppendIndex (const std::string& id, UniversalId::Type type = UniversalId::Type_None) const = 0; ///< \param type Will be ignored, unless the collection supports multiple record types virtual std::vector getIds (bool listDeleted = true) const = 0; ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list virtual bool reorderRows (int baseIndex, const std::vector& newOrder) = 0; ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// /// \return Success? int searchColumnIndex (Columns::ColumnId id) const; ///< Return index of column with the given \a id. If no such column exists, -1 is returned. int findColumnIndex (Columns::ColumnId id) const; ///< Return index of column with the given \a id. If no such column exists, an exception is /// thrown. }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/columnbase.cpp000066400000000000000000000071721264522266000237430ustar00rootroot00000000000000#include "columnbase.hpp" #include "columns.hpp" CSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags) : mColumnId (columnId), mFlags (flags), mDisplayType (displayType) {} CSMWorld::ColumnBase::~ColumnBase() {} bool CSMWorld::ColumnBase::isUserEditable() const { return isEditable(); } std::string CSMWorld::ColumnBase::getTitle() const { return Columns::getName (static_cast (mColumnId)); } int CSMWorld::ColumnBase::getId() const { return mColumnId; } bool CSMWorld::ColumnBase::isId (Display display) { static const Display ids[] = { Display_Skill, Display_Class, Display_Faction, Display_Race, Display_Sound, Display_Region, Display_Birthsign, Display_Spell, Display_Cell, Display_Referenceable, Display_Activator, Display_Potion, Display_Apparatus, Display_Armor, Display_Book, Display_Clothing, Display_Container, Display_Creature, Display_Door, Display_Ingredient, Display_CreatureLevelledList, Display_ItemLevelledList, Display_Light, Display_Lockpick, Display_Miscellaneous, Display_Npc, Display_Probe, Display_Repair, Display_Static, Display_Weapon, Display_Reference, Display_Filter, Display_Topic, Display_Journal, Display_TopicInfo, Display_JournalInfo, Display_Scene, Display_GlobalVariable, Display_BodyPart, Display_Enchantment, Display_Script, Display_Mesh, Display_Icon, Display_Music, Display_SoundRes, Display_Texture, Display_Video, Display_Id, Display_SkillId, Display_EffectRange, Display_EffectId, Display_PartRefType, Display_AiPackageType, Display_InfoCondFunc, Display_InfoCondVar, Display_InfoCondComp, Display_EffectSkill, Display_EffectAttribute, Display_IngredEffectId, Display_None }; for (int i=0; ids[i]!=Display_None; ++i) if (ids[i]==display) return true; return false; } bool CSMWorld::ColumnBase::isText (Display display) { return display==Display_String || display==Display_LongString || display==Display_String32 || display==Display_LongString256; } bool CSMWorld::ColumnBase::isScript (Display display) { return display==Display_ScriptFile || display==Display_ScriptLines; } void CSMWorld::NestableColumn::addColumn(CSMWorld::NestableColumn *column) { mNestedColumns.push_back(column); } const CSMWorld::ColumnBase& CSMWorld::NestableColumn::nestedColumn(int subColumn) const { if (mNestedColumns.empty()) throw std::logic_error("Tried to access nested column of the non-nest column"); return *mNestedColumns.at(subColumn); } CSMWorld::NestableColumn::NestableColumn(int columnId, CSMWorld::ColumnBase::Display displayType, int flag) : CSMWorld::ColumnBase(columnId, displayType, flag) {} CSMWorld::NestableColumn::~NestableColumn() { for (unsigned int i = 0; i < mNestedColumns.size(); ++i) { delete mNestedColumns[i]; } } bool CSMWorld::NestableColumn::hasChildren() const { return !mNestedColumns.empty(); } CSMWorld::NestedChildColumn::NestedChildColumn (int id, CSMWorld::ColumnBase::Display display, int flags, bool isEditable) : NestableColumn (id, display, flags) , mIsEditable(isEditable) {} bool CSMWorld::NestedChildColumn::isEditable () const { return mIsEditable; } openmw-openmw-0.38.0/apps/opencs/model/world/columnbase.hpp000066400000000000000000000155041264522266000237460ustar00rootroot00000000000000#ifndef CSM_WOLRD_COLUMNBASE_H #define CSM_WOLRD_COLUMNBASE_H #include #include #include #include #include #include "record.hpp" namespace CSMWorld { struct ColumnBase { enum TableEditModes { TableEdit_None, // no editing TableEdit_Full, // edit cells and add/remove rows TableEdit_FixedRows // edit cells only }; enum Roles { Role_Flags = Qt::UserRole, Role_Display = Qt::UserRole+1, Role_ColumnId = Qt::UserRole+2 }; enum Flags { Flag_Table = 1, // column should be displayed in table view Flag_Dialogue = 2, // column should be displayed in dialogue view Flag_Dialogue_List = 4, // column should be diaplyed in dialogue view Flag_Dialogue_Refresh = 8 // refresh dialogue view if this column is modified }; enum Display { Display_None, //Do not use Display_String, Display_LongString, //CONCRETE TYPES STARTS HERE (for drag and drop) Display_Skill, Display_Class, Display_Faction, Display_Race, Display_Sound, Display_Region, Display_Birthsign, Display_Spell, Display_Cell, Display_Referenceable, Display_Activator, Display_Potion, Display_Apparatus, Display_Armor, Display_Book, Display_Clothing, Display_Container, Display_Creature, Display_Door, Display_Ingredient, Display_CreatureLevelledList, Display_ItemLevelledList, Display_Light, Display_Lockpick, Display_Miscellaneous, Display_Npc, Display_Probe, Display_Repair, Display_Static, Display_Weapon, Display_Reference, Display_Filter, Display_Topic, Display_Journal, Display_TopicInfo, Display_JournalInfo, Display_Scene, Display_GlobalVariable, Display_BodyPart, Display_Enchantment, //CONCRETE TYPES ENDS HERE Display_Integer, Display_Float, Display_Var, Display_GmstVarType, Display_GlobalVarType, Display_Specialisation, Display_Attribute, Display_Boolean, Display_SpellType, Display_Script, Display_ApparatusType, Display_ArmorType, Display_ClothingType, Display_CreatureType, Display_WeaponType, Display_RecordState, Display_RefRecordType, Display_DialogueType, Display_QuestStatusType, Display_EnchantmentType, Display_BodyPartType, Display_MeshType, Display_Gender, Display_Mesh, Display_Icon, Display_Music, Display_SoundRes, Display_Texture, Display_Video, Display_Colour, Display_ScriptFile, Display_ScriptLines, // console context Display_SoundGeneratorType, Display_School, Display_Id, Display_SkillId, Display_EffectRange, Display_EffectId, Display_PartRefType, Display_AiPackageType, Display_InfoCondFunc, Display_InfoCondVar, Display_InfoCondComp, Display_String32, Display_LongString256, Display_EffectSkill, // must display at least one, unlike Display_Skill Display_EffectAttribute, // must display at least one, unlike Display_Attribute Display_IngredEffectId, // display none allowed, unlike Display_EffectId //top level columns that nest other columns Display_NestedHeader }; int mColumnId; int mFlags; Display mDisplayType; ColumnBase (int columnId, Display displayType, int flag); virtual ~ColumnBase(); virtual bool isEditable() const = 0; virtual bool isUserEditable() const; ///< Can this column be edited directly by the user? virtual std::string getTitle() const; virtual int getId() const; static bool isId (Display display); static bool isText (Display display); static bool isScript (Display display); }; class NestableColumn : public ColumnBase { std::vector mNestedColumns; public: NestableColumn(int columnId, Display displayType, int flag); ~NestableColumn(); void addColumn(CSMWorld::NestableColumn *column); const ColumnBase& nestedColumn(int subColumn) const; bool hasChildren() const; }; template struct Column : public NestableColumn { Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) : NestableColumn (columnId, displayType, flags) {} virtual QVariant get (const Record& record) const = 0; virtual void set (Record& record, const QVariant& data) { throw std::logic_error ("Column " + getTitle() + " is not editable"); } }; template struct NestedParentColumn : public Column { NestedParentColumn (int id, int flags = ColumnBase::Flag_Dialogue, bool fixedRows = false) : Column (id, ColumnBase::Display_NestedHeader, flags), mFixedRows(fixedRows) {} virtual void set (Record& record, const QVariant& data) { // There is nothing to do here. // This prevents exceptions from parent's implementation } virtual QVariant get (const Record& record) const { // by default editable; also see IdTree::hasChildren() if (mFixedRows) return QVariant::fromValue(ColumnBase::TableEdit_FixedRows); else return QVariant::fromValue(ColumnBase::TableEdit_Full); } virtual bool isEditable() const { return true; } private: bool mFixedRows; }; struct NestedChildColumn : public NestableColumn { NestedChildColumn (int id, Display display, int flags = ColumnBase::Flag_Dialogue, bool isEditable = true); virtual bool isEditable() const; private: bool mIsEditable; }; } Q_DECLARE_METATYPE(CSMWorld::ColumnBase::TableEditModes) #endif openmw-openmw-0.38.0/apps/opencs/model/world/columnimp.cpp000066400000000000000000000013501264522266000236060ustar00rootroot00000000000000#include "columnimp.hpp" CSMWorld::BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn *meshType) : mMeshType(meshType) {} QVariant CSMWorld::BodyPartRaceColumn::get(const Record &record) const { if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) { return QString::fromUtf8(record.get().mRace.c_str()); } return QVariant(QVariant::UserType); } void CSMWorld::BodyPartRaceColumn::set(Record &record, const QVariant &data) { ESM::BodyPart record2 = record.get(); record2.mRace = data.toString().toUtf8().constData(); record.setModified(record2); } bool CSMWorld::BodyPartRaceColumn::isEditable() const { return true; } openmw-openmw-0.38.0/apps/opencs/model/world/columnimp.hpp000066400000000000000000002041161264522266000236200ustar00rootroot00000000000000#ifndef CSM_WOLRD_COLUMNIMP_H #define CSM_WOLRD_COLUMNIMP_H #include #include #include #include #include #include #include #include #include "columnbase.hpp" #include "columns.hpp" #include "info.hpp" namespace CSMWorld { /// \note Shares ID with VarValueColumn. A table can not have both. template struct FloatValueColumn : public Column { FloatValueColumn() : Column (Columns::ColumnId_Value, ColumnBase::Display_Float) {} virtual QVariant get (const Record& record) const { return record.get().mValue.getFloat(); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mValue.setFloat (data.toFloat()); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct StringIdColumn : public Column { StringIdColumn (bool hidden = false) : Column (Columns::ColumnId_Id, ColumnBase::Display_Id, hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mId.c_str()); } virtual bool isEditable() const { return false; } }; template struct RecordStateColumn : public Column { RecordStateColumn() : Column (Columns::ColumnId_Modification, ColumnBase::Display_RecordState) {} virtual QVariant get (const Record& record) const { if (record.mState==Record::State_Erased) return static_cast (Record::State_Deleted); return static_cast (record.mState); } virtual void set (Record& record, const QVariant& data) { record.mState = static_cast (data.toInt()); } virtual bool isEditable() const { return true; } virtual bool isUserEditable() const { return false; } }; template struct FixedRecordTypeColumn : public Column { int mType; FixedRecordTypeColumn (int type) : Column (Columns::ColumnId_RecordType, ColumnBase::Display_Integer, 0), mType (type) {} virtual QVariant get (const Record& record) const { return mType; } virtual bool isEditable() const { return false; } }; /// \attention A var type column must be immediately followed by a suitable value column. template struct VarTypeColumn : public Column { VarTypeColumn (ColumnBase::Display display) : Column (Columns::ColumnId_ValueType, display) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mValue.getType()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mValue.setType (static_cast (data.toInt())); record.setModified (record2); } virtual bool isEditable() const { return true; } }; /// \note Shares ID with FloatValueColumn. A table can not have both. template struct VarValueColumn : public Column { VarValueColumn() : Column (Columns::ColumnId_Value, ColumnBase::Display_Var) {} virtual QVariant get (const Record& record) const { switch (record.get().mValue.getType()) { case ESM::VT_String: return QString::fromUtf8 (record.get().mValue.getString().c_str()); case ESM::VT_Int: case ESM::VT_Short: case ESM::VT_Long: return record.get().mValue.getInteger(); case ESM::VT_Float: return record.get().mValue.getFloat(); default: return QVariant(); } } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); switch (record2.mValue.getType()) { case ESM::VT_String: record2.mValue.setString (data.toString().toUtf8().constData()); break; case ESM::VT_Int: case ESM::VT_Short: case ESM::VT_Long: record2.mValue.setInteger (data.toInt()); break; case ESM::VT_Float: record2.mValue.setFloat (data.toFloat()); break; default: break; } record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct DescriptionColumn : public Column { DescriptionColumn() : Column (Columns::ColumnId_Description, ColumnBase::Display_LongString) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mDescription.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mDescription = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct SpecialisationColumn : public Column { SpecialisationColumn() : Column (Columns::ColumnId_Specialisation, ColumnBase::Display_Specialisation) {} virtual QVariant get (const Record& record) const { return record.get().mData.mSpecialization; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mSpecialization = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct UseValueColumn : public Column { int mIndex; UseValueColumn (int index) : Column (Columns::ColumnId_UseValue1 + index, ColumnBase::Display_Float), mIndex (index) {} virtual QVariant get (const Record& record) const { return record.get().mData.mUseValue[mIndex]; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mUseValue[mIndex] = data.toFloat(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct AttributeColumn : public Column { AttributeColumn() : Column (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute) {} virtual QVariant get (const Record& record) const { return record.get().mData.mAttribute; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mAttribute = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct NameColumn : public Column { NameColumn() : Column (Columns::ColumnId_Name, ColumnBase::Display_String) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mName.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mName = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct AttributesColumn : public Column { int mIndex; AttributesColumn (int index) : Column (Columns::ColumnId_Attribute1 + index, ColumnBase::Display_Attribute), mIndex (index) {} virtual QVariant get (const Record& record) const { return record.get().mData.mAttribute[mIndex]; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mAttribute[mIndex] = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct SkillsColumn : public Column { int mIndex; bool mMajor; SkillsColumn (int index, bool typePrefix = false, bool major = false) : Column ((typePrefix ? ( major ? Columns::ColumnId_MajorSkill1 : Columns::ColumnId_MinorSkill1) : Columns::ColumnId_Skill1) + index, ColumnBase::Display_Skill), mIndex (index), mMajor (major) {} virtual QVariant get (const Record& record) const { int skill = record.get().mData.getSkill (mIndex, mMajor); return QString::fromUtf8 (ESM::Skill::indexToId (skill).c_str()); } virtual void set (Record& record, const QVariant& data) { std::istringstream stream (data.toString().toUtf8().constData()); int index = -1; char c; stream >> c >> index; if (index!=-1) { ESXRecordT record2 = record.get(); record2.mData.getSkill (mIndex, mMajor) = index; record.setModified (record2); } } virtual bool isEditable() const { return true; } }; template struct PlayableColumn : public Column { PlayableColumn() : Column (Columns::ColumnId_Playable, ColumnBase::Display_Boolean) {} virtual QVariant get (const Record& record) const { return record.get().mData.mIsPlayable!=0; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mIsPlayable = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct HiddenColumn : public Column { HiddenColumn() : Column (Columns::ColumnId_Hidden, ColumnBase::Display_Boolean) {} virtual QVariant get (const Record& record) const { return record.get().mData.mIsHidden!=0; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mIsHidden = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct FlagColumn : public Column { int mMask; bool mInverted; FlagColumn (int columnId, int mask, int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, bool inverted = false) : Column (columnId, ColumnBase::Display_Boolean, flags), mMask (mask), mInverted (inverted) {} virtual QVariant get (const Record& record) const { bool flag = (record.get().mData.mFlags & mMask)!=0; if (mInverted) flag = !flag; return flag; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); int flags = record2.mData.mFlags & ~mMask; if ((data.toInt()!=0)!=mInverted) flags |= mMask; record2.mData.mFlags = flags; record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct FlagColumn2 : public Column { int mMask; bool mInverted; FlagColumn2 (int columnId, int mask, bool inverted = false) : Column (columnId, ColumnBase::Display_Boolean), mMask (mask), mInverted (inverted) {} virtual QVariant get (const Record& record) const { bool flag = (record.get().mFlags & mMask)!=0; if (mInverted) flag = !flag; return flag; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); int flags = record2.mFlags & ~mMask; if ((data.toInt()!=0)!=mInverted) flags |= mMask; record2.mFlags = flags; record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct WeightHeightColumn : public Column { bool mMale; bool mWeight; WeightHeightColumn (bool male, bool weight) : Column (male ? (weight ? Columns::ColumnId_MaleWeight : Columns::ColumnId_MaleHeight) : (weight ? Columns::ColumnId_FemaleWeight : Columns::ColumnId_FemaleHeight), ColumnBase::Display_Float), mMale (male), mWeight (weight) {} virtual QVariant get (const Record& record) const { const ESM::Race::MaleFemaleF& value = mWeight ? record.get().mData.mWeight : record.get().mData.mHeight; return mMale ? value.mMale : value.mFemale; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); ESM::Race::MaleFemaleF& value = mWeight ? record2.mData.mWeight : record2.mData.mHeight; (mMale ? value.mMale : value.mFemale) = data.toFloat(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct SoundParamColumn : public Column { enum Type { Type_Volume, Type_MinRange, Type_MaxRange }; Type mType; SoundParamColumn (Type type) : Column (type==Type_Volume ? Columns::ColumnId_Volume : (type==Type_MinRange ? Columns::ColumnId_MinRange : Columns::ColumnId_MaxRange), ColumnBase::Display_Integer), mType (type) {} virtual QVariant get (const Record& record) const { int value = 0; switch (mType) { case Type_Volume: value = record.get().mData.mVolume; break; case Type_MinRange: value = record.get().mData.mMinRange; break; case Type_MaxRange: value = record.get().mData.mMaxRange; break; } return value; } virtual void set (Record& record, const QVariant& data) { int value = data.toInt(); if (value<0) value = 0; else if (value>255) value = 255; ESXRecordT record2 = record.get(); switch (mType) { case Type_Volume: record2.mData.mVolume = static_cast (value); break; case Type_MinRange: record2.mData.mMinRange = static_cast (value); break; case Type_MaxRange: record2.mData.mMaxRange = static_cast (value); break; } record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct SoundFileColumn : public Column { SoundFileColumn() : Column (Columns::ColumnId_SoundFile, ColumnBase::Display_SoundRes) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mSound.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mSound = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; /// \todo QColor is a GUI class and should not be in model. Need to think of an alternative /// solution. template struct MapColourColumn : public Column { /// \todo Replace Display_Integer with something that displays the colour value more directly. MapColourColumn() : Column (Columns::ColumnId_MapColour, ColumnBase::Display_Colour) {} virtual QVariant get (const Record& record) const { int colour = record.get().mMapColor; return QColor (colour & 0xff, (colour>>8) & 0xff, (colour>>16) & 0xff); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); QColor colour = data.value(); record2.mMapColor = (colour.blue() << 16) | (colour.green() << 8) | colour.red(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct SleepListColumn : public Column { SleepListColumn() : Column (Columns::ColumnId_SleepEncounter, ColumnBase::Display_CreatureLevelledList) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mSleepList.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mSleepList = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct TextureColumn : public Column { TextureColumn() : Column (Columns::ColumnId_Texture, ColumnBase::Display_Texture) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mTexture.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mTexture = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct SpellTypeColumn : public Column { SpellTypeColumn() : Column (Columns::ColumnId_SpellType, ColumnBase::Display_SpellType) {} virtual QVariant get (const Record& record) const { return record.get().mData.mType; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mType = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct CostColumn : public Column { CostColumn() : Column (Columns::ColumnId_Cost, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return record.get().mData.mCost; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mCost = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct ScriptColumn : public Column { enum Type { Type_File, // regular script record Type_Lines, // console context Type_Info // dialogue context (not implemented yet) }; ScriptColumn (Type type) : Column (Columns::ColumnId_ScriptText, type==Type_File ? ColumnBase::Display_ScriptFile : ColumnBase::Display_ScriptLines, type==Type_File ? 0 : ColumnBase::Flag_Dialogue) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mScriptText.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mScriptText = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct RegionColumn : public Column { RegionColumn() : Column (Columns::ColumnId_Region, ColumnBase::Display_Region) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mRegion.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mRegion = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct CellColumn : public Column { bool mBlocked; /// \param blocked Do not allow user-modification CellColumn (bool blocked = false) : Column (Columns::ColumnId_Cell, ColumnBase::Display_Cell), mBlocked (blocked) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mCell.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mCell = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } virtual bool isUserEditable() const { return !mBlocked; } }; template struct OriginalCellColumn : public Column { OriginalCellColumn() : Column (Columns::ColumnId_OriginalCell, ColumnBase::Display_Cell) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mOriginalCell.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mOriginalCell = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } virtual bool isUserEditable() const { return false; } }; template struct IdColumn : public Column { IdColumn() : Column (Columns::ColumnId_ReferenceableId, ColumnBase::Display_Referenceable) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mRefID.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mRefID = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct ScaleColumn : public Column { ScaleColumn() : Column (Columns::ColumnId_Scale, ColumnBase::Display_Float) {} virtual QVariant get (const Record& record) const { return record.get().mScale; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mScale = data.toFloat(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct OwnerColumn : public Column { OwnerColumn() : Column (Columns::ColumnId_Owner, ColumnBase::Display_Npc) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mOwner.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mOwner = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct SoulColumn : public Column { SoulColumn() : Column (Columns::ColumnId_Soul, ColumnBase::Display_Creature) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mSoul.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mSoul = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct FactionColumn : public Column { FactionColumn() : Column (Columns::ColumnId_Faction, ColumnBase::Display_Faction) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mFaction.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mFaction = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct FactionIndexColumn : public Column { FactionIndexColumn() : Column (Columns::ColumnId_FactionIndex, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return record.get().mFactionRank; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mFactionRank = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct ChargesColumn : public Column { ChargesColumn() : Column (Columns::ColumnId_Charges, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return record.get().mChargeInt; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mChargeInt = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct EnchantmentChargesColumn : public Column { EnchantmentChargesColumn() : Column (Columns::ColumnId_Enchantment, ColumnBase::Display_Float) {} virtual QVariant get (const Record& record) const { return record.get().mEnchantmentCharge; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mEnchantmentCharge = data.toFloat(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct GoldValueColumn : public Column { GoldValueColumn() : Column (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return record.get().mGoldValue; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mGoldValue = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct TeleportColumn : public Column { TeleportColumn() : Column (Columns::ColumnId_Teleport, ColumnBase::Display_Boolean) {} virtual QVariant get (const Record& record) const { return record.get().mTeleport; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mTeleport = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct TeleportCellColumn : public Column { TeleportCellColumn() : Column (Columns::ColumnId_TeleportCell, ColumnBase::Display_Cell) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mDestCell.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mDestCell = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } virtual bool isUserEditable() const { return true; } }; template struct LockLevelColumn : public Column { LockLevelColumn() : Column (Columns::ColumnId_LockLevel, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return record.get().mLockLevel; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mLockLevel = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct KeyColumn : public Column { KeyColumn() : Column (Columns::ColumnId_Key, ColumnBase::Display_Miscellaneous) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mKey.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mKey = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct TrapColumn : public Column { TrapColumn() : Column (Columns::ColumnId_Trap, ColumnBase::Display_Spell) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mTrap.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mTrap = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct FilterColumn : public Column { FilterColumn() : Column (Columns::ColumnId_Filter, ColumnBase::Display_Filter) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mFilter.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mFilter = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct PosColumn : public Column { ESM::Position ESXRecordT::* mPosition; int mIndex; PosColumn (ESM::Position ESXRecordT::* position, int index, bool door) : Column ( (door ? Columns::ColumnId_DoorPositionXPos : Columns::ColumnId_PositionXPos)+index, ColumnBase::Display_Float), mPosition (position), mIndex (index) {} virtual QVariant get (const Record& record) const { const ESM::Position& position = record.get().*mPosition; return position.pos[mIndex]; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); ESM::Position& position = record2.*mPosition; position.pos[mIndex] = data.toFloat(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct RotColumn : public Column { ESM::Position ESXRecordT::* mPosition; int mIndex; RotColumn (ESM::Position ESXRecordT::* position, int index, bool door) : Column ( (door ? Columns::ColumnId_DoorPositionXRot : Columns::ColumnId_PositionXRot)+index, ColumnBase::Display_Float), mPosition (position), mIndex (index) {} virtual QVariant get (const Record& record) const { const ESM::Position& position = record.get().*mPosition; return position.rot[mIndex]; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); ESM::Position& position = record2.*mPosition; position.rot[mIndex] = data.toFloat(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct DialogueTypeColumn : public Column { DialogueTypeColumn (bool hidden = false) : Column (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType, hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mType); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mType = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } virtual bool isUserEditable() const { return false; } }; template struct QuestStatusTypeColumn : public Column { QuestStatusTypeColumn() : Column (Columns::ColumnId_QuestStatusType, ColumnBase::Display_QuestStatusType) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mQuestStatus); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mQuestStatus = static_cast (data.toInt()); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct QuestDescriptionColumn : public Column { QuestDescriptionColumn() : Column (Columns::ColumnId_QuestDescription, ColumnBase::Display_LongString) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mResponse.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mResponse = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct QuestIndexColumn : public Column { QuestIndexColumn() : Column (Columns::ColumnId_QuestIndex, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return record.get().mData.mDisposition; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mDisposition = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct TopicColumn : public Column { TopicColumn (bool journal) : Column (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic, journal ? ColumnBase::Display_Journal : ColumnBase::Display_Topic) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mTopicId.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mTopicId = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } virtual bool isUserEditable() const { return false; } }; template struct ActorColumn : public Column { ActorColumn() : Column (Columns::ColumnId_Actor, ColumnBase::Display_Npc) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mActor.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mActor = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct RaceColumn : public Column { RaceColumn() : Column (Columns::ColumnId_Race, ColumnBase::Display_Race) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mRace.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mRace = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct ClassColumn : public Column { ClassColumn() : Column (Columns::ColumnId_Class, ColumnBase::Display_Class) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mClass.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mClass = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct PcFactionColumn : public Column { PcFactionColumn() : Column (Columns::ColumnId_PcFaction, ColumnBase::Display_Faction) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mPcFaction.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mPcFaction = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct ResponseColumn : public Column { ResponseColumn() : Column (Columns::ColumnId_Response, ColumnBase::Display_LongString) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mResponse.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mResponse = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct DispositionColumn : public Column { DispositionColumn() : Column (Columns::ColumnId_Disposition, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return record.get().mData.mDisposition; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mDisposition = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct RankColumn : public Column { RankColumn() : Column (Columns::ColumnId_Rank, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mData.mRank); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mRank = static_cast (data.toInt()); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct PcRankColumn : public Column { PcRankColumn() : Column (Columns::ColumnId_PcRank, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mData.mPCrank); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mPCrank = static_cast (data.toInt()); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct GenderColumn : public Column { GenderColumn() : Column (Columns::ColumnId_Gender, ColumnBase::Display_Gender) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mData.mGender); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mGender = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct EnchantmentTypeColumn : public Column { EnchantmentTypeColumn() : Column (Columns::ColumnId_EnchantmentType, ColumnBase::Display_EnchantmentType) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mData.mType); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mType = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct ChargesColumn2 : public Column { ChargesColumn2() : Column (Columns::ColumnId_Charges, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return record.get().mData.mCharge; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mCharge = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct AutoCalcColumn : public Column { AutoCalcColumn() : Column (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean) {} virtual QVariant get (const Record& record) const { return record.get().mData.mAutocalc!=0; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mAutocalc = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct ModelColumn : public Column { ModelColumn() : Column (Columns::ColumnId_Model, ColumnBase::Display_Mesh) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mModel.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mModel = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct VampireColumn : public Column { VampireColumn() : Column (Columns::ColumnId_Vampire, ColumnBase::Display_Boolean) {} virtual QVariant get (const Record& record) const { return record.get().mData.mVampire!=0; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mVampire = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct BodyPartTypeColumn : public Column { BodyPartTypeColumn() : Column (Columns::ColumnId_BodyPartType, ColumnBase::Display_BodyPartType) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mData.mPart); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mPart = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct MeshTypeColumn : public Column { MeshTypeColumn(int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue) : Column (Columns::ColumnId_MeshType, ColumnBase::Display_MeshType, flags) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mData.mType); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mType = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct OwnerGlobalColumn : public Column { OwnerGlobalColumn() : Column (Columns::ColumnId_OwnerGlobal, ColumnBase::Display_GlobalVariable) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mGlobalVariable.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mGlobalVariable = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct RefNumCounterColumn : public Column { RefNumCounterColumn() : Column (Columns::ColumnId_RefNumCounter, ColumnBase::Display_Integer, 0) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mRefNumCounter); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mRefNumCounter = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } virtual bool isUserEditable() const { return false; } }; template struct RefNumColumn : public Column { RefNumColumn() : Column (Columns::ColumnId_RefNum, ColumnBase::Display_Integer, 0) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mRefNum.mIndex); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mRefNum.mIndex = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } virtual bool isUserEditable() const { return false; } }; template struct SoundColumn : public Column { SoundColumn() : Column (Columns::ColumnId_Sound, ColumnBase::Display_Sound) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mSound.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mSound = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct CreatureColumn : public Column { CreatureColumn() : Column (Columns::ColumnId_Creature, ColumnBase::Display_Creature) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mCreature.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mCreature = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct SoundGeneratorTypeColumn : public Column { SoundGeneratorTypeColumn() : Column (Columns::ColumnId_SoundGeneratorType, ColumnBase::Display_SoundGeneratorType) {} virtual QVariant get (const Record& record) const { return static_cast (record.get().mType); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mType = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct BaseCostColumn : public Column { BaseCostColumn() : Column (Columns::ColumnId_BaseCost, ColumnBase::Display_Float) {} virtual QVariant get (const Record& record) const { return record.get().mData.mBaseCost; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mBaseCost = data.toFloat(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct SchoolColumn : public Column { SchoolColumn() : Column (Columns::ColumnId_School, ColumnBase::Display_School) {} virtual QVariant get (const Record& record) const { return record.get().mData.mSchool; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mData.mSchool = data.toInt(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct EffectTextureColumn : public Column { EffectTextureColumn (Columns::ColumnId columnId) : Column (columnId, columnId == Columns::ColumnId_Particle ? ColumnBase::Display_Texture : ColumnBase::Display_Icon) { assert (this->mColumnId==Columns::ColumnId_Icon || this->mColumnId==Columns::ColumnId_Particle); } virtual QVariant get (const Record& record) const { return QString::fromUtf8 ( (this->mColumnId==Columns::ColumnId_Icon ? record.get().mIcon : record.get().mParticle).c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); (this->mColumnId==Columns::ColumnId_Icon ? record2.mIcon : record2.mParticle) = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct EffectObjectColumn : public Column { EffectObjectColumn (Columns::ColumnId columnId) : Column (columnId, columnId==Columns::ColumnId_BoltObject ? ColumnBase::Display_Weapon : ColumnBase::Display_Static) { assert (this->mColumnId==Columns::ColumnId_CastingObject || this->mColumnId==Columns::ColumnId_HitObject || this->mColumnId==Columns::ColumnId_AreaObject || this->mColumnId==Columns::ColumnId_BoltObject); } virtual QVariant get (const Record& record) const { const std::string *string = 0; switch (this->mColumnId) { case Columns::ColumnId_CastingObject: string = &record.get().mCasting; break; case Columns::ColumnId_HitObject: string = &record.get().mHit; break; case Columns::ColumnId_AreaObject: string = &record.get().mArea; break; case Columns::ColumnId_BoltObject: string = &record.get().mBolt; break; } if (!string) throw std::logic_error ("Unsupported column ID"); return QString::fromUtf8 (string->c_str()); } virtual void set (Record& record, const QVariant& data) { std::string *string = 0; ESXRecordT record2 = record.get(); switch (this->mColumnId) { case Columns::ColumnId_CastingObject: string = &record2.mCasting; break; case Columns::ColumnId_HitObject: string = &record2.mHit; break; case Columns::ColumnId_AreaObject: string = &record2.mArea; break; case Columns::ColumnId_BoltObject: string = &record2.mBolt; break; } if (!string) throw std::logic_error ("Unsupported column ID"); *string = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct EffectSoundColumn : public Column { EffectSoundColumn (Columns::ColumnId columnId) : Column (columnId, ColumnBase::Display_Sound) { assert (this->mColumnId==Columns::ColumnId_CastingSound || this->mColumnId==Columns::ColumnId_HitSound || this->mColumnId==Columns::ColumnId_AreaSound || this->mColumnId==Columns::ColumnId_BoltSound); } virtual QVariant get (const Record& record) const { const std::string *string = 0; switch (this->mColumnId) { case Columns::ColumnId_CastingSound: string = &record.get().mCastSound; break; case Columns::ColumnId_HitSound: string = &record.get().mHitSound; break; case Columns::ColumnId_AreaSound: string = &record.get().mAreaSound; break; case Columns::ColumnId_BoltSound: string = &record.get().mBoltSound; break; } if (!string) throw std::logic_error ("Unsupported column ID"); return QString::fromUtf8 (string->c_str()); } virtual void set (Record& record, const QVariant& data) { std::string *string = 0; ESXRecordT record2 = record.get(); switch (this->mColumnId) { case Columns::ColumnId_CastingSound: string = &record2.mCastSound; break; case Columns::ColumnId_HitSound: string = &record2.mHitSound; break; case Columns::ColumnId_AreaSound: string = &record2.mAreaSound; break; case Columns::ColumnId_BoltSound: string = &record2.mBoltSound; break; } if (!string) throw std::logic_error ("Unsupported column ID"); *string = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct FormatColumn : public Column { FormatColumn() : Column (Columns::ColumnId_FileFormat, ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { return record.get().mFormat; } virtual bool isEditable() const { return false; } }; template struct AuthorColumn : public Column { AuthorColumn() : Column (Columns::ColumnId_Author, ColumnBase::Display_String32) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mAuthor.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mAuthor = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; template struct FileDescriptionColumn : public Column { FileDescriptionColumn() : Column (Columns::ColumnId_FileDescription, ColumnBase::Display_LongString256) {} virtual QVariant get (const Record& record) const { return QString::fromUtf8 (record.get().mDescription.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mDescription = data.toString().toUtf8().constData(); record.setModified (record2); } virtual bool isEditable() const { return true; } }; struct BodyPartRaceColumn : public RaceColumn { const MeshTypeColumn *mMeshType; BodyPartRaceColumn(const MeshTypeColumn *meshType); virtual QVariant get(const Record &record) const; virtual void set(Record &record, const QVariant &data); virtual bool isEditable() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/columns.cpp000066400000000000000000000640121264522266000232670ustar00rootroot00000000000000#include "columns.hpp" #include #include "universalid.hpp" namespace CSMWorld { namespace Columns { struct ColumnDesc { int mId; const char *mName; }; const ColumnDesc sNames[] = { { ColumnId_Value, "Value" }, { ColumnId_Id, "ID" }, { ColumnId_Modification, "Modified" }, { ColumnId_RecordType, "Record Type" }, { ColumnId_ValueType, "Value Type" }, { ColumnId_Description, "Description" }, { ColumnId_Specialisation, "Specialisation" }, { ColumnId_Attribute, "Attribute" }, { ColumnId_Name, "Name" }, { ColumnId_Playable, "Playable" }, { ColumnId_Hidden, "Hidden" }, { ColumnId_MaleWeight, "Male Weight" }, { ColumnId_FemaleWeight, "Female Weight" }, { ColumnId_MaleHeight, "Male Height" }, { ColumnId_FemaleHeight, "Female Height" }, { ColumnId_Volume, "Volume" }, { ColumnId_MinRange, "Min Range" }, { ColumnId_MaxRange, "Max Range" }, { ColumnId_MinMagnitude, "Min Magnitude" }, { ColumnId_MaxMagnitude, "Max Magnitude" }, { ColumnId_SoundFile, "Sound File" }, { ColumnId_MapColour, "Map Colour" }, { ColumnId_SleepEncounter, "Sleep Encounter" }, { ColumnId_Texture, "Texture" }, { ColumnId_SpellType, "Spell Type" }, { ColumnId_Cost, "Cost" }, { ColumnId_ScriptText, "Script Text" }, { ColumnId_Region, "Region" }, { ColumnId_Cell, "Cell" }, { ColumnId_Scale, "Scale" }, { ColumnId_Owner, "Owner" }, { ColumnId_Soul, "Soul" }, { ColumnId_Faction, "Faction" }, { ColumnId_FactionIndex, "Faction Index" }, { ColumnId_Charges, "Charges" }, { ColumnId_Enchantment, "Enchantment" }, { ColumnId_CoinValue, "Coin Value" }, { ColumnId_Teleport, "Teleport" }, { ColumnId_TeleportCell, "Teleport Cell" }, { ColumnId_LockLevel, "Lock Level" }, { ColumnId_Key, "Key" }, { ColumnId_Trap, "Trap" }, { ColumnId_BeastRace, "Beast Race" }, { ColumnId_AutoCalc, "Auto Calc" }, { ColumnId_StarterSpell, "Starter Spell" }, { ColumnId_AlwaysSucceeds, "Always Succeeds" }, { ColumnId_SleepForbidden, "Sleep Forbidden" }, { ColumnId_InteriorWater, "Interior Water" }, { ColumnId_InteriorSky, "Interior Sky" }, { ColumnId_Model, "Model" }, { ColumnId_Script, "Script" }, { ColumnId_Icon, "Icon" }, { ColumnId_Weight, "Weight" }, { ColumnId_EnchantmentPoints, "Enchantment Points" }, { ColumnId_Quality, "Quality" }, { ColumnId_AiHello, "AI Hello" }, { ColumnId_AiFlee, "AI Flee" }, { ColumnId_AiFight, "AI Fight" }, { ColumnId_AiAlarm, "AI Alarm" }, { ColumnId_BuysWeapons, "Buys Weapons" }, { ColumnId_BuysArmor, "Buys Armor" }, { ColumnId_BuysClothing, "Buys Clothing" }, { ColumnId_BuysBooks, "Buys Books" }, { ColumnId_BuysIngredients, "Buys Ingredients" }, { ColumnId_BuysLockpicks, "Buys Lockpicks" }, { ColumnId_BuysProbes, "Buys Probes" }, { ColumnId_BuysLights, "Buys Lights" }, { ColumnId_BuysApparati, "Buys Apparati" }, { ColumnId_BuysRepairItems, "Buys Repair Items" }, { ColumnId_BuysMiscItems, "Buys Misc Items" }, { ColumnId_BuysPotions, "Buys Potions" }, { ColumnId_BuysMagicItems, "Buys Magic Items" }, { ColumnId_SellsSpells, "Sells Spells" }, { ColumnId_Trainer, "Trainer" }, { ColumnId_Spellmaking, "Spellmaking" }, { ColumnId_EnchantingService, "Enchanting Service" }, { ColumnId_RepairService, "Repair Service" }, { ColumnId_ApparatusType, "Apparatus Type" }, { ColumnId_ArmorType, "Armor Type" }, { ColumnId_Health, "Health" }, { ColumnId_ArmorValue, "Armor Value" }, { ColumnId_Scroll, "Scroll" }, { ColumnId_ClothingType, "Clothing Type" }, { ColumnId_WeightCapacity, "Weight Capacity" }, { ColumnId_OrganicContainer, "Organic Container" }, { ColumnId_Respawn, "Respawn" }, { ColumnId_CreatureType, "Creature Type" }, { ColumnId_SoulPoints, "Soul Points" }, { ColumnId_OriginalCreature, "Original Creature" }, { ColumnId_Biped, "Biped" }, { ColumnId_HasWeapon, "Has Weapon" }, { ColumnId_Swims, "Swims" }, { ColumnId_Flies, "Flies" }, { ColumnId_Walks, "Walks" }, { ColumnId_Essential, "Essential" }, { ColumnId_SkeletonBlood, "Skeleton Blood" }, { ColumnId_MetalBlood, "Metal Blood" }, { ColumnId_OpenSound, "Open Sound" }, { ColumnId_CloseSound, "Close Sound" }, { ColumnId_Duration, "Duration" }, { ColumnId_Radius, "Radius" }, { ColumnId_Colour, "Colour" }, { ColumnId_Sound, "Sound" }, { ColumnId_Dynamic, "Dynamic" }, { ColumnId_Portable, "Portable" }, { ColumnId_NegativeLight, "Negative Light" }, { ColumnId_Flickering, "Flickering" }, { ColumnId_SlowFlickering, "Slow Flickering" }, { ColumnId_Pulsing, "Pulsing" }, { ColumnId_SlowPulsing, "Slow Pulsing" }, { ColumnId_Fire, "Fire" }, { ColumnId_OffByDefault, "Off by default" }, { ColumnId_IsKey, "Is Key" }, { ColumnId_Race, "Race" }, { ColumnId_Class, "Class" }, { Columnid_Hair, "Hair" }, { ColumnId_Head, "Head" }, { ColumnId_Female, "Female" }, { ColumnId_WeaponType, "Weapon Type" }, { ColumnId_WeaponSpeed, "Weapon Speed" }, { ColumnId_WeaponReach, "Weapon Reach" }, { ColumnId_MinChop, "Min Chop" }, { ColumnId_MaxChip, "Max Chop" }, { Columnid_MinSlash, "Min Slash" }, { ColumnId_MaxSlash, "Max Slash" }, { ColumnId_MinThrust, "Min Thrust" }, { ColumnId_MaxThrust, "Max Thrust" }, { ColumnId_Magical, "Magical" }, { ColumnId_Silver, "Silver" }, { ColumnId_Filter, "Filter" }, { ColumnId_PositionXPos, "Pos X" }, { ColumnId_PositionYPos, "Pos Y" }, { ColumnId_PositionZPos, "Pos Z" }, { ColumnId_PositionXRot, "Rot X" }, { ColumnId_PositionYRot, "Rot Y" }, { ColumnId_PositionZRot, "Rot Z" }, { ColumnId_DoorPositionXPos, "Teleport Pos X" }, { ColumnId_DoorPositionYPos, "Teleport Pos Y" }, { ColumnId_DoorPositionZPos, "Teleport Pos Z" }, { ColumnId_DoorPositionXRot, "Teleport Rot X" }, { ColumnId_DoorPositionYRot, "Teleport Rot Y" }, { ColumnId_DoorPositionZRot, "Teleport Rot Z" }, { ColumnId_DialogueType, "Dialogue Type" }, { ColumnId_QuestIndex, "Quest Index" }, { ColumnId_QuestStatusType, "Quest Status" }, { ColumnId_QuestDescription, "Quest Description" }, { ColumnId_Topic, "Topic" }, { ColumnId_Journal, "Journal" }, { ColumnId_Actor, "Actor" }, { ColumnId_PcFaction, "PC Faction" }, { ColumnId_Response, "Response" }, { ColumnId_Disposition, "Disposition" }, { ColumnId_Rank, "Rank" }, { ColumnId_Gender, "Gender" }, { ColumnId_PcRank, "PC Rank" }, { ColumnId_ReferenceableId, "Object ID" }, { ColumnId_ContainerContent, "Content" }, { ColumnId_ItemCount, "Count" }, { ColumnId_InventoryItemId, "Item ID"}, { ColumnId_CombatState, "Combat" }, { ColumnId_MagicState, "Magic" }, { ColumnId_StealthState, "Stealth" }, { ColumnId_EnchantmentType, "Enchantment Type" }, { ColumnId_Vampire, "Vampire" }, { ColumnId_BodyPartType, "Bodypart Type" }, { ColumnId_MeshType, "Mesh Type" }, { ColumnId_ActorInventory, "Inventory" }, { ColumnId_SpellList, "Spells" }, { ColumnId_SpellId, "Spell ID"}, { ColumnId_NpcDestinations, "Destinations" }, { ColumnId_DestinationCell, "Dest Cell"}, { ColumnId_PosX, "Dest X"}, { ColumnId_PosY, "Dest Y"}, { ColumnId_PosZ, "Dest Z"}, { ColumnId_RotX, "Rotation X"}, { ColumnId_RotY, "Rotation Y"}, { ColumnId_RotZ, "Rotation Z"}, { ColumnId_OwnerGlobal, "Owner Global" }, { ColumnId_DefaultProfile, "Default Profile" }, { ColumnId_BypassNewGame, "Bypass New Game" }, { ColumnId_GlobalProfile, "Global Profile" }, { ColumnId_RefNumCounter, "RefNum Counter" }, { ColumnId_RefNum, "RefNum" }, { ColumnId_Creature, "Creature" }, { ColumnId_SoundGeneratorType, "Sound Generator Type" }, { ColumnId_AllowSpellmaking, "Allow Spellmaking" }, { ColumnId_AllowEnchanting, "Allow Enchanting" }, { ColumnId_BaseCost, "Base Cost" }, { ColumnId_School, "School" }, { ColumnId_Particle, "Particle" }, { ColumnId_CastingObject, "Casting Object" }, { ColumnId_HitObject, "Hit Object" }, { ColumnId_AreaObject, "Area Object" }, { ColumnId_BoltObject, "Bolt Object" }, { ColumnId_CastingSound, "Casting Sound" }, { ColumnId_HitSound, "Hit Sound" }, { ColumnId_AreaSound, "Area Sound" }, { ColumnId_BoltSound, "Bolt Sound" }, { ColumnId_PathgridPoints, "Points" }, { ColumnId_PathgridIndex, "pIndex" }, { ColumnId_PathgridPosX, "X" }, { ColumnId_PathgridPosY, "Y" }, { ColumnId_PathgridPosZ, "Z" }, { ColumnId_PathgridEdges, "Edges" }, { ColumnId_PathgridEdgeIndex, "eIndex" }, { ColumnId_PathgridEdge0, "Point 0" }, { ColumnId_PathgridEdge1, "Point 1" }, { ColumnId_RegionSounds, "Sounds" }, { ColumnId_SoundName, "Sound Name" }, { ColumnId_SoundChance, "Chance" }, { ColumnId_FactionReactions, "Reactions" }, //{ ColumnId_FactionID, "Faction ID" }, { ColumnId_FactionReaction, "Reaction" }, { ColumnId_EffectList, "Effects" }, { ColumnId_EffectId, "Effect" }, //{ ColumnId_EffectAttribute, "Attrib" }, { ColumnId_EffectRange, "Range" }, { ColumnId_EffectArea, "Area" }, { ColumnId_AiPackageList, "Ai Packages" }, { ColumnId_AiPackageType, "Package" }, { ColumnId_AiWanderDist, "Wander Dist" }, { ColumnId_AiDuration, "Ai Duration" }, { ColumnId_AiWanderToD, "Wander ToD" }, //{ ColumnId_AiWanderIdle, "Wander Idle" }, { ColumnId_AiWanderRepeat, "Wander Repeat" }, { ColumnId_AiActivateName, "Activate" }, { ColumnId_AiTargetId, "Target ID" }, { ColumnId_AiTargetCell, "Target Cell" }, { ColumnId_PartRefList, "Part Reference" }, { ColumnId_PartRefType, "Type" }, { ColumnId_PartRefMale, "Male Part" }, { ColumnId_PartRefFemale, "Female Part" }, { ColumnId_LevelledList,"Levelled List" }, { ColumnId_LevelledItemId,"Levelled Item" }, { ColumnId_LevelledItemLevel,"Item Level" }, { ColumnId_LevelledItemType, "Calculate all levels <= player" }, { ColumnId_LevelledItemTypeEach, "Select a new item each instance" }, { ColumnId_LevelledItemChanceNone, "Chance None" }, { ColumnId_PowerList, "Powers" }, { ColumnId_Skill, "Skill" }, { ColumnId_InfoList, "Info List" }, { ColumnId_InfoCondition, "Info Conditions" }, { ColumnId_InfoCondFunc, "Function" }, { ColumnId_InfoCondVar, "Func/Variable" }, { ColumnId_InfoCondComp, "Comp" }, { ColumnId_InfoCondValue, "Values" }, { ColumnId_OriginalCell, "Original Cell" }, { ColumnId_NpcAttributes, "NPC Attributes" }, { ColumnId_NpcSkills, "NPC Skill" }, { ColumnId_UChar, "Value [0..255]" }, { ColumnId_NpcMisc, "NPC Misc" }, { ColumnId_Level, "Level" }, { ColumnId_NpcFactionID, "Faction ID" }, { ColumnId_Mana, "Mana" }, { ColumnId_Fatigue, "Fatigue" }, { ColumnId_NpcDisposition, "NPC Disposition" }, { ColumnId_NpcReputation, "Reputation" }, { ColumnId_NpcRank, "NPC Rank" }, { ColumnId_Gold, "Gold" }, { ColumnId_NpcPersistence, "Persistent" }, { ColumnId_RaceAttributes, "Race Attributes" }, { ColumnId_Male, "Male" }, { ColumnId_RaceSkillBonus, "Skill Bonus" }, { ColumnId_RaceBonus, "Bonus" }, { ColumnId_Interior, "Interior" }, { ColumnId_Ambient, "Ambient" }, { ColumnId_Sunlight, "Sunlight" }, { ColumnId_Fog, "Fog" }, { ColumnId_FogDensity, "Fog Density" }, { ColumnId_WaterLevel, "Water Level" }, { ColumnId_MapColor, "Map Color" }, { ColumnId_FileFormat, "File Format" }, { ColumnId_FileDescription, "File Description" }, { ColumnId_Author, "Author" }, { ColumnId_CreatureAttributes, "Creature Attributes" }, { ColumnId_AttributeValue, "Attrib Value" }, { ColumnId_CreatureAttack, "Creature Attack" }, { ColumnId_MinAttack, "Min Attack" }, { ColumnId_MaxAttack, "Max Attack" }, { ColumnId_CreatureMisc, "Creature Misc" }, { ColumnId_Idle1, "Idle 1" }, { ColumnId_Idle2, "Idle 2" }, { ColumnId_Idle3, "Idle 3" }, { ColumnId_Idle4, "Idle 4" }, { ColumnId_Idle5, "Idle 5" }, { ColumnId_Idle6, "Idle 6" }, { ColumnId_Idle7, "Idle 7" }, { ColumnId_Idle8, "Idle 8" }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue3, "Use value 3" }, { ColumnId_UseValue4, "Use value 4" }, { ColumnId_Attribute1, "Attribute 1" }, { ColumnId_Attribute2, "Attribute 2" }, { ColumnId_MajorSkill1, "Major Skill 1" }, { ColumnId_MajorSkill2, "Major Skill 2" }, { ColumnId_MajorSkill3, "Major Skill 3" }, { ColumnId_MajorSkill4, "Major Skill 4" }, { ColumnId_MajorSkill5, "Major Skill 5" }, { ColumnId_MinorSkill1, "Minor Skill 1" }, { ColumnId_MinorSkill2, "Minor Skill 2" }, { ColumnId_MinorSkill3, "Minor Skill 3" }, { ColumnId_MinorSkill4, "Minor Skill 4" }, { ColumnId_MinorSkill5, "Minor Skill 5" }, { ColumnId_Skill1, "Skill 1" }, { ColumnId_Skill2, "Skill 2" }, { ColumnId_Skill3, "Skill 3" }, { ColumnId_Skill4, "Skill 4" }, { ColumnId_Skill5, "Skill 5" }, { ColumnId_Skill6, "Skill 6" }, { ColumnId_Skill7, "Skill 7" }, { -1, 0 } // end marker }; } } std::string CSMWorld::Columns::getName (ColumnId column) { for (int i=0; sNames[i].mName; ++i) if (column==sNames[i].mId) return sNames[i].mName; return ""; } int CSMWorld::Columns::getId (const std::string& name) { std::string name2 = Misc::StringUtils::lowerCase (name); for (int i=0; sNames[i].mName; ++i) if (Misc::StringUtils::ciEqual(sNames[i].mName, name2)) return sNames[i].mId; return -1; } namespace { static const char *sSpecialisations[] = { "Combat", "Magic", "Stealth", 0 }; // see ESM::Attribute::AttributeID in static const char *sAttributes[] = { "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", "Luck", 0 }; static const char *sSpellTypes[] = { "Spell", "Ability", "Blight", "Disease", "Curse", "Power", 0 }; static const char *sApparatusTypes[] = { "Mortar & Pestle", "Albemic", "Calcinator", "Retort", 0 }; static const char *sArmorTypes[] = { "Helmet", "Cuirass", "Left Pauldron", "Right Pauldron", "Greaves", "Boots", "Left Gauntlet", "Right Gauntlet", "Shield", "Left Bracer", "Right Bracer", 0 }; static const char *sClothingTypes[] = { "Pants", "Shoes", "Shirt", "Belt", "Robe", "Right Glove", "Left Glove", "Skirt", "Ring", "Amulet", 0 }; static const char *sCreatureTypes[] = { "Creature", "Daedra", "Undead", "Humanoid", 0 }; static const char *sWeaponTypes[] = { "Short Blade 1H", "Long Blade 1H", "Long Blade 2H", "Blunt 1H", "Blunt 2H Close", "Blunt 2H Wide", "Spear 2H", "Axe 1H", "Axe 2H", "Bow", "Crossbow", "Thrown", "Arrow", "Bolt", 0 }; static const char *sModificationEnums[] = { "Base", "Modified", "Added", "Deleted", "Deleted", 0 }; static const char *sVarTypeEnums[] = { "unknown", "none", "short", "integer", "long", "float", "string", 0 }; static const char *sDialogueTypeEnums[] = { "Topic", "Voice", "Greeting", "Persuasion", 0 }; static const char *sQuestStatusTypes[] = { "None", "Name", "Finished", "Restart", 0 }; static const char *sGenderEnums[] = { "Male", "Female", 0 }; static const char *sEnchantmentTypes[] = { "Cast Once", "When Strikes", "When Used", "Constant Effect", 0 }; static const char *sBodyPartTypes[] = { "Head", "Hair", "Neck", "Chest", "Groin", "Hand", "Wrist", "Forearm", "Upper Arm", "Foot", "Ankle", "Knee", "Upper Leg", "Clavicle", "Tail", 0 }; static const char *sMeshTypes[] = { "Skin", "Clothing", "Armour", 0 }; static const char *sSoundGeneratorType[] = { "Left Foot", "Right Foot", "Swim Left", "Swim Right", "Moan", "Roar", "Scream", "Land", 0 }; static const char *sSchools[] = { "Alteration", "Conjuration", "Destruction", "Illusion", "Mysticism", "Restoration", 0 }; // impact from magic effects, see ESM::Skill::SkillEnum in static const char *sSkills[] = { "Block", "Armorer", "MediumArmor", "HeavyArmor", "BluntWeapon", "LongBlade", "Axe", "Spear", "Athletics", "Enchant", "Destruction", "Alteration", "Illusion", "Conjuration", "Mysticism", "Restoration", "Alchemy", "Unarmored", "Security", "Sneak", "Acrobatics", "LightArmor", "ShortBlade", "Marksman", "Mercantile", "Speechcraft", "HandToHand", 0 }; // range of magic effects, see ESM::RangeType in static const char *sEffectRange[] = { "Self", "Touch", "Target", 0 }; // magic effect names, see ESM::MagicEffect::Effects in static const char *sEffectId[] = { "WaterBreathing", "SwiftSwim", "WaterWalking", "Shield", "FireShield", "LightningShield", "FrostShield", "Burden", "Feather", "Jump", "Levitate", "SlowFall", "Lock", "Open", "FireDamage", "ShockDamage", "FrostDamage", "DrainAttribute", "DrainHealth", "DrainMagicka", "DrainFatigue", "DrainSkill", "DamageAttribute", "DamageHealth", "DamageMagicka", "DamageFatigue", "DamageSkill", "Poison", "WeaknessToFire", "WeaknessToFrost", "WeaknessToShock", "WeaknessToMagicka", "WeaknessToCommonDisease", "WeaknessToBlightDisease", "WeaknessToCorprusDisease", "WeaknessToPoison", "WeaknessToNormalWeapons", "DisintegrateWeapon", "DisintegrateArmor", "Invisibility", "Chameleon", "Light", "Sanctuary", "NightEye", "Charm", "Paralyze", "Silence", "Blind", "Sound", "CalmHumanoid", "CalmCreature", "FrenzyHumanoid", "FrenzyCreature", "DemoralizeHumanoid", "DemoralizeCreature", "RallyHumanoid", "RallyCreature", "Dispel", "Soultrap", "Telekinesis", "Mark", "Recall", "DivineIntervention", "AlmsiviIntervention", "DetectAnimal", "DetectEnchantment", "DetectKey", "SpellAbsorption", "Reflect", "CureCommonDisease", "CureBlightDisease", "CureCorprusDisease", "CurePoison", "CureParalyzation", "RestoreAttribute", "RestoreHealth", "RestoreMagicka", "RestoreFatigue", "RestoreSkill", "FortifyAttribute", "FortifyHealth", "FortifyMagicka", "FortifyFatigue", "FortifySkill", "FortifyMaximumMagicka", "AbsorbAttribute", "AbsorbHealth", "AbsorbMagicka", "AbsorbFatigue", "AbsorbSkill", "ResistFire", "ResistFrost", "ResistShock", "ResistMagicka", "ResistCommonDisease", "ResistBlightDisease", "ResistCorprusDisease", "ResistPoison", "ResistNormalWeapons", "ResistParalysis", "RemoveCurse", "TurnUndead", "SummonScamp", "SummonClannfear", "SummonDaedroth", "SummonDremora", "SummonAncestralGhost", "SummonSkeletalMinion", "SummonBonewalker", "SummonGreaterBonewalker", "SummonBonelord", "SummonWingedTwilight", "SummonHunger", "SummonGoldenSaint", "SummonFlameAtronach", "SummonFrostAtronach", "SummonStormAtronach", "FortifyAttack", "CommandCreature", "CommandHumanoid", "BoundDagger", "BoundLongsword", "BoundMace", "BoundBattleAxe", "BoundSpear", "BoundLongbow", "ExtraSpell", "BoundCuirass", "BoundHelm", "BoundBoots", "BoundShield", "BoundGloves", "Corprus", "Vampirism", "SummonCenturionSphere", "SunDamage", "StuntedMagicka", "SummonFabricant", "SummonWolf", "SummonBear", "SummonBonewolf", "SummonCreature04", "SummonCreature05", 0 }; // see ESM::PartReferenceType in static const char *sPartRefType[] = { "Head", "Hair", "Neck", "Cuirass", "Groin", "Skirt", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield", "Right Forearm", "Left Forearm", "Right Upperarm", "Left Upperarm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Leg", "Left Leg", "Right Pauldron", "Left Pauldron", "Weapon", "Tail", 0 }; // see the enums in static const char *sAiPackageType[] = { "AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0 }; static const char *sInfoCondFunc[] = { " ", "Function", "Global", "Local", "Journal", "Item", "Dead", "Not ID", "Not Faction", "Not Class", "Not Race", "Not Cell", "Not Local", 0 }; static const char *sInfoCondComp[] = { "!=", "<", "<=", "=", ">", ">=", 0 }; const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) { case CSMWorld::Columns::ColumnId_Specialisation: return sSpecialisations; case CSMWorld::Columns::ColumnId_Attribute: return sAttributes; case CSMWorld::Columns::ColumnId_SpellType: return sSpellTypes; case CSMWorld::Columns::ColumnId_ApparatusType: return sApparatusTypes; case CSMWorld::Columns::ColumnId_ArmorType: return sArmorTypes; case CSMWorld::Columns::ColumnId_ClothingType: return sClothingTypes; case CSMWorld::Columns::ColumnId_CreatureType: return sCreatureTypes; case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes; case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums; case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums; case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums; case CSMWorld::Columns::ColumnId_QuestStatusType: return sQuestStatusTypes; case CSMWorld::Columns::ColumnId_Gender: return sGenderEnums; case CSMWorld::Columns::ColumnId_EnchantmentType: return sEnchantmentTypes; case CSMWorld::Columns::ColumnId_BodyPartType: return sBodyPartTypes; case CSMWorld::Columns::ColumnId_MeshType: return sMeshTypes; case CSMWorld::Columns::ColumnId_SoundGeneratorType: return sSoundGeneratorType; case CSMWorld::Columns::ColumnId_School: return sSchools; case CSMWorld::Columns::ColumnId_Skill: return sSkills; case CSMWorld::Columns::ColumnId_EffectRange: return sEffectRange; case CSMWorld::Columns::ColumnId_EffectId: return sEffectId; case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType; case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType; case CSMWorld::Columns::ColumnId_InfoCondFunc: return sInfoCondFunc; // FIXME: don't have dynamic value enum delegate, use Display_String for now //case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond; case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp; default: return 0; } } } bool CSMWorld::Columns::hasEnums (ColumnId column) { return getEnumNames (column)!=0 || column==ColumnId_RecordType; } std::vector CSMWorld::Columns::getEnums (ColumnId column) { std::vector enums; if (const char **table = getEnumNames (column)) for (int i=0; table[i]; ++i) enums.push_back (table[i]); else if (column==ColumnId_RecordType) { enums.push_back (""); // none for (int i=UniversalId::Type_None+1; i (i)).getTypeName()); } return enums; } openmw-openmw-0.38.0/apps/opencs/model/world/columns.hpp000066400000000000000000000325121264522266000232740ustar00rootroot00000000000000#ifndef CSM_WOLRD_COLUMNS_H #define CSM_WOLRD_COLUMNS_H #include #include #include "columnbase.hpp" namespace CSMWorld { namespace Columns { enum ColumnId { ColumnId_Value = 0, ColumnId_Id = 1, ColumnId_Modification = 2, ColumnId_RecordType = 3, ColumnId_ValueType = 4, ColumnId_Description = 5, ColumnId_Specialisation = 6, ColumnId_Attribute = 7, ColumnId_Name = 8, ColumnId_Playable = 9, ColumnId_Hidden = 10, ColumnId_MaleWeight = 11, ColumnId_FemaleWeight = 12, ColumnId_MaleHeight = 13, ColumnId_FemaleHeight = 14, ColumnId_Volume = 15, ColumnId_MinRange = 16, ColumnId_MaxRange = 17, ColumnId_SoundFile = 18, ColumnId_MapColour = 19, ColumnId_SleepEncounter = 20, ColumnId_Texture = 21, ColumnId_SpellType = 22, ColumnId_Cost = 23, ColumnId_ScriptText = 24, ColumnId_Region = 25, ColumnId_Cell = 26, ColumnId_Scale = 27, ColumnId_Owner = 28, ColumnId_Soul = 29, ColumnId_Faction = 30, ColumnId_FactionIndex = 31, ColumnId_Charges = 32, ColumnId_Enchantment = 33, ColumnId_CoinValue = 34, ColumnId_Teleport = 35, ColumnId_TeleportCell = 36, ColumnId_LockLevel = 37, ColumnId_Key = 38, ColumnId_Trap = 39, ColumnId_BeastRace = 40, ColumnId_AutoCalc = 41, ColumnId_StarterSpell = 42, ColumnId_AlwaysSucceeds = 43, ColumnId_SleepForbidden = 44, ColumnId_InteriorWater = 45, ColumnId_InteriorSky = 46, ColumnId_Model = 47, ColumnId_Script = 48, ColumnId_Icon = 49, ColumnId_Weight = 50, ColumnId_EnchantmentPoints = 51, ColumnId_Quality = 52, // unused ColumnId_AiHello = 54, ColumnId_AiFlee = 55, ColumnId_AiFight = 56, ColumnId_AiAlarm = 57, ColumnId_BuysWeapons = 58, ColumnId_BuysArmor = 59, ColumnId_BuysClothing = 60, ColumnId_BuysBooks = 61, ColumnId_BuysIngredients = 62, ColumnId_BuysLockpicks = 63, ColumnId_BuysProbes = 64, ColumnId_BuysLights = 65, ColumnId_BuysApparati = 66, ColumnId_BuysRepairItems = 67, ColumnId_BuysMiscItems = 68, ColumnId_BuysPotions = 69, ColumnId_BuysMagicItems = 70, ColumnId_SellsSpells = 71, ColumnId_Trainer = 72, ColumnId_Spellmaking = 73, ColumnId_EnchantingService = 74, ColumnId_RepairService = 75, ColumnId_ApparatusType = 76, ColumnId_ArmorType = 77, ColumnId_Health = 78, ColumnId_ArmorValue = 79, ColumnId_Scroll = 80, ColumnId_ClothingType = 81, ColumnId_WeightCapacity = 82, ColumnId_OrganicContainer = 83, ColumnId_Respawn = 84, ColumnId_CreatureType = 85, ColumnId_SoulPoints = 86, ColumnId_OriginalCreature = 87, ColumnId_Biped = 88, ColumnId_HasWeapon = 89, // unused ColumnId_Swims = 91, ColumnId_Flies = 92, ColumnId_Walks = 93, ColumnId_Essential = 94, ColumnId_SkeletonBlood = 95, ColumnId_MetalBlood = 96, ColumnId_OpenSound = 97, ColumnId_CloseSound = 98, ColumnId_Duration = 99, ColumnId_Radius = 100, ColumnId_Colour = 101, ColumnId_Sound = 102, ColumnId_Dynamic = 103, ColumnId_Portable = 104, ColumnId_NegativeLight = 105, ColumnId_Flickering = 106, ColumnId_SlowFlickering = 107, ColumnId_Pulsing = 108, ColumnId_SlowPulsing = 109, ColumnId_Fire = 110, ColumnId_OffByDefault = 111, ColumnId_IsKey = 112, ColumnId_Race = 113, ColumnId_Class = 114, Columnid_Hair = 115, ColumnId_Head = 116, ColumnId_Female = 117, ColumnId_WeaponType = 118, ColumnId_WeaponSpeed = 119, ColumnId_WeaponReach = 120, ColumnId_MinChop = 121, ColumnId_MaxChip = 122, Columnid_MinSlash = 123, ColumnId_MaxSlash = 124, ColumnId_MinThrust = 125, ColumnId_MaxThrust = 126, ColumnId_Magical = 127, ColumnId_Silver = 128, ColumnId_Filter = 129, ColumnId_PositionXPos = 130, ColumnId_PositionYPos = 131, ColumnId_PositionZPos = 132, ColumnId_PositionXRot = 133, ColumnId_PositionYRot = 134, ColumnId_PositionZRot = 135, ColumnId_DoorPositionXPos = 136, ColumnId_DoorPositionYPos = 137, ColumnId_DoorPositionZPos = 138, ColumnId_DoorPositionXRot = 139, ColumnId_DoorPositionYRot = 140, ColumnId_DoorPositionZRot = 141, ColumnId_DialogueType = 142, ColumnId_QuestIndex = 143, ColumnId_QuestStatusType = 144, ColumnId_QuestDescription = 145, ColumnId_Topic = 146, ColumnId_Journal = 147, ColumnId_Actor = 148, ColumnId_PcFaction = 149, ColumnId_Response = 150, ColumnId_Disposition = 151, ColumnId_Rank = 152, ColumnId_Gender = 153, ColumnId_PcRank = 154, ColumnId_ReferenceableId = 155, ColumnId_ContainerContent = 156, ColumnId_ItemCount = 157, ColumnId_InventoryItemId = 158, ColumnId_CombatState = 159, ColumnId_MagicState = 160, ColumnId_StealthState = 161, ColumnId_EnchantmentType = 162, ColumnId_Vampire = 163, ColumnId_BodyPartType = 164, ColumnId_MeshType = 165, ColumnId_ActorInventory = 166, ColumnId_SpellList = 167, ColumnId_SpellId = 168, ColumnId_NpcDestinations = 169, ColumnId_DestinationCell = 170, ColumnId_PosX = 171, // these are float ColumnId_PosY = 172, // these are float ColumnId_PosZ = 173, // these are float ColumnId_RotX = 174, ColumnId_RotY = 175, ColumnId_RotZ = 176, // unused ColumnId_OwnerGlobal = 178, ColumnId_DefaultProfile = 179, ColumnId_BypassNewGame = 180, ColumnId_GlobalProfile = 181, ColumnId_RefNumCounter = 182, ColumnId_RefNum = 183, ColumnId_Creature = 184, ColumnId_SoundGeneratorType = 185, ColumnId_AllowSpellmaking = 186, ColumnId_AllowEnchanting = 187, ColumnId_BaseCost = 188, ColumnId_School = 189, ColumnId_Particle = 190, ColumnId_CastingObject = 191, ColumnId_HitObject = 192, ColumnId_AreaObject = 193, ColumnId_BoltObject = 194, ColumnId_CastingSound = 195, ColumnId_HitSound = 196, ColumnId_AreaSound = 197, ColumnId_BoltSound = 198, ColumnId_PathgridPoints = 199, ColumnId_PathgridIndex = 200, ColumnId_PathgridPosX = 201, // these are int ColumnId_PathgridPosY = 202, // these are int ColumnId_PathgridPosZ = 203, // these are int ColumnId_PathgridEdges = 204, ColumnId_PathgridEdgeIndex = 205, ColumnId_PathgridEdge0 = 206, ColumnId_PathgridEdge1 = 207, ColumnId_RegionSounds = 208, ColumnId_SoundName = 209, ColumnId_SoundChance = 210, ColumnId_FactionReactions = 211, //ColumnId_FactionID = 212, ColumnId_FactionReaction = 213, ColumnId_EffectList = 214, ColumnId_EffectId = 215, //ColumnId_EffectAttribute = 216, ColumnId_EffectRange = 217, ColumnId_EffectArea = 218, ColumnId_AiPackageList = 219, ColumnId_AiPackageType = 220, ColumnId_AiWanderDist = 221, ColumnId_AiDuration = 222, ColumnId_AiWanderToD = 223, // unused ColumnId_AiWanderRepeat = 225, ColumnId_AiActivateName = 226, // use ColumnId_PosX, etc for AI destinations ColumnId_AiTargetId = 227, ColumnId_AiTargetCell = 228, ColumnId_PartRefList = 229, ColumnId_PartRefType = 230, ColumnId_PartRefMale = 231, ColumnId_PartRefFemale = 232, ColumnId_LevelledList = 233, ColumnId_LevelledItemId = 234, ColumnId_LevelledItemLevel = 235, ColumnId_LevelledItemType = 236, ColumnId_LevelledItemTypeEach = 237, ColumnId_LevelledItemChanceNone = 238, ColumnId_PowerList = 239, ColumnId_Skill = 240, ColumnId_InfoList = 241, ColumnId_InfoCondition = 242, ColumnId_InfoCondFunc = 243, ColumnId_InfoCondVar = 244, ColumnId_InfoCondComp = 245, ColumnId_InfoCondValue = 246, ColumnId_OriginalCell = 247, ColumnId_NpcAttributes = 248, ColumnId_NpcSkills = 249, ColumnId_UChar = 250, ColumnId_NpcMisc = 251, ColumnId_Level = 252, ColumnId_NpcFactionID = 253, // unused ColumnId_Mana = 255, ColumnId_Fatigue = 256, ColumnId_NpcDisposition = 257, ColumnId_NpcReputation = 258, ColumnId_NpcRank = 259, ColumnId_Gold = 260, ColumnId_NpcPersistence = 261, ColumnId_RaceAttributes = 262, ColumnId_Male = 263, // unused ColumnId_RaceSkillBonus = 265, // unused ColumnId_RaceBonus = 267, ColumnId_Interior = 268, ColumnId_Ambient = 269, ColumnId_Sunlight = 270, ColumnId_Fog = 271, ColumnId_FogDensity = 272, ColumnId_WaterLevel = 273, ColumnId_MapColor = 274, ColumnId_FileFormat = 275, ColumnId_FileDescription = 276, ColumnId_Author = 277, ColumnId_MinMagnitude = 278, ColumnId_MaxMagnitude = 279, ColumnId_CreatureAttributes = 280, ColumnId_AttributeValue = 281, ColumnId_CreatureAttack = 282, ColumnId_MinAttack = 283, ColumnId_MaxAttack = 284, ColumnId_CreatureMisc = 285, ColumnId_Idle1 = 286, ColumnId_Idle2 = 287, ColumnId_Idle3 = 288, ColumnId_Idle4 = 289, ColumnId_Idle5 = 290, ColumnId_Idle6 = 291, ColumnId_Idle7 = 292, ColumnId_Idle8 = 293, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. ColumnId_UseValue1 = 0x10000, ColumnId_UseValue2 = 0x10001, ColumnId_UseValue3 = 0x10002, ColumnId_UseValue4 = 0x10003, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of attributes. Note that this is not the number of different // attributes, but the number of attributes that can be references from a record. ColumnId_Attribute1 = 0x20000, ColumnId_Attribute2 = 0x20001, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of skills. Note that this is not the number of different // skills, but the number of skills that can be references from a record. ColumnId_MajorSkill1 = 0x30000, ColumnId_MajorSkill2 = 0x30001, ColumnId_MajorSkill3 = 0x30002, ColumnId_MajorSkill4 = 0x30003, ColumnId_MajorSkill5 = 0x30004, ColumnId_MinorSkill1 = 0x40000, ColumnId_MinorSkill2 = 0x40001, ColumnId_MinorSkill3 = 0x40002, ColumnId_MinorSkill4 = 0x40003, ColumnId_MinorSkill5 = 0x40004, ColumnId_Skill1 = 0x50000, ColumnId_Skill2 = 0x50001, ColumnId_Skill3 = 0x50002, ColumnId_Skill4 = 0x50003, ColumnId_Skill5 = 0x50004, ColumnId_Skill6 = 0x50005, ColumnId_Skill7 = 0x50006 }; std::string getName (ColumnId column); int getId (const std::string& name); ///< Will return -1 for an invalid name. bool hasEnums (ColumnId column); std::vector getEnums (ColumnId column); ///< Returns an empty vector, if \a column isn't an enum type column. } } #endif openmw-openmw-0.38.0/apps/opencs/model/world/commanddispatcher.cpp000066400000000000000000000235601264522266000252770ustar00rootroot00000000000000#include "commanddispatcher.hpp" #include #include #include #include "../doc/document.hpp" #include "idtable.hpp" #include "record.hpp" #include "commands.hpp" #include "idtableproxymodel.hpp" std::vector CSMWorld::CommandDispatcher::getDeletableRecords() const { std::vector result; IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification); for (std::vector::const_iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { int row = model.getModelIndex (*iter, 0).row(); // check record state RecordBase::State state = static_cast ( model.data (model.index (row, stateColumnIndex)).toInt()); if (state==RecordBase::State_Deleted) continue; // check other columns (only relevant for a subset of the tables) int dialogueTypeIndex = model.searchColumnIndex (Columns::ColumnId_DialogueType); if (dialogueTypeIndex!=-1) { int type = model.data (model.index (row, dialogueTypeIndex)).toInt(); if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) continue; } result.push_back (*iter); } return result; } std::vector CSMWorld::CommandDispatcher::getRevertableRecords() const { std::vector result; IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); /// \todo Reverting temporarily disabled on tables that support reordering, because /// revert logic currently can not handle reordering. if (model.getFeatures() & IdTable::Feature_ReorderWithinTopic) return result; int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification); for (std::vector::const_iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { int row = model.getModelIndex (*iter, 0).row(); // check record state RecordBase::State state = static_cast ( model.data (model.index (row, stateColumnIndex)).toInt()); if (state==RecordBase::State_BaseOnly) continue; result.push_back (*iter); } return result; } CSMWorld::CommandDispatcher::CommandDispatcher (CSMDoc::Document& document, const CSMWorld::UniversalId& id, QObject *parent) : QObject (parent), mLocked (false), mDocument (document), mId (id) {} void CSMWorld::CommandDispatcher::setEditLock (bool locked) { mLocked = locked; } void CSMWorld::CommandDispatcher::setSelection (const std::vector& selection) { mSelection = selection; std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::lowerCaseInPlace); std::sort (mSelection.begin(), mSelection.end()); } void CSMWorld::CommandDispatcher::setExtendedTypes (const std::vector& types) { mExtendedTypes = types; } bool CSMWorld::CommandDispatcher::canDelete() const { if (mLocked) return false; return getDeletableRecords().size()!=0; } bool CSMWorld::CommandDispatcher::canRevert() const { if (mLocked) return false; return getRevertableRecords().size()!=0; } std::vector CSMWorld::CommandDispatcher::getExtendedTypes() const { std::vector tables; if (mId==UniversalId::Type_Cells) { tables.push_back (mId); tables.push_back (UniversalId::Type_References); /// \todo add other cell-specific types } return tables; } void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_) { if (mLocked) return; std::auto_ptr modifyCell; int columnId = model->data (index, ColumnBase::Role_ColumnId).toInt(); if (columnId==Columns::ColumnId_PositionXPos || columnId==Columns::ColumnId_PositionYPos) { IdTableProxyModel *proxy = dynamic_cast (model); int row = proxy ? proxy->mapToSource (index).row() : index.row(); // This is not guaranteed to be the same as \a model, since a proxy could be used. IdTable& model2 = dynamic_cast (*mDocument.getData().getTableModel (mId)); int cellColumn = model2.searchColumnIndex (Columns::ColumnId_Cell); if (cellColumn!=-1) { QModelIndex cellIndex = model2.index (row, cellColumn); std::string cellId = model2.data (cellIndex).toString().toUtf8().data(); if (cellId.find ('#')!=std::string::npos) { // Need to recalculate the cell modifyCell.reset (new UpdateCellCommand (model2, row)); } } } std::auto_ptr modifyData ( new CSMWorld::ModifyCommand (*model, index, new_)); if (modifyCell.get()) { mDocument.getUndoStack().beginMacro (modifyData->text()); mDocument.getUndoStack().push (modifyData.release()); mDocument.getUndoStack().push (modifyCell.release()); mDocument.getUndoStack().endMacro(); } else mDocument.getUndoStack().push (modifyData.release()); } void CSMWorld::CommandDispatcher::executeDelete() { if (mLocked) return; std::vector rows = getDeletableRecords(); if (rows.empty()) return; IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); int columnIndex = model.findColumnIndex (Columns::ColumnId_Id); if (rows.size()>1) mDocument.getUndoStack().beginMacro (tr ("Delete multiple records")); for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) { std::string id = model.data (model.getModelIndex (*iter, columnIndex)). toString().toUtf8().constData(); if (mId.getType() == UniversalId::Type_Referenceables) { mDocument.getUndoStack().push ( new CSMWorld::DeleteCommand (model, id, static_cast(model.data (model.index ( model.getModelIndex (id, columnIndex).row(), model.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType))).toInt()))); } else mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id)); } if (rows.size()>1) mDocument.getUndoStack().endMacro(); } void CSMWorld::CommandDispatcher::executeRevert() { if (mLocked) return; std::vector rows = getRevertableRecords(); if (rows.empty()) return; IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); int columnIndex = model.findColumnIndex (Columns::ColumnId_Id); if (rows.size()>1) mDocument.getUndoStack().beginMacro (tr ("Revert multiple records")); for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) { std::string id = model.data (model.getModelIndex (*iter, columnIndex)). toString().toUtf8().constData(); mDocument.getUndoStack().push (new CSMWorld::RevertCommand (model, id)); } if (rows.size()>1) mDocument.getUndoStack().endMacro(); } void CSMWorld::CommandDispatcher::executeExtendedDelete() { if (mExtendedTypes.size()>1) mDocument.getUndoStack().beginMacro (tr ("Extended delete of multiple records")); for (std::vector::const_iterator iter (mExtendedTypes.begin()); iter!=mExtendedTypes.end(); ++iter) { if (*iter==mId) executeDelete(); else if (*iter==UniversalId::Type_References) { IdTable& model = dynamic_cast ( *mDocument.getData().getTableModel (*iter)); const RefCollection& collection = mDocument.getData().getReferences(); int size = collection.getSize(); for (int i=size-1; i>=0; --i) { const Record& record = collection.getRecord (i); if (record.mState==RecordBase::State_Deleted) continue; if (!std::binary_search (mSelection.begin(), mSelection.end(), Misc::StringUtils::lowerCase (record.get().mCell))) continue; mDocument.getUndoStack().push ( new CSMWorld::DeleteCommand (model, record.get().mId)); } } } if (mExtendedTypes.size()>1) mDocument.getUndoStack().endMacro(); } void CSMWorld::CommandDispatcher::executeExtendedRevert() { if (mExtendedTypes.size()>1) mDocument.getUndoStack().beginMacro (tr ("Extended revert of multiple records")); for (std::vector::const_iterator iter (mExtendedTypes.begin()); iter!=mExtendedTypes.end(); ++iter) { if (*iter==mId) executeRevert(); else if (*iter==UniversalId::Type_References) { IdTable& model = dynamic_cast ( *mDocument.getData().getTableModel (*iter)); const RefCollection& collection = mDocument.getData().getReferences(); int size = collection.getSize(); for (int i=size-1; i>=0; --i) { const Record& record = collection.getRecord (i); if (!std::binary_search (mSelection.begin(), mSelection.end(), Misc::StringUtils::lowerCase (record.get().mCell))) continue; mDocument.getUndoStack().push ( new CSMWorld::RevertCommand (model, record.get().mId)); } } } if (mExtendedTypes.size()>1) mDocument.getUndoStack().endMacro(); } openmw-openmw-0.38.0/apps/opencs/model/world/commanddispatcher.hpp000066400000000000000000000043241264522266000253010ustar00rootroot00000000000000#ifndef CSM_WOLRD_COMMANDDISPATCHER_H #define CSM_WOLRD_COMMANDDISPATCHER_H #include #include #include "universalid.hpp" class QModelIndex; class QAbstractItemModel; namespace CSMDoc { class Document; } namespace CSMWorld { class CommandDispatcher : public QObject { Q_OBJECT bool mLocked; CSMDoc::Document& mDocument; UniversalId mId; std::vector mSelection; std::vector mExtendedTypes; std::vector getDeletableRecords() const; std::vector getRevertableRecords() const; public: CommandDispatcher (CSMDoc::Document& document, const CSMWorld::UniversalId& id, QObject *parent = 0); ///< \param id ID of the table the commands should operate on primarily. void setEditLock (bool locked); void setSelection (const std::vector& selection); void setExtendedTypes (const std::vector& types); ///< Set record lists selected by the user for extended operations. bool canDelete() const; bool canRevert() const; /// Return IDs of the record collection that can also be affected when /// operating on the record collection this dispatcher is used for. /// /// \note The returned collection contains the ID of the record collection this /// dispatcher is used for. However if that record collection does not support /// the extended mode, the returned vector will be empty instead. std::vector getExtendedTypes() const; /// Add a modify command to the undo stack. /// /// \attention model must either be a model for the table operated on by this /// dispatcher or a proxy of it. void executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_); public slots: void executeDelete(); void executeRevert(); void executeExtendedDelete(); void executeExtendedRevert(); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/commands.cpp000066400000000000000000000250341264522266000234110ustar00rootroot00000000000000#include "commands.hpp" #include #include #include #include #include #include "idtable.hpp" #include "idtree.hpp" #include "nestedtablewrapper.hpp" CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false) { if (QAbstractProxyModel *proxy = dynamic_cast (&model)) { // Replace proxy with actual model mIndex = proxy->mapToSource (index); mModel = proxy->sourceModel(); } if (mIndex.parent().isValid()) { setText ("Modify " + dynamic_cast(mModel)->nestedHeaderData ( mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } else { setText ("Modify " + mModel->headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } // Remember record state before the modification if (CSMWorld::IdTable *table = dynamic_cast(mModel)) { mHasRecordState = true; int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification); int rowIndex = mIndex.row(); if (mIndex.parent().isValid()) { rowIndex = mIndex.parent().row(); } mRecordStateIndex = table->index(rowIndex, stateColumnIndex); mOldRecordState = static_cast(table->data(mRecordStateIndex).toInt()); } } void CSMWorld::ModifyCommand::redo() { mOld = mModel->data (mIndex, Qt::EditRole); mModel->setData (mIndex, mNew); } void CSMWorld::ModifyCommand::undo() { mModel->setData (mIndex, mOld); if (mHasRecordState) { mModel->setData(mRecordStateIndex, mOldRecordState); } } void CSMWorld::CreateCommand::applyModifications() { for (std::map::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); if (!mNestedValues.empty()) { CSMWorld::IdTree *tree = dynamic_cast(&mModel); if (tree == NULL) { throw std::logic_error("CSMWorld::CreateCommand: Attempt to add nested values to the non-nested model"); } std::map >::const_iterator current = mNestedValues.begin(); std::map >::const_iterator end = mNestedValues.end(); for (; current != end; ++current) { QModelIndex index = tree->index(0, current->second.first, tree->getNestedModelIndex(mId, current->first)); tree->setData(index, current->second.second); } } } CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent) : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { setText (("Create record " + id).c_str()); } void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) { mValues[column] = value; } void CSMWorld::CreateCommand::addNestedValue(int parentColumn, int nestedColumn, const QVariant &value) { mNestedValues[parentColumn] = std::make_pair(nestedColumn, value); } void CSMWorld::CreateCommand::setType (UniversalId::Type type) { mType = type; } void CSMWorld::CreateCommand::redo() { mModel.addRecord (mId, mType); applyModifications(); } void CSMWorld::CreateCommand::undo() { mModel.removeRow (mModel.getModelIndex (mId, 0).row()); } CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent) : QUndoCommand (parent), mModel (model), mId (id), mOld (0) { setText (("Revert record " + id).c_str()); mOld = model.getRecord (id).clone(); } CSMWorld::RevertCommand::~RevertCommand() { delete mOld; } void CSMWorld::RevertCommand::redo() { int column = mModel.findColumnIndex (Columns::ColumnId_Modification); QModelIndex index = mModel.getModelIndex (mId, column); RecordBase::State state = static_cast (mModel.data (index).toInt()); if (state==RecordBase::State_ModifiedOnly) { mModel.removeRows (index.row(), 1); } else { mModel.setData (index, static_cast (RecordBase::State_BaseOnly)); } } void CSMWorld::RevertCommand::undo() { mModel.setRecord (mId, *mOld); } CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent) : QUndoCommand (parent), mModel (model), mId (id), mOld (0), mType(type) { setText (("Delete record " + id).c_str()); mOld = model.getRecord (id).clone(); } CSMWorld::DeleteCommand::~DeleteCommand() { delete mOld; } void CSMWorld::DeleteCommand::redo() { int column = mModel.findColumnIndex (Columns::ColumnId_Modification); QModelIndex index = mModel.getModelIndex (mId, column); RecordBase::State state = static_cast (mModel.data (index).toInt()); if (state==RecordBase::State_ModifiedOnly) { mModel.removeRows (index.row(), 1); } else { mModel.setData (index, static_cast (RecordBase::State_Deleted)); } } void CSMWorld::DeleteCommand::undo() { mModel.setRecord (mId, *mOld, mType); } CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector& newOrder) : mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) {} void CSMWorld::ReorderRowsCommand::redo() { mModel.reorderRows (mBaseIndex, mNewOrder); } void CSMWorld::ReorderRowsCommand::undo() { int size = static_cast (mNewOrder.size()); std::vector reverse (size); for (int i=0; i< size; ++i) reverse.at (mNewOrder[i]) = i; mModel.reorderRows (mBaseIndex, reverse); } CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model, const std::string& idOrigin, const std::string& idDestination, const CSMWorld::UniversalId::Type type, QUndoCommand* parent) : CreateCommand (model, idDestination, parent), mIdOrigin (idOrigin) { setType (type); setText ( ("Clone record " + idOrigin + " to the " + idDestination).c_str()); } void CSMWorld::CloneCommand::redo() { mModel.cloneRecord (mIdOrigin, mId, mType); applyModifications(); } void CSMWorld::CloneCommand::undo() { mModel.removeRow (mModel.getModelIndex (mId, 0).row()); } CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent) : QUndoCommand (parent), mModel (model), mRow (row) { setText ("Update cell ID"); } void CSMWorld::UpdateCellCommand::redo() { if (!mNew.isValid()) { int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell); mIndex = mModel.index (mRow, cellColumn); const int cellSize = 8192; QModelIndex xIndex = mModel.index ( mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos)); QModelIndex yIndex = mModel.index ( mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos)); int x = std::floor (mModel.data (xIndex).toFloat() / cellSize); int y = std::floor (mModel.data (yIndex).toFloat() / cellSize); std::ostringstream stream; stream << "#" << x << " " << y; mNew = QString::fromUtf8 (stream.str().c_str()); } mModel.setData (mIndex, mNew); } void CSMWorld::UpdateCellCommand::undo() { mModel.setData (mIndex, mOld); } CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent) : QUndoCommand(parent), NestedTableStoring(model, id, parentColumn), mModel(model), mId(id), mParentColumn(parentColumn), mNestedRow(nestedRow) { std::string title = model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); setText (("Delete row in " + title + " sub-table of " + mId).c_str()); QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this); } void CSMWorld::DeleteNestedCommand::redo() { QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); mModel.removeRows (mNestedRow, 1, parentIndex); mModifyParentCommand->redo(); } void CSMWorld::DeleteNestedCommand::undo() { QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); mModel.setNestedTable(parentIndex, getOld()); mModifyParentCommand->undo(); } CSMWorld::AddNestedCommand::AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent) : QUndoCommand(parent), NestedTableStoring(model, id, parentColumn), mModel(model), mId(id), mNewRow(nestedRow), mParentColumn(parentColumn) { std::string title = model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); setText (("Add row in " + title + " sub-table of " + mId).c_str()); QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this); } void CSMWorld::AddNestedCommand::redo() { QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); mModel.addNestedRow (parentIndex, mNewRow); mModifyParentCommand->redo(); } void CSMWorld::AddNestedCommand::undo() { QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn); mModel.setNestedTable(parentIndex, getOld()); mModifyParentCommand->undo(); } CSMWorld::NestedTableStoring::NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn) : mOld(model.nestedTable(model.getModelIndex(id, parentColumn))) {} CSMWorld::NestedTableStoring::~NestedTableStoring() { delete mOld; } const CSMWorld::NestedTableWrapperBase& CSMWorld::NestedTableStoring::getOld() const { return *mOld; } openmw-openmw-0.38.0/apps/opencs/model/world/commands.hpp000066400000000000000000000133471264522266000234220ustar00rootroot00000000000000#ifndef CSM_WOLRD_COMMANDS_H #define CSM_WOLRD_COMMANDS_H #include "record.hpp" #include #include #include #include #include #include #include "universalid.hpp" #include "nestedtablewrapper.hpp" class QModelIndex; class QAbstractItemModel; namespace CSMWorld { class IdTable; class IdTree; struct RecordBase; struct NestedTableWrapperBase; class ModifyCommand : public QUndoCommand { QAbstractItemModel *mModel; QModelIndex mIndex; QVariant mNew; QVariant mOld; bool mHasRecordState; QModelIndex mRecordStateIndex; CSMWorld::RecordBase::State mOldRecordState; public: ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand *parent = 0); virtual void redo(); virtual void undo(); }; class CreateCommand : public QUndoCommand { std::map mValues; std::map > mNestedValues; ///< Parameter order: a parent column, a nested column, a data. ///< A nested row has index of 0. protected: IdTable& mModel; std::string mId; UniversalId::Type mType; protected: /// Apply modifications set via addValue. void applyModifications(); public: CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); void setType (UniversalId::Type type); void addValue (int column, const QVariant& value); void addNestedValue(int parentColumn, int nestedColumn, const QVariant &value); virtual void redo(); virtual void undo(); }; class CloneCommand : public CreateCommand { std::string mIdOrigin; public: CloneCommand (IdTable& model, const std::string& idOrigin, const std::string& IdDestination, const UniversalId::Type type, QUndoCommand* parent = 0); virtual void redo(); virtual void undo(); }; class RevertCommand : public QUndoCommand { IdTable& mModel; std::string mId; RecordBase *mOld; // not implemented RevertCommand (const RevertCommand&); RevertCommand& operator= (const RevertCommand&); public: RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); virtual ~RevertCommand(); virtual void redo(); virtual void undo(); }; class DeleteCommand : public QUndoCommand { IdTable& mModel; std::string mId; RecordBase *mOld; UniversalId::Type mType; // not implemented DeleteCommand (const DeleteCommand&); DeleteCommand& operator= (const DeleteCommand&); public: DeleteCommand (IdTable& model, const std::string& id, UniversalId::Type type = UniversalId::Type_None, QUndoCommand *parent = 0); virtual ~DeleteCommand(); virtual void redo(); virtual void undo(); }; class ReorderRowsCommand : public QUndoCommand { IdTable& mModel; int mBaseIndex; std::vector mNewOrder; public: ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector& newOrder); virtual void redo(); virtual void undo(); }; /// \brief Update cell ID according to x/y-coordinates /// /// \note The new value will be calculated in the first call to redo instead of the /// constructor to accommodate multiple coordinate-affecting commands being executed /// in a macro. class UpdateCellCommand : public QUndoCommand { IdTable& mModel; int mRow; QModelIndex mIndex; QVariant mNew; // invalid, if new cell ID has not been calculated yet QVariant mOld; public: UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent = 0); virtual void redo(); virtual void undo(); }; class NestedTableStoring { NestedTableWrapperBase* mOld; public: NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn); ~NestedTableStoring(); protected: const NestedTableWrapperBase& getOld() const; }; class DeleteNestedCommand : public QUndoCommand, private NestedTableStoring { IdTree& mModel; std::string mId; int mParentColumn; int mNestedRow; // The command to redo/undo the Modified status of a record ModifyCommand *mModifyParentCommand; public: DeleteNestedCommand (IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0); virtual void redo(); virtual void undo(); }; class AddNestedCommand : public QUndoCommand, private NestedTableStoring { IdTree& mModel; std::string mId; int mNewRow; int mParentColumn; // The command to redo/undo the Modified status of a record ModifyCommand *mModifyParentCommand; public: AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0); virtual void redo(); virtual void undo(); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/data.cpp000066400000000000000000001450641264522266000225270ustar00rootroot00000000000000#include "data.hpp" #include #include #include #include #include #include #include #include "idtable.hpp" #include "idtree.hpp" #include "columnimp.hpp" #include "regionmap.hpp" #include "columns.hpp" #include "resourcesmanager.hpp" #include "resourcetable.hpp" #include "nestedcoladapterimp.hpp" void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update) { mModels.push_back (model); mModelIndex.insert (std::make_pair (type, model)); UniversalId::Type type2 = UniversalId::getParentType (type); if (type2!=UniversalId::Type_None) mModelIndex.insert (std::make_pair (type2, model)); if (update) { connect (model, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (dataChanged (const QModelIndex&, const QModelIndex&))); connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (rowsChanged (const QModelIndex&, int, int))); connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (rowsChanged (const QModelIndex&, int, int))); } } void CSMWorld::Data::appendIds (std::vector& ids, const CollectionBase& collection, bool listDeleted) { std::vector ids2 = collection.getIds (listDeleted); ids.insert (ids.end(), ids2.begin(), ids2.end()); } int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collection) { int number = 0; for (int i=0; i); mGlobals.addColumn (new RecordStateColumn); mGlobals.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Global)); mGlobals.addColumn (new VarTypeColumn (ColumnBase::Display_GlobalVarType)); mGlobals.addColumn (new VarValueColumn); mGmsts.addColumn (new StringIdColumn); mGmsts.addColumn (new RecordStateColumn); mGmsts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Gmst)); mGmsts.addColumn (new VarTypeColumn (ColumnBase::Display_GmstVarType)); mGmsts.addColumn (new VarValueColumn); mSkills.addColumn (new StringIdColumn); mSkills.addColumn (new RecordStateColumn); mSkills.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Skill)); mSkills.addColumn (new AttributeColumn); mSkills.addColumn (new SpecialisationColumn); for (int i=0; i<4; ++i) mSkills.addColumn (new UseValueColumn (i)); mSkills.addColumn (new DescriptionColumn); mClasses.addColumn (new StringIdColumn); mClasses.addColumn (new RecordStateColumn); mClasses.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Class)); mClasses.addColumn (new NameColumn); mClasses.addColumn (new AttributesColumn (0)); mClasses.addColumn (new AttributesColumn (1)); mClasses.addColumn (new SpecialisationColumn); for (int i=0; i<5; ++i) mClasses.addColumn (new SkillsColumn (i, true, true)); for (int i=0; i<5; ++i) mClasses.addColumn (new SkillsColumn (i, true, false)); mClasses.addColumn (new PlayableColumn); mClasses.addColumn (new DescriptionColumn); mFactions.addColumn (new StringIdColumn); mFactions.addColumn (new RecordStateColumn); mFactions.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Faction)); mFactions.addColumn (new NameColumn); mFactions.addColumn (new AttributesColumn (0)); mFactions.addColumn (new AttributesColumn (1)); mFactions.addColumn (new HiddenColumn); for (int i=0; i<7; ++i) mFactions.addColumn (new SkillsColumn (i)); // Faction Reactions mFactions.addColumn (new NestedParentColumn (Columns::ColumnId_FactionReactions)); index = mFactions.getColumns()-1; mFactions.addAdapter (std::make_pair(&mFactions.getColumn(index), new FactionReactionsAdapter ())); mFactions.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Faction, ColumnBase::Display_Faction)); mFactions.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_FactionReaction, ColumnBase::Display_Integer)); mRaces.addColumn (new StringIdColumn); mRaces.addColumn (new RecordStateColumn); mRaces.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Race)); mRaces.addColumn (new NameColumn); mRaces.addColumn (new DescriptionColumn); mRaces.addColumn (new FlagColumn (Columns::ColumnId_Playable, 0x1)); mRaces.addColumn (new FlagColumn (Columns::ColumnId_BeastRace, 0x2)); mRaces.addColumn (new WeightHeightColumn (true, true)); mRaces.addColumn (new WeightHeightColumn (true, false)); mRaces.addColumn (new WeightHeightColumn (false, true)); mRaces.addColumn (new WeightHeightColumn (false, false)); // Race spells mRaces.addColumn (new NestedParentColumn (Columns::ColumnId_PowerList)); index = mRaces.getColumns()-1; mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter ())); mRaces.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_Spell)); // Race attributes mRaces.addColumn (new NestedParentColumn (Columns::ColumnId_RaceAttributes, ColumnBase::Flag_Dialogue, true)); // fixed rows table index = mRaces.getColumns()-1; mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceAttributeAdapter())); mRaces.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute, ColumnBase::Flag_Dialogue, false)); mRaces.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Male, ColumnBase::Display_Integer)); mRaces.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Female, ColumnBase::Display_Integer)); // Race skill bonus mRaces.addColumn (new NestedParentColumn (Columns::ColumnId_RaceSkillBonus, ColumnBase::Flag_Dialogue, true)); // fixed rows table index = mRaces.getColumns()-1; mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceSkillsBonusAdapter())); mRaces.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId)); mRaces.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_RaceBonus, ColumnBase::Display_Integer)); mSounds.addColumn (new StringIdColumn); mSounds.addColumn (new RecordStateColumn); mSounds.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Sound)); mSounds.addColumn (new SoundParamColumn (SoundParamColumn::Type_Volume)); mSounds.addColumn (new SoundParamColumn (SoundParamColumn::Type_MinRange)); mSounds.addColumn (new SoundParamColumn (SoundParamColumn::Type_MaxRange)); mSounds.addColumn (new SoundFileColumn); mScripts.addColumn (new StringIdColumn); mScripts.addColumn (new RecordStateColumn); mScripts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Script)); mScripts.addColumn (new ScriptColumn (ScriptColumn::Type_File)); mRegions.addColumn (new StringIdColumn); mRegions.addColumn (new RecordStateColumn); mRegions.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Region)); mRegions.addColumn (new NameColumn); mRegions.addColumn (new MapColourColumn); mRegions.addColumn (new SleepListColumn); // Region Sounds mRegions.addColumn (new NestedParentColumn (Columns::ColumnId_RegionSounds)); index = mRegions.getColumns()-1; mRegions.addAdapter (std::make_pair(&mRegions.getColumn(index), new RegionSoundListAdapter ())); mRegions.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_SoundName, ColumnBase::Display_Sound)); mRegions.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_SoundChance, ColumnBase::Display_Integer)); mBirthsigns.addColumn (new StringIdColumn); mBirthsigns.addColumn (new RecordStateColumn); mBirthsigns.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Birthsign)); mBirthsigns.addColumn (new NameColumn); mBirthsigns.addColumn (new TextureColumn); mBirthsigns.addColumn (new DescriptionColumn); // Birthsign spells mBirthsigns.addColumn (new NestedParentColumn (Columns::ColumnId_PowerList)); index = mBirthsigns.getColumns()-1; mBirthsigns.addAdapter (std::make_pair(&mBirthsigns.getColumn(index), new SpellListAdapter ())); mBirthsigns.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_Spell)); mSpells.addColumn (new StringIdColumn); mSpells.addColumn (new RecordStateColumn); mSpells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Spell)); mSpells.addColumn (new NameColumn); mSpells.addColumn (new SpellTypeColumn); mSpells.addColumn (new CostColumn); mSpells.addColumn (new FlagColumn (Columns::ColumnId_AutoCalc, 0x1)); mSpells.addColumn (new FlagColumn (Columns::ColumnId_StarterSpell, 0x2)); mSpells.addColumn (new FlagColumn (Columns::ColumnId_AlwaysSucceeds, 0x4)); // Spell effects mSpells.addColumn (new NestedParentColumn (Columns::ColumnId_EffectList)); index = mSpells.getColumns()-1; mSpells.addAdapter (std::make_pair(&mSpells.getColumn(index), new EffectsListAdapter ())); mSpells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId)); mSpells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill)); mSpells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute)); mSpells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange)); mSpells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_EffectArea, ColumnBase::Display_String)); mSpells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); // reuse from light mSpells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_MinMagnitude, ColumnBase::Display_Integer)); mSpells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_MaxMagnitude, ColumnBase::Display_Integer)); mTopics.addColumn (new StringIdColumn); mTopics.addColumn (new RecordStateColumn); mTopics.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Topic)); mTopics.addColumn (new DialogueTypeColumn); mJournals.addColumn (new StringIdColumn); mJournals.addColumn (new RecordStateColumn); mJournals.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Journal)); mJournals.addColumn (new DialogueTypeColumn (true)); mTopicInfos.addColumn (new StringIdColumn (true)); mTopicInfos.addColumn (new RecordStateColumn); mTopicInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_TopicInfo)); mTopicInfos.addColumn (new TopicColumn (false)); mTopicInfos.addColumn (new ActorColumn); mTopicInfos.addColumn (new RaceColumn); mTopicInfos.addColumn (new ClassColumn); mTopicInfos.addColumn (new FactionColumn); mTopicInfos.addColumn (new CellColumn); mTopicInfos.addColumn (new DispositionColumn); mTopicInfos.addColumn (new RankColumn); mTopicInfos.addColumn (new GenderColumn); mTopicInfos.addColumn (new PcFactionColumn); mTopicInfos.addColumn (new PcRankColumn); mTopicInfos.addColumn (new SoundFileColumn); mTopicInfos.addColumn (new ResponseColumn); // Result script mTopicInfos.addColumn (new NestedParentColumn (Columns::ColumnId_InfoList, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); index = mTopicInfos.getColumns()-1; mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoListAdapter ())); mTopicInfos.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_ScriptText, ColumnBase::Display_ScriptLines)); // Special conditions mTopicInfos.addColumn (new NestedParentColumn (Columns::ColumnId_InfoCondition)); index = mTopicInfos.getColumns()-1; mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoConditionAdapter ())); mTopicInfos.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc)); // FIXME: don't have dynamic value enum delegate, use Display_String for now mTopicInfos.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_String)); mTopicInfos.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp)); mTopicInfos.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Value, ColumnBase::Display_Var)); mJournalInfos.addColumn (new StringIdColumn (true)); mJournalInfos.addColumn (new RecordStateColumn); mJournalInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_JournalInfo)); mJournalInfos.addColumn (new TopicColumn (true)); mJournalInfos.addColumn (new QuestStatusTypeColumn); mJournalInfos.addColumn (new QuestIndexColumn); mJournalInfos.addColumn (new QuestDescriptionColumn); mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); mCells.addColumn (new NameColumn); mCells.addColumn (new FlagColumn (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep)); mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); mCells.addColumn (new RegionColumn); mCells.addColumn (new RefNumCounterColumn); // Misc Cell data mCells.addColumn (new NestedParentColumn (Columns::ColumnId_Cell, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); index = mCells.getColumns()-1; mCells.addAdapter (std::make_pair(&mCells.getColumn(index), new CellListAdapter ())); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Interior, ColumnBase::Display_Boolean, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Ambient, ColumnBase::Display_Integer)); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Sunlight, ColumnBase::Display_Integer)); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Fog, ColumnBase::Display_Integer)); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_FogDensity, ColumnBase::Display_Float)); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_WaterLevel, ColumnBase::Display_Float)); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_MapColor, ColumnBase::Display_Integer)); mEnchantments.addColumn (new StringIdColumn); mEnchantments.addColumn (new RecordStateColumn); mEnchantments.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Enchantment)); mEnchantments.addColumn (new EnchantmentTypeColumn); mEnchantments.addColumn (new CostColumn); mEnchantments.addColumn (new ChargesColumn2); mEnchantments.addColumn (new AutoCalcColumn); // Enchantment effects mEnchantments.addColumn (new NestedParentColumn (Columns::ColumnId_EffectList)); index = mEnchantments.getColumns()-1; mEnchantments.addAdapter (std::make_pair(&mEnchantments.getColumn(index), new EffectsListAdapter ())); mEnchantments.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId)); mEnchantments.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill)); mEnchantments.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute)); mEnchantments.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange)); mEnchantments.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_EffectArea, ColumnBase::Display_String)); mEnchantments.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); // reuse from light mEnchantments.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_MinMagnitude, ColumnBase::Display_Integer)); mEnchantments.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_MaxMagnitude, ColumnBase::Display_Integer)); mBodyParts.addColumn (new StringIdColumn); mBodyParts.addColumn (new RecordStateColumn); mBodyParts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_BodyPart)); mBodyParts.addColumn (new BodyPartTypeColumn); mBodyParts.addColumn (new VampireColumn); mBodyParts.addColumn (new FlagColumn (Columns::ColumnId_Female, ESM::BodyPart::BPF_Female)); mBodyParts.addColumn (new FlagColumn (Columns::ColumnId_Playable, ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true)); int meshTypeFlags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh; MeshTypeColumn *meshTypeColumn = new MeshTypeColumn(meshTypeFlags); mBodyParts.addColumn (meshTypeColumn); mBodyParts.addColumn (new ModelColumn); mBodyParts.addColumn (new BodyPartRaceColumn(meshTypeColumn)); mSoundGens.addColumn (new StringIdColumn); mSoundGens.addColumn (new RecordStateColumn); mSoundGens.addColumn (new FixedRecordTypeColumn (UniversalId::Type_SoundGen)); mSoundGens.addColumn (new CreatureColumn); mSoundGens.addColumn (new SoundColumn); mSoundGens.addColumn (new SoundGeneratorTypeColumn); mMagicEffects.addColumn (new StringIdColumn); mMagicEffects.addColumn (new RecordStateColumn); mMagicEffects.addColumn (new FixedRecordTypeColumn (UniversalId::Type_MagicEffect)); mMagicEffects.addColumn (new SchoolColumn); mMagicEffects.addColumn (new BaseCostColumn); mMagicEffects.addColumn (new EffectTextureColumn (Columns::ColumnId_Icon)); mMagicEffects.addColumn (new EffectTextureColumn (Columns::ColumnId_Particle)); mMagicEffects.addColumn (new EffectObjectColumn (Columns::ColumnId_CastingObject)); mMagicEffects.addColumn (new EffectObjectColumn (Columns::ColumnId_HitObject)); mMagicEffects.addColumn (new EffectObjectColumn (Columns::ColumnId_AreaObject)); mMagicEffects.addColumn (new EffectObjectColumn (Columns::ColumnId_BoltObject)); mMagicEffects.addColumn (new EffectSoundColumn (Columns::ColumnId_CastingSound)); mMagicEffects.addColumn (new EffectSoundColumn (Columns::ColumnId_HitSound)); mMagicEffects.addColumn (new EffectSoundColumn (Columns::ColumnId_AreaSound)); mMagicEffects.addColumn (new EffectSoundColumn (Columns::ColumnId_BoltSound)); mMagicEffects.addColumn (new FlagColumn ( Columns::ColumnId_AllowSpellmaking, ESM::MagicEffect::AllowSpellmaking)); mMagicEffects.addColumn (new FlagColumn ( Columns::ColumnId_AllowEnchanting, ESM::MagicEffect::AllowEnchanting)); mMagicEffects.addColumn (new FlagColumn ( Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight)); mMagicEffects.addColumn (new DescriptionColumn); mPathgrids.addColumn (new StringIdColumn); mPathgrids.addColumn (new RecordStateColumn); mPathgrids.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Pathgrid)); // new object deleted in dtor of Collection mPathgrids.addColumn (new NestedParentColumn (Columns::ColumnId_PathgridPoints)); index = mPathgrids.getColumns()-1; // new object deleted in dtor of NestedCollection mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridPointListAdapter ())); // new objects deleted in dtor of NestableColumn // WARNING: The order of the columns below are assumed in PathgridPointListAdapter mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridIndex, ColumnBase::Display_Integer, ColumnBase::Flag_Dialogue, false)); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer)); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridPosY, ColumnBase::Display_Integer)); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridPosZ, ColumnBase::Display_Integer)); mPathgrids.addColumn (new NestedParentColumn (Columns::ColumnId_PathgridEdges)); index = mPathgrids.getColumns()-1; mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ())); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridEdgeIndex, ColumnBase::Display_Integer, ColumnBase::Flag_Dialogue, false)); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer)); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridEdge1, ColumnBase::Display_Integer)); mStartScripts.addColumn (new StringIdColumn); mStartScripts.addColumn (new RecordStateColumn); mStartScripts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_StartScript)); mRefs.addColumn (new StringIdColumn (true)); mRefs.addColumn (new RecordStateColumn); mRefs.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Reference)); mRefs.addColumn (new CellColumn (true)); mRefs.addColumn (new OriginalCellColumn); mRefs.addColumn (new IdColumn); mRefs.addColumn (new PosColumn (&CellRef::mPos, 0, false)); mRefs.addColumn (new PosColumn (&CellRef::mPos, 1, false)); mRefs.addColumn (new PosColumn (&CellRef::mPos, 2, false)); mRefs.addColumn (new RotColumn (&CellRef::mPos, 0, false)); mRefs.addColumn (new RotColumn (&CellRef::mPos, 1, false)); mRefs.addColumn (new RotColumn (&CellRef::mPos, 2, false)); mRefs.addColumn (new ScaleColumn); mRefs.addColumn (new OwnerColumn); mRefs.addColumn (new SoulColumn); mRefs.addColumn (new FactionColumn); mRefs.addColumn (new FactionIndexColumn); mRefs.addColumn (new ChargesColumn); mRefs.addColumn (new EnchantmentChargesColumn); mRefs.addColumn (new GoldValueColumn); mRefs.addColumn (new TeleportColumn); mRefs.addColumn (new TeleportCellColumn); mRefs.addColumn (new PosColumn (&CellRef::mDoorDest, 0, true)); mRefs.addColumn (new PosColumn (&CellRef::mDoorDest, 1, true)); mRefs.addColumn (new PosColumn (&CellRef::mDoorDest, 2, true)); mRefs.addColumn (new RotColumn (&CellRef::mDoorDest, 0, true)); mRefs.addColumn (new RotColumn (&CellRef::mDoorDest, 1, true)); mRefs.addColumn (new RotColumn (&CellRef::mDoorDest, 2, true)); mRefs.addColumn (new LockLevelColumn); mRefs.addColumn (new KeyColumn); mRefs.addColumn (new TrapColumn); mRefs.addColumn (new OwnerGlobalColumn); mRefs.addColumn (new RefNumColumn); mFilters.addColumn (new StringIdColumn); mFilters.addColumn (new RecordStateColumn); mFilters.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Filter)); mFilters.addColumn (new FilterColumn); mFilters.addColumn (new DescriptionColumn); mDebugProfiles.addColumn (new StringIdColumn); mDebugProfiles.addColumn (new RecordStateColumn); mDebugProfiles.addColumn (new FixedRecordTypeColumn (UniversalId::Type_DebugProfile)); mDebugProfiles.addColumn (new FlagColumn2 ( Columns::ColumnId_DefaultProfile, ESM::DebugProfile::Flag_Default)); mDebugProfiles.addColumn (new FlagColumn2 ( Columns::ColumnId_BypassNewGame, ESM::DebugProfile::Flag_BypassNewGame)); mDebugProfiles.addColumn (new FlagColumn2 ( Columns::ColumnId_GlobalProfile, ESM::DebugProfile::Flag_Global)); mDebugProfiles.addColumn (new DescriptionColumn); mDebugProfiles.addColumn (new ScriptColumn ( ScriptColumn::Type_Lines)); mMetaData.appendBlankRecord ("sys::meta"); mMetaData.addColumn (new StringIdColumn (true)); mMetaData.addColumn (new RecordStateColumn); mMetaData.addColumn (new FixedRecordTypeColumn (UniversalId::Type_MetaData)); mMetaData.addColumn (new FormatColumn); mMetaData.addColumn (new AuthorColumn); mMetaData.addColumn (new FileDescriptionColumn); addModel (new IdTable (&mGlobals), UniversalId::Type_Global); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst); addModel (new IdTable (&mSkills), UniversalId::Type_Skill); addModel (new IdTable (&mClasses), UniversalId::Type_Class); addModel (new IdTree (&mFactions, &mFactions), UniversalId::Type_Faction); addModel (new IdTree (&mRaces, &mRaces), UniversalId::Type_Race); addModel (new IdTable (&mSounds), UniversalId::Type_Sound); addModel (new IdTable (&mScripts), UniversalId::Type_Script); addModel (new IdTree (&mRegions, &mRegions), UniversalId::Type_Region); addModel (new IdTree (&mBirthsigns, &mBirthsigns), UniversalId::Type_Birthsign); addModel (new IdTree (&mSpells, &mSpells), UniversalId::Type_Spell); addModel (new IdTable (&mTopics), UniversalId::Type_Topic); addModel (new IdTable (&mJournals), UniversalId::Type_Journal); addModel (new IdTree (&mTopicInfos, &mTopicInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_TopicInfo); addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo); addModel (new IdTree (&mCells, &mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell); addModel (new IdTree (&mEnchantments, &mEnchantments), UniversalId::Type_Enchantment); addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview), UniversalId::Type_Referenceable); addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_Reference); addModel (new IdTable (&mFilters), UniversalId::Type_Filter); addModel (new IdTable (&mDebugProfiles), UniversalId::Type_DebugProfile); addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Meshes)), UniversalId::Type_Mesh); addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Icons)), UniversalId::Type_Icon); addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Musics)), UniversalId::Type_Music); addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_SoundsRes)), UniversalId::Type_SoundRes); addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Textures)), UniversalId::Type_Texture); addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)), UniversalId::Type_Video); addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData); mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files } CSMWorld::Data::~Data() { for (std::vector::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) delete *iter; delete mReader; } boost::shared_ptr CSMWorld::Data::getResourceSystem() { return mResourceSystem; } boost::shared_ptr CSMWorld::Data::getResourceSystem() const { return mResourceSystem; } const CSMWorld::IdCollection& CSMWorld::Data::getGlobals() const { return mGlobals; } CSMWorld::IdCollection& CSMWorld::Data::getGlobals() { return mGlobals; } const CSMWorld::IdCollection& CSMWorld::Data::getGmsts() const { return mGmsts; } CSMWorld::IdCollection& CSMWorld::Data::getGmsts() { return mGmsts; } const CSMWorld::IdCollection& CSMWorld::Data::getSkills() const { return mSkills; } CSMWorld::IdCollection& CSMWorld::Data::getSkills() { return mSkills; } const CSMWorld::IdCollection& CSMWorld::Data::getClasses() const { return mClasses; } CSMWorld::IdCollection& CSMWorld::Data::getClasses() { return mClasses; } const CSMWorld::IdCollection& CSMWorld::Data::getFactions() const { return mFactions; } CSMWorld::IdCollection& CSMWorld::Data::getFactions() { return mFactions; } const CSMWorld::IdCollection& CSMWorld::Data::getRaces() const { return mRaces; } CSMWorld::IdCollection& CSMWorld::Data::getRaces() { return mRaces; } const CSMWorld::IdCollection& CSMWorld::Data::getSounds() const { return mSounds; } CSMWorld::IdCollection& CSMWorld::Data::getSounds() { return mSounds; } const CSMWorld::IdCollection& CSMWorld::Data::getScripts() const { return mScripts; } CSMWorld::IdCollection& CSMWorld::Data::getScripts() { return mScripts; } const CSMWorld::IdCollection& CSMWorld::Data::getRegions() const { return mRegions; } CSMWorld::IdCollection& CSMWorld::Data::getRegions() { return mRegions; } const CSMWorld::IdCollection& CSMWorld::Data::getBirthsigns() const { return mBirthsigns; } CSMWorld::IdCollection& CSMWorld::Data::getBirthsigns() { return mBirthsigns; } const CSMWorld::IdCollection& CSMWorld::Data::getSpells() const { return mSpells; } CSMWorld::IdCollection& CSMWorld::Data::getSpells() { return mSpells; } const CSMWorld::IdCollection& CSMWorld::Data::getTopics() const { return mTopics; } CSMWorld::IdCollection& CSMWorld::Data::getTopics() { return mTopics; } const CSMWorld::IdCollection& CSMWorld::Data::getJournals() const { return mJournals; } CSMWorld::IdCollection& CSMWorld::Data::getJournals() { return mJournals; } const CSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos() const { return mTopicInfos; } CSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos() { return mTopicInfos; } const CSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos() const { return mJournalInfos; } CSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos() { return mJournalInfos; } const CSMWorld::IdCollection& CSMWorld::Data::getCells() const { return mCells; } CSMWorld::IdCollection& CSMWorld::Data::getCells() { return mCells; } const CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() const { return mReferenceables; } CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() { return mReferenceables; } const CSMWorld::RefCollection& CSMWorld::Data::getReferences() const { return mRefs; } CSMWorld::RefCollection& CSMWorld::Data::getReferences() { return mRefs; } const CSMWorld::IdCollection& CSMWorld::Data::getFilters() const { return mFilters; } CSMWorld::IdCollection& CSMWorld::Data::getFilters() { return mFilters; } const CSMWorld::IdCollection& CSMWorld::Data::getEnchantments() const { return mEnchantments; } CSMWorld::IdCollection& CSMWorld::Data::getEnchantments() { return mEnchantments; } const CSMWorld::IdCollection& CSMWorld::Data::getBodyParts() const { return mBodyParts; } CSMWorld::IdCollection& CSMWorld::Data::getBodyParts() { return mBodyParts; } const CSMWorld::IdCollection& CSMWorld::Data::getDebugProfiles() const { return mDebugProfiles; } CSMWorld::IdCollection& CSMWorld::Data::getDebugProfiles() { return mDebugProfiles; } const CSMWorld::IdCollection& CSMWorld::Data::getLand() const { return mLand; } CSMWorld::IdCollection& CSMWorld::Data::getLand() { return mLand; } const CSMWorld::IdCollection& CSMWorld::Data::getLandTextures() const { return mLandTextures; } CSMWorld::IdCollection& CSMWorld::Data::getLandTextures() { return mLandTextures; } const CSMWorld::IdCollection& CSMWorld::Data::getSoundGens() const { return mSoundGens; } CSMWorld::IdCollection& CSMWorld::Data::getSoundGens() { return mSoundGens; } const CSMWorld::IdCollection& CSMWorld::Data::getMagicEffects() const { return mMagicEffects; } CSMWorld::IdCollection& CSMWorld::Data::getMagicEffects() { return mMagicEffects; } const CSMWorld::SubCellCollection& CSMWorld::Data::getPathgrids() const { return mPathgrids; } CSMWorld::SubCellCollection& CSMWorld::Data::getPathgrids() { return mPathgrids; } const CSMWorld::IdCollection& CSMWorld::Data::getStartScripts() const { return mStartScripts; } CSMWorld::IdCollection& CSMWorld::Data::getStartScripts() { return mStartScripts; } const CSMWorld::Resources& CSMWorld::Data::getResources (const UniversalId& id) const { return mResourcesManager.get (id.getType()); } const CSMWorld::MetaData& CSMWorld::Data::getMetaData() const { return mMetaData.getRecord (0).get(); } void CSMWorld::Data::setMetaData (const MetaData& metaData) { Record record (RecordBase::State_ModifiedOnly, 0, &metaData); mMetaData.setRecord (0, record); } QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id) { std::map::iterator iter = mModelIndex.find (id.getType()); if (iter==mModelIndex.end()) { // try creating missing (secondary) tables on the fly // // Note: We create these tables here so we don't have to deal with them during load/initial // construction of the ESX data where no update signals are available. if (id.getType()==UniversalId::Type_RegionMap) { RegionMap *table = 0; addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, false); return table; } throw std::logic_error ("No table model available for " + id.toString()); } return iter->second; } void CSMWorld::Data::merge() { mGlobals.merge(); } int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project) { // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading boost::shared_ptr ptr(mReader); mReaders.push_back(ptr); mReader = 0; mDialogue = 0; mReader = new ESM::ESMReader; mReader->setEncoder (&mEncoder); mReader->setIndex(mReaderIndex++); mReader->open (path.string()); mBase = base; mProject = project; if (!mProject && !mBase) { MetaData metaData; metaData.mId = "sys::meta"; metaData.load (*mReader); mMetaData.setRecord (0, Record (RecordBase::State_ModifiedOnly, 0, &metaData)); } return mReader->getRecordCount(); } bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) { if (!mReader) throw std::logic_error ("can't continue loading, because no load has been started"); if (!mReader->hasMoreRecs()) { if (mBase) { // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading. // We don't store non-base reader, because everything going into modified will be // fully loaded during the initial loading process. boost::shared_ptr ptr(mReader); mReaders.push_back(ptr); } else delete mReader; mReader = 0; mDialogue = 0; return true; } ESM::NAME n = mReader->getRecName(); mReader->getRecHeader(); bool unhandledRecord = false; switch (n.val) { case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break; case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break; case ESM::REC_SKIL: mSkills.load (*mReader, mBase); break; case ESM::REC_CLAS: mClasses.load (*mReader, mBase); break; case ESM::REC_FACT: mFactions.load (*mReader, mBase); break; case ESM::REC_RACE: mRaces.load (*mReader, mBase); break; case ESM::REC_SOUN: mSounds.load (*mReader, mBase); break; case ESM::REC_SCPT: mScripts.load (*mReader, mBase); break; case ESM::REC_REGN: mRegions.load (*mReader, mBase); break; case ESM::REC_BSGN: mBirthsigns.load (*mReader, mBase); break; case ESM::REC_SPEL: mSpells.load (*mReader, mBase); break; case ESM::REC_ENCH: mEnchantments.load (*mReader, mBase); break; case ESM::REC_BODY: mBodyParts.load (*mReader, mBase); break; case ESM::REC_SNDG: mSoundGens.load (*mReader, mBase); break; case ESM::REC_MGEF: mMagicEffects.load (*mReader, mBase); break; case ESM::REC_PGRD: mPathgrids.load (*mReader, mBase); break; case ESM::REC_SSCR: mStartScripts.load (*mReader, mBase); break; case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break; case ESM::REC_LAND: { int index = mLand.load(*mReader, mBase); // Load all land data for now. A future optimisation may only load non-base data // if a suitable mechanism for avoiding race conditions can be established. if (index!=-1/* && !mBase*/) mLand.getRecord (index).get().loadData ( ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM); break; } case ESM::REC_CELL: { int index = mCells.load (*mReader, mBase); if (index < 0 || index >= mCells.getSize()) { // log an error and continue loading the refs to the last loaded cell CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_None); messages.add (id, "Logic error: cell index out of bounds", "", CSMDoc::Message::Severity_Error); index = mCells.getSize()-1; } std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (index)); mRefs.load (*mReader, index, mBase, mRefLoadCache[cellId], messages); break; } case ESM::REC_ACTI: mReferenceables.load (*mReader, mBase, UniversalId::Type_Activator); break; case ESM::REC_ALCH: mReferenceables.load (*mReader, mBase, UniversalId::Type_Potion); break; case ESM::REC_APPA: mReferenceables.load (*mReader, mBase, UniversalId::Type_Apparatus); break; case ESM::REC_ARMO: mReferenceables.load (*mReader, mBase, UniversalId::Type_Armor); break; case ESM::REC_BOOK: mReferenceables.load (*mReader, mBase, UniversalId::Type_Book); break; case ESM::REC_CLOT: mReferenceables.load (*mReader, mBase, UniversalId::Type_Clothing); break; case ESM::REC_CONT: mReferenceables.load (*mReader, mBase, UniversalId::Type_Container); break; case ESM::REC_CREA: mReferenceables.load (*mReader, mBase, UniversalId::Type_Creature); break; case ESM::REC_DOOR: mReferenceables.load (*mReader, mBase, UniversalId::Type_Door); break; case ESM::REC_INGR: mReferenceables.load (*mReader, mBase, UniversalId::Type_Ingredient); break; case ESM::REC_LEVC: mReferenceables.load (*mReader, mBase, UniversalId::Type_CreatureLevelledList); break; case ESM::REC_LEVI: mReferenceables.load (*mReader, mBase, UniversalId::Type_ItemLevelledList); break; case ESM::REC_LIGH: mReferenceables.load (*mReader, mBase, UniversalId::Type_Light); break; case ESM::REC_LOCK: mReferenceables.load (*mReader, mBase, UniversalId::Type_Lockpick); break; case ESM::REC_MISC: mReferenceables.load (*mReader, mBase, UniversalId::Type_Miscellaneous); break; case ESM::REC_NPC_: mReferenceables.load (*mReader, mBase, UniversalId::Type_Npc); break; case ESM::REC_PROB: mReferenceables.load (*mReader, mBase, UniversalId::Type_Probe); break; case ESM::REC_REPA: mReferenceables.load (*mReader, mBase, UniversalId::Type_Repair); break; case ESM::REC_STAT: mReferenceables.load (*mReader, mBase, UniversalId::Type_Static); break; case ESM::REC_WEAP: mReferenceables.load (*mReader, mBase, UniversalId::Type_Weapon); break; case ESM::REC_DIAL: { ESM::Dialogue record; bool isDeleted = false; record.load (*mReader, isDeleted); if (isDeleted) { // record vector can be shuffled around which would make pointer to record invalid mDialogue = 0; if (mJournals.tryDelete (record.mId)) { mJournalInfos.removeDialogueInfos(record.mId); } else if (mTopics.tryDelete (record.mId)) { mTopicInfos.removeDialogueInfos(record.mId); } else { messages.add (UniversalId::Type_None, "Trying to delete dialogue record " + record.mId + " which does not exist", "", CSMDoc::Message::Severity_Warning); } } else { if (record.mType == ESM::Dialogue::Journal) { mJournals.load (record, mBase); mDialogue = &mJournals.getRecord (record.mId).get(); } else { mTopics.load (record, mBase); mDialogue = &mTopics.getRecord (record.mId).get(); } } break; } case ESM::REC_INFO: { if (!mDialogue) { messages.add (UniversalId::Type_None, "Found info record not following a dialogue record", "", CSMDoc::Message::Severity_Error); mReader->skipRecord(); break; } if (mDialogue->mType==ESM::Dialogue::Journal) mJournalInfos.load (*mReader, mBase, *mDialogue); else mTopicInfos.load (*mReader, mBase, *mDialogue); break; } case ESM::REC_FILT: if (!mProject) { unhandledRecord = true; break; } mFilters.load (*mReader, mBase); break; case ESM::REC_DBGP: if (!mProject) { unhandledRecord = true; break; } mDebugProfiles.load (*mReader, mBase); break; default: unhandledRecord = true; } if (unhandledRecord) { messages.add (UniversalId::Type_None, "Unsupported record type: " + n.toString(), "", CSMDoc::Message::Severity_Error); mReader->skipRecord(); } return false; } bool CSMWorld::Data::hasId (const std::string& id) const { return getGlobals().searchId (id)!=-1 || getGmsts().searchId (id)!=-1 || getSkills().searchId (id)!=-1 || getClasses().searchId (id)!=-1 || getFactions().searchId (id)!=-1 || getRaces().searchId (id)!=-1 || getSounds().searchId (id)!=-1 || getScripts().searchId (id)!=-1 || getRegions().searchId (id)!=-1 || getBirthsigns().searchId (id)!=-1 || getSpells().searchId (id)!=-1 || getTopics().searchId (id)!=-1 || getJournals().searchId (id)!=-1 || getCells().searchId (id)!=-1 || getEnchantments().searchId (id)!=-1 || getBodyParts().searchId (id)!=-1 || getSoundGens().searchId (id)!=-1 || getMagicEffects().searchId (id)!=-1 || getReferenceables().searchId (id)!=-1; } int CSMWorld::Data::count (RecordBase::State state) const { return count (state, mGlobals) + count (state, mGmsts) + count (state, mSkills) + count (state, mClasses) + count (state, mFactions) + count (state, mRaces) + count (state, mSounds) + count (state, mScripts) + count (state, mRegions) + count (state, mBirthsigns) + count (state, mSpells) + count (state, mCells) + count (state, mEnchantments) + count (state, mBodyParts) + count (state, mLand) + count (state, mLandTextures) + count (state, mSoundGens) + count (state, mMagicEffects) + count (state, mReferenceables) + count (state, mPathgrids); } std::vector CSMWorld::Data::getIds (bool listDeleted) const { std::vector ids; appendIds (ids, mGlobals, listDeleted); appendIds (ids, mGmsts, listDeleted); appendIds (ids, mClasses, listDeleted); appendIds (ids, mFactions, listDeleted); appendIds (ids, mRaces, listDeleted); appendIds (ids, mSounds, listDeleted); appendIds (ids, mScripts, listDeleted); appendIds (ids, mRegions, listDeleted); appendIds (ids, mBirthsigns, listDeleted); appendIds (ids, mSpells, listDeleted); appendIds (ids, mTopics, listDeleted); appendIds (ids, mJournals, listDeleted); appendIds (ids, mCells, listDeleted); appendIds (ids, mEnchantments, listDeleted); appendIds (ids, mBodyParts, listDeleted); appendIds (ids, mSoundGens, listDeleted); appendIds (ids, mMagicEffects, listDeleted); appendIds (ids, mReferenceables, listDeleted); std::sort (ids.begin(), ids.end()); return ids; } void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (topLeft.column()<=0) emit idListChanged(); } void CSMWorld::Data::rowsChanged (const QModelIndex& parent, int start, int end) { emit idListChanged(); } const VFS::Manager* CSMWorld::Data::getVFS() const { return mResourcesManager.getVFS(); } openmw-openmw-0.38.0/apps/opencs/model/world/data.hpp000066400000000000000000000232271264522266000225300ustar00rootroot00000000000000#ifndef CSM_WOLRD_DATA_H #define CSM_WOLRD_DATA_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../doc/stage.hpp" #include "idcollection.hpp" #include "nestedidcollection.hpp" #include "universalid.hpp" #include "cell.hpp" #include "land.hpp" #include "landtexture.hpp" #include "refidcollection.hpp" #include "refcollection.hpp" #include "infocollection.hpp" #include "nestedinfocollection.hpp" #include "pathgrid.hpp" #include "metadata.hpp" #ifndef Q_MOC_RUN #include "subcellcollection.hpp" #endif class QAbstractItemModel; namespace VFS { class Manager; } namespace ESM { class ESMReader; struct Dialogue; } namespace CSMWorld { class ResourcesManager; class Resources; class Data : public QObject { Q_OBJECT ToUTF8::Utf8Encoder mEncoder; IdCollection mGlobals; IdCollection mGmsts; IdCollection mSkills; IdCollection mClasses; NestedIdCollection mFactions; NestedIdCollection mRaces; IdCollection mSounds; IdCollection mScripts; NestedIdCollection mRegions; NestedIdCollection mBirthsigns; NestedIdCollection mSpells; IdCollection mTopics; IdCollection mJournals; NestedIdCollection mEnchantments; IdCollection mBodyParts; IdCollection mMagicEffects; SubCellCollection mPathgrids; IdCollection mDebugProfiles; IdCollection mSoundGens; IdCollection mStartScripts; NestedInfoCollection mTopicInfos; InfoCollection mJournalInfos; NestedIdCollection mCells; IdCollection mLandTextures; IdCollection mLand; RefIdCollection mReferenceables; RefCollection mRefs; IdCollection mFilters; Collection mMetaData; const ResourcesManager& mResourcesManager; std::vector mModels; std::map mModelIndex; ESM::ESMReader *mReader; const ESM::Dialogue *mDialogue; // last loaded dialogue bool mBase; bool mProject; std::map > mRefLoadCache; int mReaderIndex; boost::shared_ptr mResourceSystem; std::vector > mReaders; // not implemented Data (const Data&); Data& operator= (const Data&); void addModel (QAbstractItemModel *model, UniversalId::Type type, bool update = true); static void appendIds (std::vector& ids, const CollectionBase& collection, bool listDeleted); ///< Append all IDs from collection to \a ids. static int count (RecordBase::State state, const CollectionBase& collection); public: Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager); virtual ~Data(); const VFS::Manager* getVFS() const; boost::shared_ptr getResourceSystem(); boost::shared_ptr getResourceSystem() const; const IdCollection& getGlobals() const; IdCollection& getGlobals(); const IdCollection& getGmsts() const; IdCollection& getGmsts(); const IdCollection& getSkills() const; IdCollection& getSkills(); const IdCollection& getClasses() const; IdCollection& getClasses(); const IdCollection& getFactions() const; IdCollection& getFactions(); const IdCollection& getRaces() const; IdCollection& getRaces(); const IdCollection& getSounds() const; IdCollection& getSounds(); const IdCollection& getScripts() const; IdCollection& getScripts(); const IdCollection& getRegions() const; IdCollection& getRegions(); const IdCollection& getBirthsigns() const; IdCollection& getBirthsigns(); const IdCollection& getSpells() const; IdCollection& getSpells(); const IdCollection& getTopics() const; IdCollection& getTopics(); const IdCollection& getJournals() const; IdCollection& getJournals(); const InfoCollection& getTopicInfos() const; InfoCollection& getTopicInfos(); const InfoCollection& getJournalInfos() const; InfoCollection& getJournalInfos(); const IdCollection& getCells() const; IdCollection& getCells(); const RefIdCollection& getReferenceables() const; RefIdCollection& getReferenceables(); const RefCollection& getReferences() const; RefCollection& getReferences(); const IdCollection& getFilters() const; IdCollection& getFilters(); const IdCollection& getEnchantments() const; IdCollection& getEnchantments(); const IdCollection& getBodyParts() const; IdCollection& getBodyParts(); const IdCollection& getDebugProfiles() const; IdCollection& getDebugProfiles(); const IdCollection& getLand() const; IdCollection& getLand(); const IdCollection& getLandTextures() const; IdCollection& getLandTextures(); const IdCollection& getSoundGens() const; IdCollection& getSoundGens(); const IdCollection& getMagicEffects() const; IdCollection& getMagicEffects(); const SubCellCollection& getPathgrids() const; SubCellCollection& getPathgrids(); const IdCollection& getStartScripts() const; IdCollection& getStartScripts(); /// Throws an exception, if \a id does not match a resources list. const Resources& getResources (const UniversalId& id) const; const MetaData& getMetaData() const; void setMetaData (const MetaData& metaData); QAbstractItemModel *getTableModel (const UniversalId& id); ///< If no table model is available for \a id, an exception is thrown. /// /// \note The returned table may either be the model for the ID itself or the model that /// contains the record specified by the ID. void merge(); ///< Merge modified into base. int startLoading (const boost::filesystem::path& path, bool base, bool project); ///< Begin merging content of a file into base or modified. /// /// \param project load project file instead of content file /// ///< \return estimated number of records bool continueLoading (CSMDoc::Messages& messages); ///< \return Finished? bool hasId (const std::string& id) const; std::vector getIds (bool listDeleted = true) const; ///< Return a sorted collection of all IDs that are not internal to the editor. /// /// \param listDeleted include deleted record in the list int count (RecordBase::State state) const; ///< Return number of top-level records with the given \a state. signals: void idListChanged(); private slots: void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void rowsChanged (const QModelIndex& parent, int start, int end); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/idcollection.hpp000066400000000000000000000101721264522266000242620ustar00rootroot00000000000000#ifndef CSM_WOLRD_IDCOLLECTION_H #define CSM_WOLRD_IDCOLLECTION_H #include #include "collection.hpp" namespace CSMWorld { /// \brief Single type collection of top level records template > class IdCollection : public Collection { virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted); public: /// \return Index of loaded record (-1 if no record was loaded) int load (ESM::ESMReader& reader, bool base); /// \param index Index at which the record can be found. /// Special values: -2 index unknown, -1 record does not exist yet and therefore /// does not have an index /// /// \return index int load (const ESXRecordT& record, bool base, int index = -2); bool tryDelete (const std::string& id); ///< Try deleting \a id. If the id does not exist or can't be deleted the call is ignored. /// /// \return Has the ID been deleted? }; template void IdCollection::loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted) { record.load (reader, isDeleted); } template int IdCollection::load (ESM::ESMReader& reader, bool base) { ESXRecordT record; bool isDeleted = false; loadRecord (record, reader, isDeleted); std::string id = IdAccessorT().getId (record); int index = this->searchId (id); if (isDeleted) { if (index==-1) { // deleting a record that does not exist // ignore it for now /// \todo report the problem to the user return -1; } if (base) { this->removeRows (index, 1); return -1; } Record baseRecord = this->getRecord (index); baseRecord.mState = RecordBase::State_Deleted; this->setRecord (index, baseRecord); return index; } return load (record, base, index); } template int IdCollection::load (const ESXRecordT& record, bool base, int index) { if (index==-2) index = this->searchId (IdAccessorT().getId (record)); if (index==-1) { // new record Record record2; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = record; index = this->getSize(); this->appendRecord (record2); } else { // old record Record record2 = Collection::getRecord (index); if (base) record2.mBase = record; else record2.setModified (record); this->setRecord (index, record2); } return index; } template bool IdCollection::tryDelete (const std::string& id) { int index = this->searchId (id); if (index==-1) return false; Record record = Collection::getRecord (index); if (record.isDeleted()) return false; if (record.mState==RecordBase::State_ModifiedOnly) { Collection::removeRows (index, 1); } else { record.mState = RecordBase::State_Deleted; this->setRecord (index, record); } return true; } } #endif openmw-openmw-0.38.0/apps/opencs/model/world/idcompletionmanager.cpp000066400000000000000000000127201264522266000256270ustar00rootroot00000000000000#include "idcompletionmanager.hpp" #include #include #include "../../view/widget/completerpopup.hpp" #include "data.hpp" #include "idtablebase.hpp" namespace { std::map generateModelTypes() { std::map types; types[CSMWorld::ColumnBase::Display_BodyPart ] = CSMWorld::UniversalId::Type_BodyPart; types[CSMWorld::ColumnBase::Display_Cell ] = CSMWorld::UniversalId::Type_Cell; types[CSMWorld::ColumnBase::Display_Class ] = CSMWorld::UniversalId::Type_Class; types[CSMWorld::ColumnBase::Display_CreatureLevelledList] = CSMWorld::UniversalId::Type_Referenceable; types[CSMWorld::ColumnBase::Display_Creature ] = CSMWorld::UniversalId::Type_Referenceable; types[CSMWorld::ColumnBase::Display_Enchantment ] = CSMWorld::UniversalId::Type_Enchantment; types[CSMWorld::ColumnBase::Display_Faction ] = CSMWorld::UniversalId::Type_Faction; types[CSMWorld::ColumnBase::Display_GlobalVariable ] = CSMWorld::UniversalId::Type_Global; types[CSMWorld::ColumnBase::Display_Icon ] = CSMWorld::UniversalId::Type_Icon; types[CSMWorld::ColumnBase::Display_Journal ] = CSMWorld::UniversalId::Type_Journal; types[CSMWorld::ColumnBase::Display_Mesh ] = CSMWorld::UniversalId::Type_Mesh; types[CSMWorld::ColumnBase::Display_Miscellaneous ] = CSMWorld::UniversalId::Type_Referenceable; types[CSMWorld::ColumnBase::Display_Npc ] = CSMWorld::UniversalId::Type_Referenceable; types[CSMWorld::ColumnBase::Display_Race ] = CSMWorld::UniversalId::Type_Race; types[CSMWorld::ColumnBase::Display_Region ] = CSMWorld::UniversalId::Type_Region; types[CSMWorld::ColumnBase::Display_Referenceable ] = CSMWorld::UniversalId::Type_Referenceable; types[CSMWorld::ColumnBase::Display_Script ] = CSMWorld::UniversalId::Type_Script; types[CSMWorld::ColumnBase::Display_Skill ] = CSMWorld::UniversalId::Type_Skill; types[CSMWorld::ColumnBase::Display_Sound ] = CSMWorld::UniversalId::Type_Sound; types[CSMWorld::ColumnBase::Display_SoundRes ] = CSMWorld::UniversalId::Type_SoundRes; types[CSMWorld::ColumnBase::Display_Spell ] = CSMWorld::UniversalId::Type_Spell; types[CSMWorld::ColumnBase::Display_Static ] = CSMWorld::UniversalId::Type_Referenceable; types[CSMWorld::ColumnBase::Display_Texture ] = CSMWorld::UniversalId::Type_Texture; types[CSMWorld::ColumnBase::Display_Topic ] = CSMWorld::UniversalId::Type_Topic; types[CSMWorld::ColumnBase::Display_Weapon ] = CSMWorld::UniversalId::Type_Referenceable; return types; } typedef std::map::const_iterator ModelTypeConstIterator; } const std::map CSMWorld::IdCompletionManager::sCompleterModelTypes = generateModelTypes(); std::vector CSMWorld::IdCompletionManager::getDisplayTypes() { std::vector types; ModelTypeConstIterator current = sCompleterModelTypes.begin(); ModelTypeConstIterator end = sCompleterModelTypes.end(); for (; current != end; ++current) { types.push_back(current->first); } return types; } CSMWorld::IdCompletionManager::IdCompletionManager(CSMWorld::Data &data) { generateCompleters(data); } bool CSMWorld::IdCompletionManager::hasCompleterFor(CSMWorld::ColumnBase::Display display) const { return mCompleters.find(display) != mCompleters.end(); } boost::shared_ptr CSMWorld::IdCompletionManager::getCompleter(CSMWorld::ColumnBase::Display display) { if (!hasCompleterFor(display)) { throw std::logic_error("This column doesn't have an ID completer"); } return mCompleters[display]; } void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data) { ModelTypeConstIterator current = sCompleterModelTypes.begin(); ModelTypeConstIterator end = sCompleterModelTypes.end(); for (; current != end; ++current) { QAbstractItemModel *model = data.getTableModel(current->second); CSMWorld::IdTableBase *table = dynamic_cast(model); if (table != NULL) { int idColumn = table->searchColumnIndex(CSMWorld::Columns::ColumnId_Id); if (idColumn != -1) { boost::shared_ptr completer = boost::make_shared(table); completer->setCompletionColumn(idColumn); // The completion role must be Qt::DisplayRole to get the ID values from the model completer->setCompletionRole(Qt::DisplayRole); completer->setCaseSensitivity(Qt::CaseInsensitive); QAbstractItemView *popup = new CSVWidget::CompleterPopup(); completer->setPopup(popup); // The completer takes ownership of the popup completer->setMaxVisibleItems(10); mCompleters[current->first] = completer; } } } } openmw-openmw-0.38.0/apps/opencs/model/world/idcompletionmanager.hpp000066400000000000000000000020501264522266000256270ustar00rootroot00000000000000#ifndef CSM_WORLD_IDCOMPLETIONMANAGER_HPP #define CSM_WORLD_IDCOMPLETIONMANAGER_HPP #include #include #include #include "columnbase.hpp" #include "universalid.hpp" class QCompleter; namespace CSMWorld { class Data; /// \brief Creates and stores all ID completers class IdCompletionManager { static const std::map sCompleterModelTypes; std::map > mCompleters; // Don't allow copying IdCompletionManager(const IdCompletionManager &); IdCompletionManager &operator = (const IdCompletionManager &); void generateCompleters(Data &data); public: static std::vector getDisplayTypes(); IdCompletionManager(Data &data); bool hasCompleterFor(ColumnBase::Display display) const; boost::shared_ptr getCompleter(ColumnBase::Display display); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/idtable.cpp000066400000000000000000000162151264522266000232150ustar00rootroot00000000000000#include "idtable.hpp" #include #include "collectionbase.hpp" #include "columnbase.hpp" CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features) : IdTableBase (features), mIdCollection (idCollection) {} CSMWorld::IdTable::~IdTable() {} int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const { if (parent.isValid()) return 0; return mIdCollection->getSize(); } int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const { if (parent.isValid()) return 0; return mIdCollection->getColumns(); } QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const { if (index.row() < 0 || index.column() < 0) return QVariant(); if (role==ColumnBase::Role_Display) return QVariant(mIdCollection->getColumn(index.column()).mDisplayType); if (role==ColumnBase::Role_ColumnId) return QVariant (getColumnId (index.column())); if ((role!=Qt::DisplayRole && role!=Qt::EditRole)) return QVariant(); if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable()) return QVariant(); return mIdCollection->getData (index.row(), index.column()); } QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation, int role) const { if (orientation==Qt::Vertical) return QVariant(); if (orientation != Qt::Horizontal) throw std::logic_error("Unknown header orientation specified"); if (role==Qt::DisplayRole) return tr (mIdCollection->getColumn (section).getTitle().c_str()); if (role==ColumnBase::Role_Flags) return mIdCollection->getColumn (section).mFlags; if (role==ColumnBase::Role_Display) return mIdCollection->getColumn (section).mDisplayType; if (role==ColumnBase::Role_ColumnId) return getColumnId (section); return QVariant(); } bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value, int role) { if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole) { mIdCollection->setData (index.row(), index.column(), value); emit dataChanged(index, index); // Modifying a value can also change the Modified status of a record. int stateColumn = searchColumnIndex(Columns::ColumnId_Modification); if (stateColumn != -1) { QModelIndex stateIndex = this->index(index.row(), stateColumn); emit dataChanged(stateIndex, stateIndex); } return true; } return false; } Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const { if (!index.isValid()) return 0; Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (mIdCollection->getColumn (index.column()).isUserEditable()) flags |= Qt::ItemIsEditable; return flags; } bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& parent) { if (parent.isValid()) return false; beginRemoveRows (parent, row, row+count-1); mIdCollection->removeRows (row, count); endRemoveRows(); return true; } QModelIndex CSMWorld::IdTable::index (int row, int column, const QModelIndex& parent) const { if (parent.isValid()) return QModelIndex(); if (row<0 || row>=mIdCollection->getSize()) return QModelIndex(); if (column<0 || column>=mIdCollection->getColumns()) return QModelIndex(); return createIndex (row, column); } QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const { return QModelIndex(); } void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type) { int index = mIdCollection->getAppendIndex (id, type); beginInsertRows (QModelIndex(), index, index); mIdCollection->appendBlankRecord (id, type); endInsertRows(); } void CSMWorld::IdTable::cloneRecord(const std::string& origin, const std::string& destination, CSMWorld::UniversalId::Type type) { int index = mIdCollection->getAppendIndex (destination); beginInsertRows (QModelIndex(), index, index); mIdCollection->cloneRecord(origin, destination, type); endInsertRows(); } ///This method can return only indexes to the top level table cells QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const { return index(mIdCollection->getIndex (id), column); } void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record, CSMWorld::UniversalId::Type type) { int index = mIdCollection->searchId (id); if (index==-1) { int index = mIdCollection->getAppendIndex (id, type); beginInsertRows (QModelIndex(), index, index); mIdCollection->appendRecord (record, type); endInsertRows(); } else { mIdCollection->replace (index, record); emit dataChanged (CSMWorld::IdTable::index (index, 0), CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1)); } } const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const { return mIdCollection->getRecord (id); } int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const { return mIdCollection->searchColumnIndex (id); } int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const { return mIdCollection->findColumnIndex (id); } void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector& newOrder) { if (!newOrder.empty()) if (mIdCollection->reorderRows (baseIndex, newOrder)) emit dataChanged (index (baseIndex, 0), index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1)); } std::pair CSMWorld::IdTable::view (int row) const { std::string id; std::string hint; if (getFeatures() & Feature_ViewCell) { int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell); int idColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); if (cellColumn!=-1 && idColumn!=-1) { id = mIdCollection->getData (row, cellColumn).toString().toUtf8().constData(); hint = "r:" + std::string (mIdCollection->getData (row, idColumn).toString().toUtf8().constData()); } } else if (getFeatures() & Feature_ViewId) { int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); if (column!=-1) { id = mIdCollection->getData (row, column).toString().toUtf8().constData(); hint = "c:" + id; } } if (id.empty()) return std::make_pair (UniversalId::Type_None, ""); if (id[0]=='#') id = "sys::default"; return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint); } ///For top level data/columns bool CSMWorld::IdTable::isDeleted (const std::string& id) const { return getRecord (id).isDeleted(); } int CSMWorld::IdTable::getColumnId(int column) const { return mIdCollection->getColumn(column).getId(); } CSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const { return mIdCollection; } openmw-openmw-0.38.0/apps/opencs/model/world/idtable.hpp000066400000000000000000000066431264522266000232260ustar00rootroot00000000000000#ifndef CSM_WOLRD_IDTABLE_H #define CSM_WOLRD_IDTABLE_H #include #include "idtablebase.hpp" #include "universalid.hpp" #include "columns.hpp" namespace CSMWorld { class CollectionBase; struct RecordBase; class IdTable : public IdTableBase { Q_OBJECT private: CollectionBase *mIdCollection; // not implemented IdTable (const IdTable&); IdTable& operator= (const IdTable&); public: IdTable (CollectionBase *idCollection, unsigned int features = 0); ///< The ownership of \a idCollection is not transferred. virtual ~IdTable(); virtual int rowCount (const QModelIndex & parent = QModelIndex()) const; virtual int columnCount (const QModelIndex & parent = QModelIndex()) const; virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); virtual Qt::ItemFlags flags (const QModelIndex & index) const; virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const; virtual QModelIndex parent (const QModelIndex& index) const; void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types void cloneRecord(const std::string& origin, const std::string& destination, UniversalId::Type type = UniversalId::Type_None); virtual QModelIndex getModelIndex (const std::string& id, int column) const; void setRecord (const std::string& id, const RecordBase& record, UniversalId::Type type = UniversalId::Type_None); ///< Add record or overwrite existing recrod. const RecordBase& getRecord (const std::string& id) const; virtual int searchColumnIndex (Columns::ColumnId id) const; ///< Return index of column with the given \a id. If no such column exists, -1 is returned. virtual int findColumnIndex (Columns::ColumnId id) const; ///< Return index of column with the given \a id. If no such column exists, an exception is /// thrown. void reorderRows (int baseIndex, const std::vector& newOrder); ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). virtual std::pair view (int row) const; ///< Return the UniversalId and the hint for viewing \a row. If viewing is not /// supported by this table, return (UniversalId::Type_None, ""). /// Is \a id flagged as deleted? virtual bool isDeleted (const std::string& id) const; virtual int getColumnId(int column) const; protected: virtual CollectionBase *idCollection() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/idtablebase.cpp000066400000000000000000000003041264522266000240400ustar00rootroot00000000000000#include "idtablebase.hpp" CSMWorld::IdTableBase::IdTableBase (unsigned int features) : mFeatures (features) {} unsigned int CSMWorld::IdTableBase::getFeatures() const { return mFeatures; } openmw-openmw-0.38.0/apps/opencs/model/world/idtablebase.hpp000066400000000000000000000040721264522266000240530ustar00rootroot00000000000000#ifndef CSM_WOLRD_IDTABLEBASE_H #define CSM_WOLRD_IDTABLEBASE_H #include #include "columns.hpp" namespace CSMWorld { class UniversalId; class IdTableBase : public QAbstractItemModel { Q_OBJECT public: enum Features { Feature_ReorderWithinTopic = 1, /// Use ID column to generate view request (ID is transformed into /// worldspace and original ID is passed as hint with c: prefix). Feature_ViewId = 2, /// Use cell column to generate view request (cell ID is transformed /// into worldspace and record ID is passed as hint with r: prefix). Feature_ViewCell = 4, Feature_View = Feature_ViewId | Feature_ViewCell, Feature_Preview = 8, /// Table can not be modified through ordinary means. Feature_Constant = 16 }; private: unsigned int mFeatures; public: IdTableBase (unsigned int features); virtual QModelIndex getModelIndex (const std::string& id, int column) const = 0; /// Return index of column with the given \a id. If no such column exists, -1 is /// returned. virtual int searchColumnIndex (Columns::ColumnId id) const = 0; /// Return index of column with the given \a id. If no such column exists, an /// exception is thrown. virtual int findColumnIndex (Columns::ColumnId id) const = 0; /// Return the UniversalId and the hint for viewing \a row. If viewing is not /// supported by this table, return (UniversalId::Type_None, ""). virtual std::pair view (int row) const = 0; /// Is \a id flagged as deleted? virtual bool isDeleted (const std::string& id) const = 0; virtual int getColumnId (int column) const = 0; unsigned int getFeatures() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/idtableproxymodel.cpp000066400000000000000000000106641264522266000253420ustar00rootroot00000000000000#include "idtableproxymodel.hpp" #include #include "idtablebase.hpp" namespace { std::string getEnumValue(const std::vector &values, int index) { if (index < 0 || index >= static_cast(values.size())) { return ""; } return values[index]; } } void CSMWorld::IdTableProxyModel::updateColumnMap() { Q_ASSERT(mSourceModel != NULL); mColumnMap.clear(); if (mFilter) { std::vector columns = mFilter->getReferencedColumns(); for (std::vector::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter) mColumnMap.insert (std::make_pair (*iter, mSourceModel->searchColumnIndex (static_cast (*iter)))); } } bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const { Q_ASSERT(mSourceModel != NULL); // It is not possible to use filterAcceptsColumn() and check for // sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags) // because the sourceColumn parameter excludes the hidden columns, i.e. wrong columns can // be rejected. Workaround by disallowing tree branches (nested columns), which are not meant // to be visible, from the filter. if (sourceParent.isValid()) return false; if (!mFilter) return true; return mFilter->test (*mSourceModel, sourceRow, mColumnMap); } CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) : QSortFilterProxyModel (parent), mSourceModel(NULL) { setSortCaseSensitivity (Qt::CaseInsensitive); } QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { Q_ASSERT(mSourceModel != NULL); return mapFromSource(mSourceModel->getModelIndex (id, column)); } void CSMWorld::IdTableProxyModel::setSourceModel(QAbstractItemModel *model) { QSortFilterProxyModel::setSourceModel(model); mSourceModel = dynamic_cast(sourceModel()); connect(mSourceModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); connect(mSourceModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); connect(mSourceModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex &))); } void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr& filter) { beginResetModel(); mFilter = filter; updateColumnMap(); endResetModel(); } bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { Columns::ColumnId id = static_cast(left.data(ColumnBase::Role_ColumnId).toInt()); EnumColumnCache::const_iterator valuesIt = mEnumColumnCache.find(id); if (valuesIt == mEnumColumnCache.end()) { if (Columns::hasEnums(id)) { valuesIt = mEnumColumnCache.insert(std::make_pair(id, Columns::getEnums(id))).first; } } if (valuesIt != mEnumColumnCache.end()) { std::string first = getEnumValue(valuesIt->second, left.data().toInt()); std::string second = getEnumValue(valuesIt->second, right.data().toInt()); return first < second; } return QSortFilterProxyModel::lessThan(left, right); } QString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const { Q_ASSERT(mSourceModel != NULL); int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id); return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString(); } void CSMWorld::IdTableProxyModel::refreshFilter() { updateColumnMap(); invalidateFilter(); } void CSMWorld::IdTableProxyModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int end) { refreshFilter(); if (!parent.isValid()) { emit rowAdded(getRecordId(end).toUtf8().constData()); } } void CSMWorld::IdTableProxyModel::sourceRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/) { refreshFilter(); } void CSMWorld::IdTableProxyModel::sourceDataChanged(const QModelIndex &/*topLeft*/, const QModelIndex &/*bottomRight*/) { refreshFilter(); } openmw-openmw-0.38.0/apps/opencs/model/world/idtableproxymodel.hpp000066400000000000000000000036061264522266000253450ustar00rootroot00000000000000#ifndef CSM_WOLRD_IDTABLEPROXYMODEL_H #define CSM_WOLRD_IDTABLEPROXYMODEL_H #include #include #include #include #include "../filter/node.hpp" #include "columns.hpp" namespace CSMWorld { class IdTableProxyModel : public QSortFilterProxyModel { Q_OBJECT boost::shared_ptr mFilter; std::map mColumnMap; // column ID, column index in this model (or -1) // Cache of enum values for enum columns (e.g. Modified, Record Type). // Used to speed up comparisons during the sort by such columns. typedef std::map > EnumColumnCache; mutable EnumColumnCache mEnumColumnCache; protected: IdTableBase *mSourceModel; private: void updateColumnMap(); public: IdTableProxyModel (QObject *parent = 0); virtual QModelIndex getModelIndex (const std::string& id, int column) const; virtual void setSourceModel(QAbstractItemModel *model); void setFilter (const boost::shared_ptr& filter); void refreshFilter(); protected: virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; virtual bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const; QString getRecordId(int sourceRow) const; protected slots: virtual void sourceRowsInserted(const QModelIndex &parent, int start, int end); virtual void sourceRowsRemoved(const QModelIndex &parent, int start, int end); virtual void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); signals: void rowAdded(const std::string &id); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/idtree.cpp000066400000000000000000000214431264522266000230640ustar00rootroot00000000000000#include "idtree.hpp" #include "nestedtablewrapper.hpp" #include "collectionbase.hpp" #include "nestedcollection.hpp" #include "columnbase.hpp" // NOTE: parent class still needs idCollection CSMWorld::IdTree::IdTree (NestedCollection *nestedCollection, CollectionBase *idCollection, unsigned int features) : IdTable (idCollection, features), mNestedCollection (nestedCollection) {} CSMWorld::IdTree::~IdTree() {} int CSMWorld::IdTree::rowCount (const QModelIndex & parent) const { if (hasChildren(parent)) return mNestedCollection->getNestedRowsCount(parent.row(), parent.column()); return IdTable::rowCount(parent); } int CSMWorld::IdTree::columnCount (const QModelIndex & parent) const { if (hasChildren(parent)) return mNestedCollection->getNestedColumnsCount(parent.row(), parent.column()); return IdTable::columnCount(parent); } QVariant CSMWorld::IdTree::data (const QModelIndex & index, int role) const { if (!index.isValid()) return QVariant(); if (index.internalId() != 0) { std::pair parentAddress(unfoldIndexAddress(index.internalId())); const NestableColumn *parentColumn = mNestedCollection->getNestableColumn(parentAddress.second); if (role == ColumnBase::Role_Display) return parentColumn->nestedColumn(index.column()).mDisplayType; if (role == ColumnBase::Role_ColumnId) return parentColumn->nestedColumn(index.column()).mColumnId; if (role == Qt::EditRole && !parentColumn->nestedColumn(index.column()).isEditable()) return QVariant(); if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); return mNestedCollection->getNestedData(parentAddress.first, parentAddress.second, index.row(), index.column()); } else { return IdTable::data(index, role); } } QVariant CSMWorld::IdTree::nestedHeaderData(int section, int subSection, Qt::Orientation orientation, int role) const { if (section < 0 || section >= idCollection()->getColumns()) return QVariant(); const NestableColumn *parentColumn = mNestedCollection->getNestableColumn(section); if (orientation==Qt::Vertical) return QVariant(); if (role==Qt::DisplayRole) return tr(parentColumn->nestedColumn(subSection).getTitle().c_str()); if (role==ColumnBase::Role_Flags) return parentColumn->nestedColumn(subSection).mFlags; if (role==ColumnBase::Role_Display) return parentColumn->nestedColumn(subSection).mDisplayType; if (role==ColumnBase::Role_ColumnId) return parentColumn->nestedColumn(subSection).mColumnId; return QVariant(); } bool CSMWorld::IdTree::setData (const QModelIndex &index, const QVariant &value, int role) { if (index.internalId() != 0) { if (idCollection()->getColumn(parent(index).column()).isEditable() && role==Qt::EditRole) { const std::pair& parentAddress(unfoldIndexAddress(index.internalId())); mNestedCollection->setNestedData(parentAddress.first, parentAddress.second, value, index.row(), index.column()); emit dataChanged(index, index); // Modifying a value can also change the Modified status of a record. int stateColumn = searchColumnIndex(Columns::ColumnId_Modification); if (stateColumn != -1) { QModelIndex stateIndex = this->index(index.parent().row(), stateColumn); emit dataChanged(stateIndex, stateIndex); } return true; } else return false; } return IdTable::setData(index, value, role); } Qt::ItemFlags CSMWorld::IdTree::flags (const QModelIndex & index) const { if (!index.isValid()) return 0; if (index.internalId() != 0) { std::pair parentAddress(unfoldIndexAddress(index.internalId())); Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (mNestedCollection->getNestableColumn(parentAddress.second)->nestedColumn(index.column()).isEditable()) flags |= Qt::ItemIsEditable; return flags; } else return IdTable::flags(index); } bool CSMWorld::IdTree::removeRows (int row, int count, const QModelIndex& parent) { if (parent.isValid()) { beginRemoveRows (parent, row, row+count-1); for (int i = 0; i < count; ++i) { mNestedCollection->removeNestedRows(parent.row(), parent.column(), row+i); } endRemoveRows(); emit dataChanged (CSMWorld::IdTree::index (parent.row(), 0), CSMWorld::IdTree::index (parent.row(), idCollection()->getColumns()-1)); return true; } else return IdTable::removeRows(row, count, parent); } void CSMWorld::IdTree::addNestedRow(const QModelIndex& parent, int position) { if (!hasChildren(parent)) throw std::logic_error("Tried to set nested table, but index has no children"); int row = parent.row(); beginInsertRows(parent, position, position); mNestedCollection->addNestedRow(row, parent.column(), position); endInsertRows(); emit dataChanged (CSMWorld::IdTree::index (row, 0), CSMWorld::IdTree::index (row, idCollection()->getColumns()-1)); } QModelIndex CSMWorld::IdTree::index (int row, int column, const QModelIndex& parent) const { unsigned int encodedId = 0; if (parent.isValid()) { encodedId = this->foldIndexAddress(parent); } if (row < 0 || row >= rowCount(parent)) return QModelIndex(); if (column < 0 || column >= columnCount(parent)) return QModelIndex(); return createIndex(row, column, encodedId); // store internal id } QModelIndex CSMWorld::IdTree::getNestedModelIndex (const std::string& id, int column) const { return CSMWorld::IdTable::index(idCollection()->getIndex (id), column); } QModelIndex CSMWorld::IdTree::parent (const QModelIndex& index) const { if (index.internalId() == 0) // 0 is used for indexs with invalid parent (top level data) return QModelIndex(); unsigned int id = index.internalId(); const std::pair& adress(unfoldIndexAddress(id)); if (adress.first >= this->rowCount() || adress.second >= this->columnCount()) throw "Parent index is not present in the model"; return createIndex(adress.first, adress.second); } unsigned int CSMWorld::IdTree::foldIndexAddress (const QModelIndex& index) const { unsigned int out = index.row() * this->columnCount(); out += index.column(); return ++out; } std::pair< int, int > CSMWorld::IdTree::unfoldIndexAddress (unsigned int id) const { if (id == 0) throw "Attempt to unfold index id of the top level data cell"; --id; int row = id / this->columnCount(); int column = id - row * this->columnCount(); return std::make_pair (row, column); } // FIXME: Not sure why this check is also needed? // // index.data().isValid() requires RefIdAdapter::getData() to return a valid QVariant for // nested columns (refidadapterimp.hpp) // // Also see comments in refidadapter.hpp and refidadapterimp.hpp. bool CSMWorld::IdTree::hasChildren(const QModelIndex& index) const { return (index.isValid() && index.internalId() == 0 && mNestedCollection->getNestableColumn(index.column())->hasChildren() && index.data().isValid()); } void CSMWorld::IdTree::setNestedTable(const QModelIndex& index, const CSMWorld::NestedTableWrapperBase& nestedTable) { if (!hasChildren(index)) throw std::logic_error("Tried to set nested table, but index has no children"); bool removeRowsMode = false; if (nestedTable.size() != this->nestedTable(index)->size()) { emit resetStart(this->index(index.row(), 0).data().toString()); removeRowsMode = true; } mNestedCollection->setNestedTable(index.row(), index.column(), nestedTable); emit dataChanged (CSMWorld::IdTree::index (index.row(), 0), CSMWorld::IdTree::index (index.row(), idCollection()->getColumns()-1)); if (removeRowsMode) { emit resetEnd(this->index(index.row(), 0).data().toString()); } } CSMWorld::NestedTableWrapperBase* CSMWorld::IdTree::nestedTable(const QModelIndex& index) const { if (!hasChildren(index)) throw std::logic_error("Tried to retrive nested table, but index has no children"); return mNestedCollection->nestedTable(index.row(), index.column()); } int CSMWorld::IdTree::searchNestedColumnIndex(int parentColumn, Columns::ColumnId id) { return mNestedCollection->searchNestedColumnIndex(parentColumn, id); } int CSMWorld::IdTree::findNestedColumnIndex(int parentColumn, Columns::ColumnId id) { return mNestedCollection->findNestedColumnIndex(parentColumn, id); } openmw-openmw-0.38.0/apps/opencs/model/world/idtree.hpp000066400000000000000000000065761264522266000231030ustar00rootroot00000000000000#ifndef CSM_WOLRD_IDTREE_H #define CSM_WOLRD_IDTREE_H #include "idtable.hpp" #include "universalid.hpp" #include "columns.hpp" /*! \brief * Class for holding the model. Uses typical qt table abstraction/interface for granting access * to the individiual fields of the records, Some records are holding nested data (for instance * inventory list of the npc). In casses like this, table model offers interface to access * nested data in the qt way - that is specify parent. Since some of those nested data require * multiple columns to represent informations, single int (default way to index model in the * qmodelindex) is not sufficiant. Therefore tablemodelindex class can hold two ints for the * sake of indexing two dimensions of the table. This model does not support multiple levels of * the nested data. Vast majority of methods makes sense only for the top level data. */ namespace CSMWorld { class NestedCollection; struct RecordBase; struct NestedTableWrapperBase; class IdTree : public IdTable { Q_OBJECT private: NestedCollection *mNestedCollection; // not implemented IdTree (const IdTree&); IdTree& operator= (const IdTree&); unsigned int foldIndexAddress(const QModelIndex& index) const; std::pair unfoldIndexAddress(unsigned int id) const; public: IdTree (NestedCollection *nestedCollection, CollectionBase *idCollection, unsigned int features = 0); ///< The ownerships of \a nestedCollecton and \a idCollection are not transferred. virtual ~IdTree(); virtual int rowCount (const QModelIndex & parent = QModelIndex()) const; virtual int columnCount (const QModelIndex & parent = QModelIndex()) const; virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; virtual bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); virtual Qt::ItemFlags flags (const QModelIndex & index) const; virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const; virtual QModelIndex parent (const QModelIndex& index) const; QModelIndex getNestedModelIndex (const std::string& id, int column) const; QVariant nestedHeaderData(int section, int subSection, Qt::Orientation orientation, int role = Qt::DisplayRole) const; NestedTableWrapperBase* nestedTable(const QModelIndex &index) const; void setNestedTable(const QModelIndex &index, const NestedTableWrapperBase& nestedTable); void addNestedRow (const QModelIndex& parent, int position); virtual bool hasChildren (const QModelIndex& index) const; virtual int searchNestedColumnIndex(int parentColumn, Columns::ColumnId id); ///< \return the column index or -1 if the requested column wasn't found. virtual int findNestedColumnIndex(int parentColumn, Columns::ColumnId id); ///< \return the column index or throws an exception if the requested column wasn't found. signals: void resetStart(const QString& id); void resetEnd(const QString& id); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/info.hpp000066400000000000000000000003141264522266000225420ustar00rootroot00000000000000#ifndef CSM_WOLRD_INFO_H #define CSM_WOLRD_INFO_H #include namespace CSMWorld { struct Info : public ESM::DialInfo { std::string mTopicId; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/infocollection.cpp000066400000000000000000000142321264522266000246150ustar00rootroot00000000000000#include "infocollection.hpp" #include #include #include #include #include void CSMWorld::InfoCollection::load (const Info& record, bool base) { int index = searchId (record.mId); if (index==-1) { // new record Record record2; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = record; int index = -1; std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId); if (!record2.get().mPrev.empty()) { index = getInfoIndex (record2.get().mPrev, topic); if (index!=-1) ++index; } if (index==-1 && !record2.get().mNext.empty()) { index = getInfoIndex (record2.get().mNext, topic); } if (index==-1) { Range range = getTopicRange (topic); index = std::distance (getRecords().begin(), range.second); } insertRecord (record2, index); } else { // old record Record record2 = getRecord (index); if (base) record2.mBase = record; else record2.setModified (record); setRecord (index, record2); } } int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const { std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id; std::pair range = getTopicRange (topic); for (; range.first!=range.second; ++range.first) if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId)) return std::distance (getRecords().begin(), range.first); return -1; } int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const { std::string::size_type separator = id.find_last_of ('#'); if (separator==std::string::npos) throw std::runtime_error ("invalid info ID: " + id); std::pair range = getTopicRange (id.substr (0, separator)); if (range.first==range.second) return Collection >::getAppendIndex (id, type); return std::distance (getRecords().begin(), range.second); } bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector& newOrder) { // check if the range is valid int lastIndex = baseIndex + newOrder.size() -1; if (lastIndex>=getSize()) return false; // Check that topics match if (!Misc::StringUtils::ciEqual(getRecord(baseIndex).get().mTopicId, getRecord(lastIndex).get().mTopicId)) return false; // reorder return reorderRowsImp (baseIndex, newOrder); } void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) { Info info; bool isDeleted = false; info.load (reader, isDeleted); std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + info.mId; if (isDeleted) { int index = searchId (id); if (index==-1) { // deleting a record that does not exist // ignore it for now /// \todo report the problem to the user } else if (base) { removeRows (index, 1); } else { Record record = getRecord (index); record.mState = RecordBase::State_Deleted; setRecord (index, record); } } else { info.mTopicId = dialogue.mId; info.mId = id; load (info, base); } } CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic) const { std::string topic2 = Misc::StringUtils::lowerCase (topic); std::map::const_iterator iter = getIdMap().lower_bound (topic2); // Skip invalid records: The beginning of a topic string could be identical to another topic // string. for (; iter!=getIdMap().end(); ++iter) { std::string testTopicId = Misc::StringUtils::lowerCase (getRecord (iter->second).get().mTopicId); if (testTopicId==topic2) break; std::size_t size = topic2.size(); if (testTopicId.size()second; while (begin != getRecords().begin()) { if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2)) { // we've gone one too far, go back ++begin; break; } --begin; } // Find end RecordConstIterator end = begin; for (; end!=getRecords().end(); ++end) if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2)) break; return Range (begin, end); } void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId) { std::string id = Misc::StringUtils::lowerCase(dialogueId); std::vector erasedRecords; std::map::const_iterator current = getIdMap().lower_bound(id); std::map::const_iterator end = getIdMap().end(); for (; current != end; ++current) { Record record = getRecord(current->second); if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId)) { if (record.mState == RecordBase::State_ModifiedOnly) { erasedRecords.push_back(current->second); } else { record.mState = RecordBase::State_Deleted; setRecord(current->second, record); } } else { break; } } while (!erasedRecords.empty()) { removeRows(erasedRecords.back(), 1); erasedRecords.pop_back(); } } openmw-openmw-0.38.0/apps/opencs/model/world/infocollection.hpp000066400000000000000000000033061264522266000246220ustar00rootroot00000000000000#ifndef CSM_WOLRD_INFOCOLLECTION_H #define CSM_WOLRD_INFOCOLLECTION_H #include "collection.hpp" #include "info.hpp" namespace ESM { struct Dialogue; } namespace CSMWorld { class InfoCollection : public Collection > { public: typedef std::vector >::const_iterator RecordConstIterator; typedef std::pair Range; private: void load (const Info& record, bool base); int getInfoIndex (const std::string& id, const std::string& topic) const; ///< Return index for record \a id or -1 (if not present; deleted records are considered) /// /// \param id info ID without topic prefix public: virtual int getAppendIndex (const std::string& id, UniversalId::Type type = UniversalId::Type_None) const; ///< \param type Will be ignored, unless the collection supports multiple record types virtual bool reorderRows (int baseIndex, const std::vector& newOrder); ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// /// \return Success? void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue); Range getTopicRange (const std::string& topic) const; ///< Return iterators that point to the beginning and past the end of the range for /// the given topic. void removeDialogueInfos(const std::string& dialogueId); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/infotableproxymodel.cpp000066400000000000000000000071511264522266000256760ustar00rootroot00000000000000#include "infotableproxymodel.hpp" #include #include "idtablebase.hpp" #include "columns.hpp" namespace { QString toLower(const QString &str) { return QString::fromUtf8(Misc::StringUtils::lowerCase(str.toUtf8().constData()).c_str()); } } CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type type, QObject *parent) : IdTableProxyModel(parent), mType(type), mInfoColumnId(type == UniversalId::Type_TopicInfos ? Columns::ColumnId_Topic : Columns::ColumnId_Journal), mInfoColumnIndex(-1), mLastAddedSourceRow(-1) { Q_ASSERT(type == UniversalId::Type_TopicInfos || type == UniversalId::Type_JournalInfos); } void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceModel) { IdTableProxyModel::setSourceModel(sourceModel); if (mSourceModel != NULL) { mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId); mFirstRowCache.clear(); } } bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { Q_ASSERT(mSourceModel != NULL); QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column()); QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column()); // If both indexes are belonged to the same Topic/Journal, compare their original rows only if (first.row() == second.row()) { return sortOrder() == Qt::AscendingOrder ? left.row() < right.row() : right.row() < left.row(); } return IdTableProxyModel::lessThan(first, second); } int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const { Q_ASSERT(mSourceModel != NULL); int row = currentRow; int column = mInfoColumnIndex; QString info = toLower(mSourceModel->data(mSourceModel->index(row, column)).toString()); if (mFirstRowCache.contains(info)) { return mFirstRowCache[info]; } while (--row >= 0 && toLower(mSourceModel->data(mSourceModel->index(row, column)).toString()) == info); ++row; mFirstRowCache[info] = row; return row; } void CSMWorld::InfoTableProxyModel::sourceRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/) { refreshFilter(); mFirstRowCache.clear(); } void CSMWorld::InfoTableProxyModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int end) { refreshFilter(); if (!parent.isValid()) { mFirstRowCache.clear(); // We can't re-sort the model here, because the topic of the added row isn't set yet. // Store the row index for using in the first dataChanged() after this row insertion. mLastAddedSourceRow = end; } } void CSMWorld::InfoTableProxyModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { refreshFilter(); if (mLastAddedSourceRow != -1 && topLeft.row() <= mLastAddedSourceRow && bottomRight.row() >= mLastAddedSourceRow) { // Now the topic of the last added row is set, // so we can re-sort the model to ensure the corrent position of this row int column = sortColumn(); Qt::SortOrder order = sortOrder(); sort(mInfoColumnIndex); // Restore the correct position of an added row sort(column, order); // Restore the original sort order emit rowAdded(getRecordId(mLastAddedSourceRow).toUtf8().constData()); // Make sure that we perform a re-sorting only in the first dataChanged() after a row insertion mLastAddedSourceRow = -1; } } openmw-openmw-0.38.0/apps/opencs/model/world/infotableproxymodel.hpp000066400000000000000000000025721264522266000257050ustar00rootroot00000000000000#ifndef CSM_WORLD_INFOTABLEPROXYMODEL_HPP #define CSM_WORLD_INFOTABLEPROXYMODEL_HPP #include #include "idtableproxymodel.hpp" #include "columns.hpp" #include "universalid.hpp" namespace CSMWorld { class IdTableBase; class InfoTableProxyModel : public IdTableProxyModel { Q_OBJECT UniversalId::Type mType; Columns::ColumnId mInfoColumnId; ///< Contains ID for Topic or Journal ID int mInfoColumnIndex; int mLastAddedSourceRow; mutable QHash mFirstRowCache; int getFirstInfoRow(int currentRow) const; ///< Finds the first row with the same topic (journal entry) as in \a currentRow ///< \a currentRow is a row of the source model. public: InfoTableProxyModel(UniversalId::Type type, QObject *parent = 0); virtual void setSourceModel(QAbstractItemModel *sourceModel); protected: virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; protected slots: virtual void sourceRowsInserted(const QModelIndex &parent, int start, int end); virtual void sourceRowsRemoved(const QModelIndex &parent, int start, int end); virtual void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/land.cpp000066400000000000000000000004311264522266000225200ustar00rootroot00000000000000#include "land.hpp" #include namespace CSMWorld { void Land::load(ESM::ESMReader &esm, bool &isDeleted) { ESM::Land::load(esm, isDeleted); std::ostringstream stream; stream << "#" << mX << " " << mY; mId = stream.str(); } } openmw-openmw-0.38.0/apps/opencs/model/world/land.hpp000066400000000000000000000007061264522266000225320ustar00rootroot00000000000000#ifndef CSM_WORLD_LAND_H #define CSM_WORLD_LAND_H #include #include namespace CSMWorld { /// \brief Wrapper for Land record. Encodes X and Y cell index in the ID. /// /// \todo Add worldspace support to the Land record. struct Land : public ESM::Land { std::string mId; /// Loads the metadata and ID void load (ESM::ESMReader &esm, bool &isDeleted); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/landtexture.cpp000066400000000000000000000004021264522266000241370ustar00rootroot00000000000000#include "landtexture.hpp" #include namespace CSMWorld { void LandTexture::load(ESM::ESMReader &esm, bool &isDeleted) { ESM::LandTexture::load(esm, isDeleted); mPluginIndex = esm.getIndex(); } } openmw-openmw-0.38.0/apps/opencs/model/world/landtexture.hpp000066400000000000000000000006321264522266000241510ustar00rootroot00000000000000#ifndef CSM_WORLD_LANDTEXTURE_H #define CSM_WORLD_LANDTEXTURE_H #include #include namespace CSMWorld { /// \brief Wrapper for LandTexture record, providing info which plugin the LandTexture was loaded from. struct LandTexture : public ESM::LandTexture { int mPluginIndex; void load (ESM::ESMReader &esm, bool &isDeleted); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/metadata.cpp000066400000000000000000000012001264522266000233550ustar00rootroot00000000000000#include "metadata.hpp" #include #include #include void CSMWorld::MetaData::blank() { mFormat = ESM::Header::CurrentFormat; mAuthor.clear(); mDescription.clear(); } void CSMWorld::MetaData::load (ESM::ESMReader& esm) { mFormat = esm.getHeader().mFormat; mAuthor = esm.getHeader().mData.author.toString(); mDescription = esm.getHeader().mData.desc.toString(); } void CSMWorld::MetaData::save (ESM::ESMWriter& esm) const { esm.setFormat (mFormat); esm.setAuthor (mAuthor); esm.setDescription (mDescription); } openmw-openmw-0.38.0/apps/opencs/model/world/metadata.hpp000066400000000000000000000006511264522266000233730ustar00rootroot00000000000000#ifndef CSM_WOLRD_METADATA_H #define CSM_WOLRD_METADATA_H #include namespace ESM { class ESMReader; class ESMWriter; } namespace CSMWorld { struct MetaData { std::string mId; int mFormat; std::string mAuthor; std::string mDescription; void blank(); void load (ESM::ESMReader& esm); void save (ESM::ESMWriter& esm) const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/nestedcoladapterimp.cpp000066400000000000000000001254741264522266000256500ustar00rootroot00000000000000#include "nestedcoladapterimp.hpp" #include #include #include "idcollection.hpp" #include "pathgrid.hpp" #include "info.hpp" namespace CSMWorld { PathgridPointListAdapter::PathgridPointListAdapter () {} void PathgridPointListAdapter::addRow(Record& record, int position) const { Pathgrid pathgrid = record.get(); ESM::Pathgrid::PointList& points = pathgrid.mPoints; // blank row ESM::Pathgrid::Point point; point.mX = 0; point.mY = 0; point.mZ = 0; point.mAutogenerated = 0; point.mConnectionNum = 0; point.mUnknown = 0; // inserting a point should trigger re-indexing of the edges // // FIXME: does not auto refresh edges table view std::vector::iterator iter = pathgrid.mEdges.begin(); for (;iter != pathgrid.mEdges.end(); ++iter) { if ((*iter).mV0 >= position) (*iter).mV0++; if ((*iter).mV1 >= position) (*iter).mV1++; } points.insert(points.begin()+position, point); pathgrid.mData.mS2 += 1; // increment the number of points record.setModified (pathgrid); } void PathgridPointListAdapter::removeRow(Record& record, int rowToRemove) const { Pathgrid pathgrid = record.get(); ESM::Pathgrid::PointList& points = pathgrid.mPoints; if (rowToRemove < 0 || rowToRemove >= static_cast (points.size())) throw std::runtime_error ("index out of range"); // deleting a point should trigger re-indexing of the edges // dangling edges are not allowed and hence removed // // FIXME: does not auto refresh edges table view std::vector::iterator iter = pathgrid.mEdges.begin(); for (; iter != pathgrid.mEdges.end();) { if (((*iter).mV0 == rowToRemove) || ((*iter).mV1 == rowToRemove)) iter = pathgrid.mEdges.erase(iter); else { if ((*iter).mV0 > rowToRemove) (*iter).mV0--; if ((*iter).mV1 > rowToRemove) (*iter).mV1--; ++iter; } } points.erase(points.begin()+rowToRemove); pathgrid.mData.mS2 -= 1; // decrement the number of points record.setModified (pathgrid); } void PathgridPointListAdapter::setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { Pathgrid pathgrid = record.get(); pathgrid.mPoints = static_cast(nestedTable).mRecord.mPoints; pathgrid.mData.mS2 = static_cast(nestedTable).mRecord.mData.mS2; // also update edges in case points were added/removed pathgrid.mEdges = static_cast(nestedTable).mRecord.mEdges; record.setModified (pathgrid); } NestedTableWrapperBase* PathgridPointListAdapter::table(const Record& record) const { // deleted by dtor of NestedTableStoring return new PathgridPointsWrap(record.get()); } QVariant PathgridPointListAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { ESM::Pathgrid::Point point = record.get().mPoints[subRowIndex]; switch (subColIndex) { case 0: return subRowIndex; case 1: return point.mX; case 2: return point.mY; case 3: return point.mZ; default: throw std::runtime_error("Pathgrid point subcolumn index out of range"); } } void PathgridPointListAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { Pathgrid pathgrid = record.get(); ESM::Pathgrid::Point point = pathgrid.mPoints[subRowIndex]; switch (subColIndex) { case 0: return; // return without saving case 1: point.mX = value.toInt(); break; case 2: point.mY = value.toInt(); break; case 3: point.mZ = value.toInt(); break; default: throw std::runtime_error("Pathgrid point subcolumn index out of range"); } pathgrid.mPoints[subRowIndex] = point; record.setModified (pathgrid); } int PathgridPointListAdapter::getColumnsCount(const Record& record) const { return 4; } int PathgridPointListAdapter::getRowsCount(const Record& record) const { return static_cast(record.get().mPoints.size()); } PathgridEdgeListAdapter::PathgridEdgeListAdapter () {} // ToDo: seems to be auto-sorted in the dialog table display after insertion void PathgridEdgeListAdapter::addRow(Record& record, int position) const { Pathgrid pathgrid = record.get(); ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; // blank row ESM::Pathgrid::Edge edge; edge.mV0 = 0; edge.mV1 = 0; // NOTE: inserting a blank edge does not really make sense, perhaps this should be a // logic_error exception // // Currently the code assumes that the end user to know what he/she is doing. // e.g. Edges come in pairs, from points a->b and b->a edges.insert(edges.begin()+position, edge); record.setModified (pathgrid); } void PathgridEdgeListAdapter::removeRow(Record& record, int rowToRemove) const { Pathgrid pathgrid = record.get(); ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; if (rowToRemove < 0 || rowToRemove >= static_cast (edges.size())) throw std::runtime_error ("index out of range"); edges.erase(edges.begin()+rowToRemove); record.setModified (pathgrid); } void PathgridEdgeListAdapter::setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { Pathgrid pathgrid = record.get(); pathgrid.mEdges = static_cast &>(nestedTable).mNestedTable; record.setModified (pathgrid); } NestedTableWrapperBase* PathgridEdgeListAdapter::table(const Record& record) const { // deleted by dtor of NestedTableStoring return new NestedTableWrapper(record.get().mEdges); } QVariant PathgridEdgeListAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { Pathgrid pathgrid = record.get(); if (subRowIndex < 0 || subRowIndex >= static_cast (pathgrid.mEdges.size())) throw std::runtime_error ("index out of range"); ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex]; switch (subColIndex) { case 0: return subRowIndex; case 1: return edge.mV0; case 2: return edge.mV1; default: throw std::runtime_error("Pathgrid edge subcolumn index out of range"); } } // ToDo: detect duplicates in mEdges void PathgridEdgeListAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { Pathgrid pathgrid = record.get(); if (subRowIndex < 0 || subRowIndex >= static_cast (pathgrid.mEdges.size())) throw std::runtime_error ("index out of range"); ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex]; switch (subColIndex) { case 0: return; // return without saving case 1: edge.mV0 = value.toInt(); break; case 2: edge.mV1 = value.toInt(); break; default: throw std::runtime_error("Pathgrid edge subcolumn index out of range"); } pathgrid.mEdges[subRowIndex] = edge; record.setModified (pathgrid); } int PathgridEdgeListAdapter::getColumnsCount(const Record& record) const { return 3; } int PathgridEdgeListAdapter::getRowsCount(const Record& record) const { return static_cast(record.get().mEdges.size()); } FactionReactionsAdapter::FactionReactionsAdapter () {} void FactionReactionsAdapter::addRow(Record& record, int position) const { ESM::Faction faction = record.get(); std::map& reactions = faction.mReactions; // blank row reactions.insert(std::make_pair("", 0)); record.setModified (faction); } void FactionReactionsAdapter::removeRow(Record& record, int rowToRemove) const { ESM::Faction faction = record.get(); std::map& reactions = faction.mReactions; if (rowToRemove < 0 || rowToRemove >= static_cast (reactions.size())) throw std::runtime_error ("index out of range"); // FIXME: how to ensure that the map entries correspond to table indicies? // WARNING: Assumed that the table view has the same order as std::map std::map::iterator iter = reactions.begin(); for(int i = 0; i < rowToRemove; ++i) ++iter; reactions.erase(iter); record.setModified (faction); } void FactionReactionsAdapter::setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { ESM::Faction faction = record.get(); faction.mReactions = static_cast >&>(nestedTable).mNestedTable; record.setModified (faction); } NestedTableWrapperBase* FactionReactionsAdapter::table(const Record& record) const { // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mReactions); } QVariant FactionReactionsAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { ESM::Faction faction = record.get(); std::map& reactions = faction.mReactions; if (subRowIndex < 0 || subRowIndex >= static_cast (reactions.size())) throw std::runtime_error ("index out of range"); // FIXME: how to ensure that the map entries correspond to table indicies? // WARNING: Assumed that the table view has the same order as std::map std::map::const_iterator iter = reactions.begin(); for(int i = 0; i < subRowIndex; ++i) ++iter; switch (subColIndex) { case 0: return QString((*iter).first.c_str()); case 1: return (*iter).second; default: throw std::runtime_error("Faction reactions subcolumn index out of range"); } } void FactionReactionsAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { ESM::Faction faction = record.get(); std::map& reactions = faction.mReactions; if (subRowIndex < 0 || subRowIndex >= static_cast (reactions.size())) throw std::runtime_error ("index out of range"); // FIXME: how to ensure that the map entries correspond to table indicies? // WARNING: Assumed that the table view has the same order as std::map std::map::iterator iter = reactions.begin(); for(int i = 0; i < subRowIndex; ++i) ++iter; std::string factionId = (*iter).first; int reaction = (*iter).second; switch (subColIndex) { case 0: { reactions.erase(iter); reactions.insert(std::make_pair(value.toString().toUtf8().constData(), reaction)); break; } case 1: { reactions[factionId] = value.toInt(); break; } default: throw std::runtime_error("Faction reactions subcolumn index out of range"); } record.setModified (faction); } int FactionReactionsAdapter::getColumnsCount(const Record& record) const { return 2; } int FactionReactionsAdapter::getRowsCount(const Record& record) const { return static_cast(record.get().mReactions.size()); } RegionSoundListAdapter::RegionSoundListAdapter () {} void RegionSoundListAdapter::addRow(Record& record, int position) const { ESM::Region region = record.get(); std::vector& soundList = region.mSoundList; // blank row ESM::Region::SoundRef soundRef; soundRef.mSound.assign(""); soundRef.mChance = 0; soundList.insert(soundList.begin()+position, soundRef); record.setModified (region); } void RegionSoundListAdapter::removeRow(Record& record, int rowToRemove) const { ESM::Region region = record.get(); std::vector& soundList = region.mSoundList; if (rowToRemove < 0 || rowToRemove >= static_cast (soundList.size())) throw std::runtime_error ("index out of range"); soundList.erase(soundList.begin()+rowToRemove); record.setModified (region); } void RegionSoundListAdapter::setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { ESM::Region region = record.get(); region.mSoundList = static_cast >&>(nestedTable).mNestedTable; record.setModified (region); } NestedTableWrapperBase* RegionSoundListAdapter::table(const Record& record) const { // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mSoundList); } QVariant RegionSoundListAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { ESM::Region region = record.get(); std::vector& soundList = region.mSoundList; if (subRowIndex < 0 || subRowIndex >= static_cast (soundList.size())) throw std::runtime_error ("index out of range"); ESM::Region::SoundRef soundRef = soundList[subRowIndex]; switch (subColIndex) { case 0: return QString(soundRef.mSound.toString().c_str()); case 1: return soundRef.mChance; default: throw std::runtime_error("Region sounds subcolumn index out of range"); } } void RegionSoundListAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { ESM::Region region = record.get(); std::vector& soundList = region.mSoundList; if (subRowIndex < 0 || subRowIndex >= static_cast (soundList.size())) throw std::runtime_error ("index out of range"); ESM::Region::SoundRef soundRef = soundList[subRowIndex]; switch (subColIndex) { case 0: soundRef.mSound.assign(value.toString().toUtf8().constData()); break; case 1: soundRef.mChance = static_cast(value.toInt()); break; default: throw std::runtime_error("Region sounds subcolumn index out of range"); } region.mSoundList[subRowIndex] = soundRef; record.setModified (region); } int RegionSoundListAdapter::getColumnsCount(const Record& record) const { return 2; } int RegionSoundListAdapter::getRowsCount(const Record& record) const { return static_cast(record.get().mSoundList.size()); } InfoListAdapter::InfoListAdapter () {} void InfoListAdapter::addRow(Record& record, int position) const { throw std::logic_error ("cannot add a row to a fixed table"); } void InfoListAdapter::removeRow(Record& record, int rowToRemove) const { throw std::logic_error ("cannot remove a row to a fixed table"); } void InfoListAdapter::setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { throw std::logic_error ("table operation not supported"); } NestedTableWrapperBase* InfoListAdapter::table(const Record& record) const { throw std::logic_error ("table operation not supported"); } QVariant InfoListAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { Info info = record.get(); if (subColIndex == 0) return QString(info.mResultScript.c_str()); else throw std::runtime_error("Trying to access non-existing column in the nested table!"); } void InfoListAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { Info info = record.get(); if (subColIndex == 0) info.mResultScript = value.toString().toStdString(); else throw std::runtime_error("Trying to access non-existing column in the nested table!"); record.setModified (info); } int InfoListAdapter::getColumnsCount(const Record& record) const { return 1; } int InfoListAdapter::getRowsCount(const Record& record) const { return 1; // fixed at size 1 } // ESM::DialInfo::SelectStruct.mSelectRule // 012345... // ^^^ ^^ // ||| || // ||| |+------------- condition variable string // ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc // ||+---------------- function index (encoded, where function == '1') // |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc // +------------------ unknown // InfoConditionAdapter::InfoConditionAdapter () {} void InfoConditionAdapter::addRow(Record& record, int position) const { Info info = record.get(); std::vector& conditions = info.mSelects; // blank row ESM::DialInfo::SelectStruct condStruct; condStruct.mSelectRule = "00000"; condStruct.mValue = ESM::Variant(); condStruct.mValue.setType(ESM::VT_Int); // default to ints conditions.insert(conditions.begin()+position, condStruct); record.setModified (info); } void InfoConditionAdapter::removeRow(Record& record, int rowToRemove) const { Info info = record.get(); std::vector& conditions = info.mSelects; if (rowToRemove < 0 || rowToRemove >= static_cast (conditions.size())) throw std::runtime_error ("index out of range"); conditions.erase(conditions.begin()+rowToRemove); record.setModified (info); } void InfoConditionAdapter::setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { Info info = record.get(); info.mSelects = static_cast >&>(nestedTable).mNestedTable; record.setModified (info); } NestedTableWrapperBase* InfoConditionAdapter::table(const Record& record) const { // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mSelects); } // See the mappings in MWDialogue::SelectWrapper::getArgument // from ESM::Attribute, ESM::Skill and MWMechanics::CreatureStats (for AI) static std::map populateEncToInfoFunc() { std::map funcMap; funcMap["00"] = "Rank Low"; funcMap["01"] = "Rank High"; funcMap["02"] = "Rank Requirement"; funcMap["03"] = "Reputation"; funcMap["04"] = "Health Percent"; funcMap["05"] = "PC Reputation"; funcMap["06"] = "PC Level"; funcMap["07"] = "PC Health Percent"; funcMap["08"] = "PC Magicka"; funcMap["09"] = "PC Fatigue"; funcMap["10"] = "PC Strength"; funcMap["11"] = "PC Block"; funcMap["12"] = "PC Armorer"; funcMap["13"] = "PC Medium Armor"; funcMap["14"] = "PC Heavy Armor"; funcMap["15"] = "PC Blunt Weapon"; funcMap["16"] = "PC Long Blade"; funcMap["17"] = "PC Axe"; funcMap["18"] = "PC Spear"; funcMap["19"] = "PC Athletics"; funcMap["20"] = "PC Enchant"; funcMap["21"] = "PC Destruction"; funcMap["22"] = "PC Alteration"; funcMap["23"] = "PC Illusion"; funcMap["24"] = "PC Conjuration"; funcMap["25"] = "PC Mysticism"; funcMap["26"] = "PC Restoration"; funcMap["27"] = "PC Alchemy"; funcMap["28"] = "PC Unarmored"; funcMap["29"] = "PC Security"; funcMap["30"] = "PC Sneak"; funcMap["31"] = "PC Acrobatics"; funcMap["32"] = "PC Light Armor"; funcMap["33"] = "PC Short Blade"; funcMap["34"] = "PC Marksman"; funcMap["35"] = "PC Merchantile"; funcMap["36"] = "PC Speechcraft"; funcMap["37"] = "PC Hand To Hand"; funcMap["38"] = "PC Sex"; funcMap["39"] = "PC Expelled"; funcMap["40"] = "PC Common Disease"; funcMap["41"] = "PC Blight Disease"; funcMap["42"] = "PC Clothing Modifier"; funcMap["43"] = "PC Crime Level"; funcMap["44"] = "Same Sex"; funcMap["45"] = "Same Race"; funcMap["46"] = "Same Faction"; funcMap["47"] = "Faction Rank Difference"; funcMap["48"] = "Detected"; funcMap["49"] = "Alarmed"; funcMap["50"] = "Choice"; funcMap["51"] = "PC Intelligence"; funcMap["52"] = "PC Willpower"; funcMap["53"] = "PC Agility"; funcMap["54"] = "PC Speed"; funcMap["55"] = "PC Endurance"; funcMap["56"] = "PC Personality"; funcMap["57"] = "PC Luck"; funcMap["58"] = "PC Corpus"; funcMap["59"] = "Weather"; funcMap["60"] = "PC Vampire"; funcMap["61"] = "Level"; funcMap["62"] = "Attacked"; funcMap["63"] = "Talked To PC"; funcMap["64"] = "PC Health"; funcMap["65"] = "Creature Target"; funcMap["66"] = "Friend Hit"; funcMap["67"] = "Fight"; funcMap["68"] = "Hello"; funcMap["69"] = "Alarm"; funcMap["70"] = "Flee"; funcMap["71"] = "Should Attack"; funcMap["72"] = "Werewolf"; funcMap["73"] = "PC Werewolf Kills"; return funcMap; } static const std::map sEncToInfoFunc = populateEncToInfoFunc(); QVariant InfoConditionAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { Info info = record.get(); std::vector& conditions = info.mSelects; if (subRowIndex < 0 || subRowIndex >= static_cast (conditions.size())) throw std::runtime_error ("index out of range"); switch (subColIndex) { case 0: { char condType = conditions[subRowIndex].mSelectRule[1]; switch (condType) { case '0': return 0; // blank space case '1': return 1; // Function case '2': return 2; // Global case '3': return 3; // Local case '4': return 4; // Journal case '5': return 5; // Item case '6': return 6; // Dead case '7': return 7; // Not ID case '8': return 8; // Not Factio case '9': return 9; // Not Class case 'A': return 10; // Not Race case 'B': return 11; // Not Cell case 'C': return 12; // Not Local default: return QVariant(); // TODO: log an error? } } case 1: { if (conditions[subRowIndex].mSelectRule[1] == '1') { // throws an exception if the encoding is not found return sEncToInfoFunc.at(conditions[subRowIndex].mSelectRule.substr(2, 2)).c_str(); } else return QString(conditions[subRowIndex].mSelectRule.substr(5).c_str()); } case 2: { char compType = conditions[subRowIndex].mSelectRule[4]; switch (compType) { case '0': return 3; // = case '1': return 0; // != case '2': return 4; // > case '3': return 5; // >= case '4': return 1; // < case '5': return 2; // <= default: return QVariant(); // TODO: log an error? } } case 3: { switch (conditions[subRowIndex].mValue.getType()) { case ESM::VT_String: { return QString::fromUtf8 (conditions[subRowIndex].mValue.getString().c_str()); } case ESM::VT_Int: case ESM::VT_Short: case ESM::VT_Long: { return conditions[subRowIndex].mValue.getInteger(); } case ESM::VT_Float: { return conditions[subRowIndex].mValue.getFloat(); } default: return QVariant(); } } default: throw std::runtime_error("Info condition subcolumn index out of range"); } } void InfoConditionAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { Info info = record.get(); std::vector& conditions = info.mSelects; if (subRowIndex < 0 || subRowIndex >= static_cast (conditions.size())) throw std::runtime_error ("index out of range"); switch (subColIndex) { case 0: { // See sInfoCondFunc in columns.cpp for the enum values switch (value.toInt()) { // FIXME: when these change the values of the other columns need to change // correspondingly (and automatically) case 1: { conditions[subRowIndex].mSelectRule[1] = '1'; // Function // default to "Rank Low" conditions[subRowIndex].mSelectRule[2] = '0'; conditions[subRowIndex].mSelectRule[3] = '0'; break; } case 2: conditions[subRowIndex].mSelectRule[1] = '2'; break; // Global case 3: conditions[subRowIndex].mSelectRule[1] = '3'; break; // Local case 4: conditions[subRowIndex].mSelectRule[1] = '4'; break; // Journal case 5: conditions[subRowIndex].mSelectRule[1] = '5'; break; // Item case 6: conditions[subRowIndex].mSelectRule[1] = '6'; break; // Dead case 7: conditions[subRowIndex].mSelectRule[1] = '7'; break; // Not ID case 8: conditions[subRowIndex].mSelectRule[1] = '8'; break; // Not Faction case 9: conditions[subRowIndex].mSelectRule[1] = '9'; break; // Not Class case 10: conditions[subRowIndex].mSelectRule[1] = 'A'; break; // Not Race case 11: conditions[subRowIndex].mSelectRule[1] = 'B'; break; // Not Cell case 12: conditions[subRowIndex].mSelectRule[1] = 'C'; break; // Not Local default: return; // return without saving } break; } case 1: { if (conditions[subRowIndex].mSelectRule[1] == '1') { std::map::const_iterator it = sEncToInfoFunc.begin(); for (;it != sEncToInfoFunc.end(); ++it) { if (it->second == value.toString().toUtf8().constData()) { std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 2); rule.append(it->first); // leave old values for undo (NOTE: may not be vanilla's behaviour) rule.append(conditions[subRowIndex].mSelectRule.substr(4)); conditions[subRowIndex].mSelectRule = rule; break; } } if (it == sEncToInfoFunc.end()) return; // return without saving; TODO: maybe log an error here } else { // FIXME: validate the string values before saving, based on the current function std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 5); conditions[subRowIndex].mSelectRule = rule.append(value.toString().toUtf8().constData()); } break; } case 2: { // See sInfoCondComp in columns.cpp for the enum values switch (value.toInt()) { case 0: conditions[subRowIndex].mSelectRule[4] = '1'; break; // != case 1: conditions[subRowIndex].mSelectRule[4] = '4'; break; // < case 2: conditions[subRowIndex].mSelectRule[4] = '5'; break; // <= case 3: conditions[subRowIndex].mSelectRule[4] = '0'; break; // = case 4: conditions[subRowIndex].mSelectRule[4] = '2'; break; // > case 5: conditions[subRowIndex].mSelectRule[4] = '3'; break; // >= default: return; // return without saving } break; } case 3: { switch (conditions[subRowIndex].mValue.getType()) { case ESM::VT_String: { conditions[subRowIndex].mValue.setString (value.toString().toUtf8().constData()); break; } case ESM::VT_Int: case ESM::VT_Short: case ESM::VT_Long: { conditions[subRowIndex].mValue.setInteger (value.toInt()); break; } case ESM::VT_Float: { conditions[subRowIndex].mValue.setFloat (value.toFloat()); break; } default: break; } break; } default: throw std::runtime_error("Info condition subcolumn index out of range"); } record.setModified (info); } int InfoConditionAdapter::getColumnsCount(const Record& record) const { return 4; } int InfoConditionAdapter::getRowsCount(const Record& record) const { return static_cast(record.get().mSelects.size()); } RaceAttributeAdapter::RaceAttributeAdapter () {} void RaceAttributeAdapter::addRow(Record& record, int position) const { // Do nothing, this table cannot be changed by the user } void RaceAttributeAdapter::removeRow(Record& record, int rowToRemove) const { // Do nothing, this table cannot be changed by the user } void RaceAttributeAdapter::setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { ESM::Race race = record.get(); race.mData = static_cast >&>(nestedTable).mNestedTable.at(0); record.setModified (race); } NestedTableWrapperBase* RaceAttributeAdapter::table(const Record& record) const { std::vector wrap; wrap.push_back(record.get().mData); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(wrap); } QVariant RaceAttributeAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { ESM::Race race = record.get(); if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length) throw std::runtime_error ("index out of range"); switch (subColIndex) { case 0: return subRowIndex; case 1: return race.mData.mAttributeValues[subRowIndex].mMale; case 2: return race.mData.mAttributeValues[subRowIndex].mFemale; default: throw std::runtime_error("Race Attribute subcolumn index out of range"); } } void RaceAttributeAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { ESM::Race race = record.get(); if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length) throw std::runtime_error ("index out of range"); switch (subColIndex) { case 0: return; // throw an exception here? case 1: race.mData.mAttributeValues[subRowIndex].mMale = value.toInt(); break; case 2: race.mData.mAttributeValues[subRowIndex].mFemale = value.toInt(); break; default: throw std::runtime_error("Race Attribute subcolumn index out of range"); } record.setModified (race); } int RaceAttributeAdapter::getColumnsCount(const Record& record) const { return 3; // attrib, male, female } int RaceAttributeAdapter::getRowsCount(const Record& record) const { return ESM::Attribute::Length; // there are 8 attributes } RaceSkillsBonusAdapter::RaceSkillsBonusAdapter () {} void RaceSkillsBonusAdapter::addRow(Record& record, int position) const { // Do nothing, this table cannot be changed by the user } void RaceSkillsBonusAdapter::removeRow(Record& record, int rowToRemove) const { // Do nothing, this table cannot be changed by the user } void RaceSkillsBonusAdapter::setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { ESM::Race race = record.get(); race.mData = static_cast >&>(nestedTable).mNestedTable.at(0); record.setModified (race); } NestedTableWrapperBase* RaceSkillsBonusAdapter::table(const Record& record) const { std::vector wrap; wrap.push_back(record.get().mData); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(wrap); } QVariant RaceSkillsBonusAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { ESM::Race race = record.get(); if (subRowIndex < 0 || subRowIndex >= static_cast(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0]))) throw std::runtime_error ("index out of range"); switch (subColIndex) { case 0: return race.mData.mBonus[subRowIndex].mSkill; // can be -1 case 1: return race.mData.mBonus[subRowIndex].mBonus; default: throw std::runtime_error("Race skill bonus subcolumn index out of range"); } } void RaceSkillsBonusAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { ESM::Race race = record.get(); if (subRowIndex < 0 || subRowIndex >= static_cast(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0]))) throw std::runtime_error ("index out of range"); switch (subColIndex) { case 0: race.mData.mBonus[subRowIndex].mSkill = value.toInt(); break; // can be -1 case 1: race.mData.mBonus[subRowIndex].mBonus = value.toInt(); break; default: throw std::runtime_error("Race skill bonus subcolumn index out of range"); } record.setModified (race); } int RaceSkillsBonusAdapter::getColumnsCount(const Record& record) const { return 2; // skill, bonus } int RaceSkillsBonusAdapter::getRowsCount(const Record& record) const { // there are 7 skill bonuses return static_cast(sizeof(record.get().mData.mBonus)/sizeof(record.get().mData.mBonus[0])); } CellListAdapter::CellListAdapter () {} void CellListAdapter::addRow(Record& record, int position) const { throw std::logic_error ("cannot add a row to a fixed table"); } void CellListAdapter::removeRow(Record& record, int rowToRemove) const { throw std::logic_error ("cannot remove a row to a fixed table"); } void CellListAdapter::setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { throw std::logic_error ("table operation not supported"); } NestedTableWrapperBase* CellListAdapter::table(const Record& record) const { throw std::logic_error ("table operation not supported"); } QVariant CellListAdapter::getData(const Record& record, int subRowIndex, int subColIndex) const { CSMWorld::Cell cell = record.get(); bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0; bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0; bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0; switch (subColIndex) { case 0: return isInterior; case 1: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mAmbient : QVariant(QVariant::UserType); case 2: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mSunlight : QVariant(QVariant::UserType); case 3: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFog : QVariant(QVariant::UserType); case 4: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFogDensity : QVariant(QVariant::UserType); case 5: { if (isInterior && !behaveLikeExterior && interiorWater) return cell.mWater; else return QVariant(QVariant::UserType); } case 6: return isInterior ? QVariant(QVariant::UserType) : cell.mMapColor; // TODO: how to select? //case 7: return isInterior ? //behaveLikeExterior : QVariant(QVariant::UserType); default: throw std::runtime_error("Cell subcolumn index out of range"); } } void CellListAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { CSMWorld::Cell cell = record.get(); bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0; bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0; bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0; switch (subColIndex) { case 0: { if (value.toBool()) cell.mData.mFlags |= ESM::Cell::Interior; else cell.mData.mFlags &= ~ESM::Cell::Interior; break; } case 1: { if (isInterior && !behaveLikeExterior) cell.mAmbi.mAmbient = static_cast(value.toInt()); else return; // return without saving break; } case 2: { if (isInterior && !behaveLikeExterior) cell.mAmbi.mSunlight = static_cast(value.toInt()); else return; // return without saving break; } case 3: { if (isInterior && !behaveLikeExterior) cell.mAmbi.mFog = static_cast(value.toInt()); else return; // return without saving break; } case 4: { if (isInterior && !behaveLikeExterior) cell.mAmbi.mFogDensity = value.toFloat(); else return; // return without saving break; } case 5: { if (isInterior && !behaveLikeExterior && interiorWater) cell.mWater = value.toFloat(); else return; // return without saving break; } case 6: { if (!isInterior) cell.mMapColor = value.toInt(); else return; // return without saving break; } #if 0 // redundant since this flag is shown in the main table as "Interior Sky" // keep here for documenting the logic based on vanilla case 7: { if (isInterior) { if (value.toBool()) cell.mData.mFlags |= ESM::Cell::QuasiEx; else cell.mData.mFlags &= ~ESM::Cell::QuasiEx; } else return; // return without saving break; } #endif default: throw std::runtime_error("Cell subcolumn index out of range"); } record.setModified (cell); } int CellListAdapter::getColumnsCount(const Record& record) const { return 7; } int CellListAdapter::getRowsCount(const Record& record) const { return 1; // fixed at size 1 } } openmw-openmw-0.38.0/apps/opencs/model/world/nestedcoladapterimp.hpp000066400000000000000000000454471264522266000256560ustar00rootroot00000000000000#ifndef CSM_WOLRD_NESTEDCOLADAPTERIMP_H #define CSM_WOLRD_NESTEDCOLADAPTERIMP_H #include #include #include #include // for converting magic effect id to string & back #include // for converting skill names #include // for converting attributes #include #include "nestedcolumnadapter.hpp" #include "nestedtablewrapper.hpp" #include "cell.hpp" namespace ESM { struct Faction; struct Region; } namespace CSMWorld { struct Pathgrid; struct Info; struct PathgridPointsWrap : public NestedTableWrapperBase { ESM::Pathgrid mRecord; PathgridPointsWrap(ESM::Pathgrid pathgrid) : mRecord(pathgrid) {} virtual ~PathgridPointsWrap() {} virtual int size() const { return mRecord.mPoints.size(); // used in IdTree::setNestedTable() } }; class PathgridPointListAdapter : public NestedColumnAdapter { public: PathgridPointListAdapter (); virtual void addRow(Record& record, int position) const; virtual void removeRow(Record& record, int rowToRemove) const; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* table(const Record& record) const; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getColumnsCount(const Record& record) const; virtual int getRowsCount(const Record& record) const; }; class PathgridEdgeListAdapter : public NestedColumnAdapter { public: PathgridEdgeListAdapter (); virtual void addRow(Record& record, int position) const; virtual void removeRow(Record& record, int rowToRemove) const; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* table(const Record& record) const; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getColumnsCount(const Record& record) const; virtual int getRowsCount(const Record& record) const; }; class FactionReactionsAdapter : public NestedColumnAdapter { public: FactionReactionsAdapter (); virtual void addRow(Record& record, int position) const; virtual void removeRow(Record& record, int rowToRemove) const; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* table(const Record& record) const; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getColumnsCount(const Record& record) const; virtual int getRowsCount(const Record& record) const; }; class RegionSoundListAdapter : public NestedColumnAdapter { public: RegionSoundListAdapter (); virtual void addRow(Record& record, int position) const; virtual void removeRow(Record& record, int rowToRemove) const; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* table(const Record& record) const; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getColumnsCount(const Record& record) const; virtual int getRowsCount(const Record& record) const; }; template class SpellListAdapter : public NestedColumnAdapter { public: SpellListAdapter () {} virtual void addRow(Record& record, int position) const { ESXRecordT raceOrBthSgn = record.get(); std::vector& spells = raceOrBthSgn.mPowers.mList; // blank row std::string spell = ""; spells.insert(spells.begin()+position, spell); record.setModified (raceOrBthSgn); } virtual void removeRow(Record& record, int rowToRemove) const { ESXRecordT raceOrBthSgn = record.get(); std::vector& spells = raceOrBthSgn.mPowers.mList; if (rowToRemove < 0 || rowToRemove >= static_cast (spells.size())) throw std::runtime_error ("index out of range"); spells.erase(spells.begin()+rowToRemove); record.setModified (raceOrBthSgn); } virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { ESXRecordT raceOrBthSgn = record.get(); raceOrBthSgn.mPowers.mList = static_cast >&>(nestedTable).mNestedTable; record.setModified (raceOrBthSgn); } virtual NestedTableWrapperBase* table(const Record& record) const { // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mPowers.mList); } virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const { ESXRecordT raceOrBthSgn = record.get(); std::vector& spells = raceOrBthSgn.mPowers.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (spells.size())) throw std::runtime_error ("index out of range"); std::string spell = spells[subRowIndex]; switch (subColIndex) { case 0: return QString(spell.c_str()); default: throw std::runtime_error("Spells subcolumn index out of range"); } } virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { ESXRecordT raceOrBthSgn = record.get(); std::vector& spells = raceOrBthSgn.mPowers.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (spells.size())) throw std::runtime_error ("index out of range"); std::string spell = spells[subRowIndex]; switch (subColIndex) { case 0: spell = value.toString().toUtf8().constData(); break; default: throw std::runtime_error("Spells subcolumn index out of range"); } raceOrBthSgn.mPowers.mList[subRowIndex] = spell; record.setModified (raceOrBthSgn); } virtual int getColumnsCount(const Record& record) const { return 1; } virtual int getRowsCount(const Record& record) const { return static_cast(record.get().mPowers.mList.size()); } }; template class EffectsListAdapter : public NestedColumnAdapter { public: EffectsListAdapter () {} virtual void addRow(Record& record, int position) const { ESXRecordT magic = record.get(); std::vector& effectsList = magic.mEffects.mList; // blank row ESM::ENAMstruct effect; effect.mEffectID = 0; effect.mSkill = -1; effect.mAttribute = -1; effect.mRange = 0; effect.mArea = 0; effect.mDuration = 0; effect.mMagnMin = 0; effect.mMagnMax = 0; effectsList.insert(effectsList.begin()+position, effect); record.setModified (magic); } virtual void removeRow(Record& record, int rowToRemove) const { ESXRecordT magic = record.get(); std::vector& effectsList = magic.mEffects.mList; if (rowToRemove < 0 || rowToRemove >= static_cast (effectsList.size())) throw std::runtime_error ("index out of range"); effectsList.erase(effectsList.begin()+rowToRemove); record.setModified (magic); } virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const { ESXRecordT magic = record.get(); magic.mEffects.mList = static_cast >&>(nestedTable).mNestedTable; record.setModified (magic); } virtual NestedTableWrapperBase* table(const Record& record) const { // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mEffects.mList); } virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const { ESXRecordT magic = record.get(); std::vector& effectsList = magic.mEffects.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (effectsList.size())) throw std::runtime_error ("index out of range"); ESM::ENAMstruct effect = effectsList[subRowIndex]; switch (subColIndex) { case 0: { if (effect.mEffectID >=0 && effect.mEffectID < ESM::MagicEffect::Length) return effect.mEffectID; else throw std::runtime_error("Magic effects ID unexpected value"); } case 1: { switch (effect.mEffectID) { case ESM::MagicEffect::DrainSkill: case ESM::MagicEffect::DamageSkill: case ESM::MagicEffect::RestoreSkill: case ESM::MagicEffect::FortifySkill: case ESM::MagicEffect::AbsorbSkill: return effect.mSkill; default: return QVariant(); } } case 2: { switch (effect.mEffectID) { case ESM::MagicEffect::DrainAttribute: case ESM::MagicEffect::DamageAttribute: case ESM::MagicEffect::RestoreAttribute: case ESM::MagicEffect::FortifyAttribute: case ESM::MagicEffect::AbsorbAttribute: return effect.mAttribute; default: return QVariant(); } } case 3: { if (effect.mRange >=0 && effect.mRange <=2) return effect.mRange; else throw std::runtime_error("Magic effects range unexpected value"); } case 4: return effect.mArea; case 5: return effect.mDuration; case 6: return effect.mMagnMin; case 7: return effect.mMagnMax; default: throw std::runtime_error("Magic Effects subcolumn index out of range"); } } virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { ESXRecordT magic = record.get(); std::vector& effectsList = magic.mEffects.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (effectsList.size())) throw std::runtime_error ("index out of range"); ESM::ENAMstruct effect = effectsList[subRowIndex]; switch (subColIndex) { case 0: { effect.mEffectID = static_cast(value.toInt()); break; } case 1: { effect.mSkill = static_cast(value.toInt()); break; } case 2: { effect.mAttribute = static_cast(value.toInt()); break; } case 3: { effect.mRange = value.toInt(); break; } case 4: effect.mArea = value.toInt(); break; case 5: effect.mDuration = value.toInt(); break; case 6: effect.mMagnMin = value.toInt(); break; case 7: effect.mMagnMax = value.toInt(); break; default: throw std::runtime_error("Magic Effects subcolumn index out of range"); } magic.mEffects.mList[subRowIndex] = effect; record.setModified (magic); } virtual int getColumnsCount(const Record& record) const { return 8; } virtual int getRowsCount(const Record& record) const { return static_cast(record.get().mEffects.mList.size()); } }; class InfoListAdapter : public NestedColumnAdapter { public: InfoListAdapter (); virtual void addRow(Record& record, int position) const; virtual void removeRow(Record& record, int rowToRemove) const; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* table(const Record& record) const; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getColumnsCount(const Record& record) const; virtual int getRowsCount(const Record& record) const; }; class InfoConditionAdapter : public NestedColumnAdapter { public: InfoConditionAdapter (); virtual void addRow(Record& record, int position) const; virtual void removeRow(Record& record, int rowToRemove) const; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* table(const Record& record) const; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getColumnsCount(const Record& record) const; virtual int getRowsCount(const Record& record) const; }; class RaceAttributeAdapter : public NestedColumnAdapter { public: RaceAttributeAdapter (); virtual void addRow(Record& record, int position) const; virtual void removeRow(Record& record, int rowToRemove) const; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* table(const Record& record) const; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getColumnsCount(const Record& record) const; virtual int getRowsCount(const Record& record) const; }; class RaceSkillsBonusAdapter : public NestedColumnAdapter { public: RaceSkillsBonusAdapter (); virtual void addRow(Record& record, int position) const; virtual void removeRow(Record& record, int rowToRemove) const; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* table(const Record& record) const; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getColumnsCount(const Record& record) const; virtual int getRowsCount(const Record& record) const; }; class CellListAdapter : public NestedColumnAdapter { public: CellListAdapter (); virtual void addRow(Record& record, int position) const; virtual void removeRow(Record& record, int rowToRemove) const; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* table(const Record& record) const; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getColumnsCount(const Record& record) const; virtual int getRowsCount(const Record& record) const; }; } #endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H openmw-openmw-0.38.0/apps/opencs/model/world/nestedcollection.cpp000066400000000000000000000021061264522266000251410ustar00rootroot00000000000000#include "nestedcollection.hpp" CSMWorld::NestedCollection::NestedCollection() {} CSMWorld::NestedCollection::~NestedCollection() {} int CSMWorld::NestedCollection::getNestedRowsCount(int row, int column) const { return 0; } int CSMWorld::NestedCollection::getNestedColumnsCount(int row, int column) const { return 0; } int CSMWorld::NestedCollection::searchNestedColumnIndex(int parentColumn, Columns::ColumnId id) { // Assumed that the parentColumn is always a valid index const NestableColumn *parent = getNestableColumn(parentColumn); int nestedColumnCount = getNestedColumnsCount(0, parentColumn); for (int i = 0; i < nestedColumnCount; ++i) { if (parent->nestedColumn(i).mColumnId == id) { return i; } } return -1; } int CSMWorld::NestedCollection::findNestedColumnIndex(int parentColumn, Columns::ColumnId id) { int index = searchNestedColumnIndex(parentColumn, id); if (index == -1) { throw std::logic_error("CSMWorld::NestedCollection: No such nested column"); } return index; } openmw-openmw-0.38.0/apps/opencs/model/world/nestedcollection.hpp000066400000000000000000000026741264522266000251600ustar00rootroot00000000000000#ifndef CSM_WOLRD_NESTEDCOLLECTION_H #define CSM_WOLRD_NESTEDCOLLECTION_H #include "columns.hpp" class QVariant; namespace CSMWorld { class NestableColumn; struct NestedTableWrapperBase; class NestedCollection { public: NestedCollection(); virtual ~NestedCollection(); virtual void addNestedRow(int row, int col, int position) = 0; virtual void removeNestedRows(int row, int column, int subRow) = 0; virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const = 0; virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) = 0; virtual NestedTableWrapperBase* nestedTable(int row, int column) const = 0; virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable) = 0; virtual int getNestedRowsCount(int row, int column) const; virtual int getNestedColumnsCount(int row, int column) const; virtual NestableColumn *getNestableColumn(int column) = 0; virtual int searchNestedColumnIndex(int parentColumn, Columns::ColumnId id); ///< \return the column index or -1 if the requested column wasn't found. virtual int findNestedColumnIndex(int parentColumn, Columns::ColumnId id); ///< \return the column index or throws an exception if the requested column wasn't found. }; } #endif // CSM_WOLRD_NESTEDCOLLECTION_H openmw-openmw-0.38.0/apps/opencs/model/world/nestedcolumnadapter.hpp000066400000000000000000000022541264522266000256550ustar00rootroot00000000000000#ifndef CSM_WOLRD_NESTEDCOLUMNADAPTER_H #define CSM_WOLRD_NESTEDCOLUMNADAPTER_H class QVariant; namespace CSMWorld { struct NestedTableWrapperBase; template struct Record; template class NestedColumnAdapter { public: NestedColumnAdapter() {} virtual ~NestedColumnAdapter() {} virtual void addRow(Record& record, int position) const = 0; virtual void removeRow(Record& record, int rowToRemove) const = 0; virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const = 0; virtual NestedTableWrapperBase* table(const Record& record) const = 0; virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const = 0; virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const = 0; virtual int getColumnsCount(const Record& record) const = 0; virtual int getRowsCount(const Record& record) const = 0; }; } #endif // CSM_WOLRD_NESTEDCOLUMNADAPTER_H openmw-openmw-0.38.0/apps/opencs/model/world/nestedidcollection.hpp000066400000000000000000000160561264522266000254740ustar00rootroot00000000000000#ifndef CSM_WOLRD_NESTEDIDCOLLECTION_H #define CSM_WOLRD_NESTEDIDCOLLECTION_H #include #include #include "nestedcollection.hpp" #include "nestedcoladapterimp.hpp" namespace ESM { class ESMReader; } namespace CSMWorld { struct NestedTableWrapperBase; struct Cell; template class IdCollection; template > class NestedIdCollection : public IdCollection, public NestedCollection { std::map* > mAdapters; const NestedColumnAdapter& getAdapter(const ColumnBase &column) const; public: NestedIdCollection (); ~NestedIdCollection(); virtual void addNestedRow(int row, int column, int position); virtual void removeNestedRows(int row, int column, int subRow); virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const; virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn); virtual NestedTableWrapperBase* nestedTable(int row, int column) const; virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable); virtual int getNestedRowsCount(int row, int column) const; virtual int getNestedColumnsCount(int row, int column) const; // this method is inherited from NestedCollection, not from Collection virtual NestableColumn *getNestableColumn(int column); void addAdapter(std::pair* > adapter); }; template NestedIdCollection::NestedIdCollection () {} template NestedIdCollection::~NestedIdCollection() { for (typename std::map* >::iterator iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter) { delete (*iter).second; } } template void NestedIdCollection::addAdapter(std::pair* > adapter) { mAdapters.insert(adapter); } template const NestedColumnAdapter& NestedIdCollection::getAdapter(const ColumnBase &column) const { typename std::map* >::const_iterator iter = mAdapters.find (&column); if (iter==mAdapters.end()) throw std::logic_error("No such column in the nestedidadapter"); return *iter->second; } template void NestedIdCollection::addNestedRow(int row, int column, int position) { Record record; record.assign(Collection::getRecord(row)); getAdapter(Collection::getColumn(column)).addRow(record, position); Collection::setRecord(row, record); } template void NestedIdCollection::removeNestedRows(int row, int column, int subRow) { Record record; record.assign(Collection::getRecord(row)); getAdapter(Collection::getColumn(column)).removeRow(record, subRow); Collection::setRecord(row, record); } template QVariant NestedIdCollection::getNestedData (int row, int column, int subRow, int subColumn) const { return getAdapter(Collection::getColumn(column)).getData( Collection::getRecord(row), subRow, subColumn); } template void NestedIdCollection::setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) { Record record; record.assign(Collection::getRecord(row)); getAdapter(Collection::getColumn(column)).setData( record, data, subRow, subColumn); Collection::setRecord(row, record); } template CSMWorld::NestedTableWrapperBase* NestedIdCollection::nestedTable(int row, int column) const { return getAdapter(Collection::getColumn(column)).table( Collection::getRecord(row)); } template void NestedIdCollection::setNestedTable(int row, int column, const CSMWorld::NestedTableWrapperBase& nestedTable) { Record record; record.assign(Collection::getRecord(row)); getAdapter(Collection::getColumn(column)).setTable( record, nestedTable); Collection::setRecord(row, record); } template int NestedIdCollection::getNestedRowsCount(int row, int column) const { return getAdapter(Collection::getColumn(column)).getRowsCount( Collection::getRecord(row)); } template int NestedIdCollection::getNestedColumnsCount(int row, int column) const { const ColumnBase &nestedColumn = Collection::getColumn(column); int numRecords = Collection::getSize(); if (row >= 0 && row < numRecords) { const Record& record = Collection::getRecord(row); return getAdapter(nestedColumn).getColumnsCount(record); } else { // If the row is invalid (or there no records), retrieve the column count using a blank record const Record record; return getAdapter(nestedColumn).getColumnsCount(record); } } template CSMWorld::NestableColumn *NestedIdCollection::getNestableColumn(int column) { return Collection::getNestableColumn(column); } } #endif // CSM_WOLRD_NESTEDIDCOLLECTION_H openmw-openmw-0.38.0/apps/opencs/model/world/nestedinfocollection.cpp000066400000000000000000000074711264522266000260270ustar00rootroot00000000000000#include "nestedinfocollection.hpp" #include "nestedcoladapterimp.hpp" namespace CSMWorld { NestedInfoCollection::NestedInfoCollection () {} NestedInfoCollection::~NestedInfoCollection() { for (std::map* >::iterator iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter) { delete (*iter).second; } } void NestedInfoCollection::addAdapter(std::pair* > adapter) { mAdapters.insert(adapter); } const NestedColumnAdapter& NestedInfoCollection::getAdapter(const ColumnBase &column) const { std::map* >::const_iterator iter = mAdapters.find (&column); if (iter==mAdapters.end()) throw std::logic_error("No such column in the nestedidadapter"); return *iter->second; } void NestedInfoCollection::addNestedRow(int row, int column, int position) { Record record; record.assign(Collection >::getRecord(row)); getAdapter(Collection >::getColumn(column)).addRow(record, position); Collection >::setRecord(row, record); } void NestedInfoCollection::removeNestedRows(int row, int column, int subRow) { Record record; record.assign(Collection >::getRecord(row)); getAdapter(Collection >::getColumn(column)).removeRow(record, subRow); Collection >::setRecord(row, record); } QVariant NestedInfoCollection::getNestedData (int row, int column, int subRow, int subColumn) const { return getAdapter(Collection >::getColumn(column)).getData( Collection >::getRecord(row), subRow, subColumn); } void NestedInfoCollection::setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) { Record record; record.assign(Collection >::getRecord(row)); getAdapter(Collection >::getColumn(column)).setData( record, data, subRow, subColumn); Collection >::setRecord(row, record); } CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row, int column) const { return getAdapter(Collection >::getColumn(column)).table( Collection >::getRecord(row)); } void NestedInfoCollection::setNestedTable(int row, int column, const CSMWorld::NestedTableWrapperBase& nestedTable) { Record record; record.assign(Collection >::getRecord(row)); getAdapter(Collection >::getColumn(column)).setTable( record, nestedTable); Collection >::setRecord(row, record); } int NestedInfoCollection::getNestedRowsCount(int row, int column) const { return getAdapter(Collection >::getColumn(column)).getRowsCount( Collection >::getRecord(row)); } int NestedInfoCollection::getNestedColumnsCount(int row, int column) const { return getAdapter(Collection >::getColumn(column)).getColumnsCount( Collection >::getRecord(row)); } CSMWorld::NestableColumn *NestedInfoCollection::getNestableColumn(int column) { return Collection >::getNestableColumn(column); } } openmw-openmw-0.38.0/apps/opencs/model/world/nestedinfocollection.hpp000066400000000000000000000031731264522266000260270ustar00rootroot00000000000000#ifndef CSM_WOLRD_NESTEDINFOCOLLECTION_H #define CSM_WOLRD_NESTEDINFOCOLLECTION_H #include #include "infocollection.hpp" #include "nestedcollection.hpp" namespace CSMWorld { struct NestedTableWrapperBase; template class NestedColumnAdapter; class NestedInfoCollection : public InfoCollection, public NestedCollection { std::map* > mAdapters; const NestedColumnAdapter& getAdapter(const ColumnBase &column) const; public: NestedInfoCollection (); ~NestedInfoCollection(); virtual void addNestedRow(int row, int column, int position); virtual void removeNestedRows(int row, int column, int subRow); virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const; virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn); virtual NestedTableWrapperBase* nestedTable(int row, int column) const; virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable); virtual int getNestedRowsCount(int row, int column) const; virtual int getNestedColumnsCount(int row, int column) const; // this method is inherited from NestedCollection, not from Collection > virtual NestableColumn *getNestableColumn(int column); void addAdapter(std::pair* > adapter); }; } #endif // CSM_WOLRD_NESTEDINFOCOLLECTION_H openmw-openmw-0.38.0/apps/opencs/model/world/nestedtableproxymodel.cpp000066400000000000000000000144441264522266000262300ustar00rootroot00000000000000#include "nestedtableproxymodel.hpp" #include #include "idtree.hpp" CSMWorld::NestedTableProxyModel::NestedTableProxyModel(const QModelIndex& parent, ColumnBase::Display columnId, CSMWorld::IdTree* parentModel) : mParentColumn(parent.column()), mMainModel(parentModel) { const int parentRow = parent.row(); mId = std::string(parentModel->index(parentRow, 0).data().toString().toUtf8()); QAbstractProxyModel::setSourceModel(parentModel); connect(mMainModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), this, SLOT(forwardRowsAboutToInserted(const QModelIndex &, int, int))); connect(mMainModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(forwardRowsInserted(const QModelIndex &, int, int))); connect(mMainModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), this, SLOT(forwardRowsAboutToRemoved(const QModelIndex &, int, int))); connect(mMainModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(forwardRowsRemoved(const QModelIndex &, int, int))); connect(mMainModel, SIGNAL(resetStart(const QString&)), this, SLOT(forwardResetStart(const QString&))); connect(mMainModel, SIGNAL(resetEnd(const QString&)), this, SLOT(forwardResetEnd(const QString&))); connect(mMainModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(forwardDataChanged(const QModelIndex &, const QModelIndex &))); } QModelIndex CSMWorld::NestedTableProxyModel::mapFromSource(const QModelIndex& sourceIndex) const { const QModelIndex& testedParent = mMainModel->parent(sourceIndex); const QModelIndex& parent = mMainModel->getNestedModelIndex (mId, mParentColumn); if (testedParent == parent) { return createIndex(sourceIndex.row(), sourceIndex.column()); } else { return QModelIndex(); } } QModelIndex CSMWorld::NestedTableProxyModel::mapToSource(const QModelIndex& proxyIndex) const { const QModelIndex& parent = mMainModel->getNestedModelIndex (mId, mParentColumn); return mMainModel->index(proxyIndex.row(), proxyIndex.column(), parent); } int CSMWorld::NestedTableProxyModel::rowCount(const QModelIndex& index) const { assert (!index.isValid()); return mMainModel->rowCount(mMainModel->getModelIndex(mId, mParentColumn)); } int CSMWorld::NestedTableProxyModel::columnCount(const QModelIndex& parent) const { assert (!parent.isValid()); return mMainModel->columnCount(mMainModel->getModelIndex(mId, mParentColumn)); } QModelIndex CSMWorld::NestedTableProxyModel::index(int row, int column, const QModelIndex& parent) const { assert (!parent.isValid()); int numRows = rowCount(parent); int numColumns = columnCount(parent); if (row < 0 || row >= numRows || column < 0 || column >= numColumns) return QModelIndex(); return createIndex(row, column); } QModelIndex CSMWorld::NestedTableProxyModel::parent(const QModelIndex& index) const { return QModelIndex(); } QVariant CSMWorld::NestedTableProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { return mMainModel->nestedHeaderData(mParentColumn, section, orientation, role); } QVariant CSMWorld::NestedTableProxyModel::data(const QModelIndex& index, int role) const { return mMainModel->data(mapToSource(index), role); } // NOTE: Due to mapToSouce(index) the dataChanged() signal resulting from setData() will have the // source model's index values. The indicies need to be converted to the proxy space values. // See forwardDataChanged() bool CSMWorld::NestedTableProxyModel::setData (const QModelIndex & index, const QVariant & value, int role) { return mMainModel->setData(mapToSource(index), value, role); } Qt::ItemFlags CSMWorld::NestedTableProxyModel::flags(const QModelIndex& index) const { return mMainModel->flags(mapToSource(index)); } std::string CSMWorld::NestedTableProxyModel::getParentId() const { return mId; } int CSMWorld::NestedTableProxyModel::getParentColumn() const { return mParentColumn; } CSMWorld::IdTree* CSMWorld::NestedTableProxyModel::model() const { return mMainModel; } void CSMWorld::NestedTableProxyModel::forwardRowsAboutToInserted(const QModelIndex& parent, int first, int last) { if (indexIsParent(parent)) { beginInsertRows(QModelIndex(), first, last); } } void CSMWorld::NestedTableProxyModel::forwardRowsInserted(const QModelIndex& parent, int first, int last) { if (indexIsParent(parent)) { endInsertRows(); } } bool CSMWorld::NestedTableProxyModel::indexIsParent(const QModelIndex& index) { return (index.isValid() && index.column() == mParentColumn && mMainModel->data(mMainModel->index(index.row(), 0)).toString().toUtf8().constData() == mId); } void CSMWorld::NestedTableProxyModel::forwardRowsAboutToRemoved(const QModelIndex& parent, int first, int last) { if (indexIsParent(parent)) { beginRemoveRows(QModelIndex(), first, last); } } void CSMWorld::NestedTableProxyModel::forwardRowsRemoved(const QModelIndex& parent, int first, int last) { if (indexIsParent(parent)) { endRemoveRows(); } } void CSMWorld::NestedTableProxyModel::forwardResetStart(const QString& id) { if (id.toUtf8() == mId.c_str()) beginResetModel(); } void CSMWorld::NestedTableProxyModel::forwardResetEnd(const QString& id) { if (id.toUtf8() == mId.c_str()) endResetModel(); } void CSMWorld::NestedTableProxyModel::forwardDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { const QModelIndex& parent = mMainModel->getNestedModelIndex (mId, mParentColumn); if (topLeft.column() <= parent.column() && bottomRight.column() >= parent.column()) { emit dataChanged(index(0,0), index(mMainModel->rowCount(parent)-1, mMainModel->columnCount(parent)-1)); } else if (topLeft.parent() == parent && bottomRight.parent() == parent) { emit dataChanged(index(topLeft.row(), topLeft.column()), index(bottomRight.row(), bottomRight.column())); } } openmw-openmw-0.38.0/apps/opencs/model/world/nestedtableproxymodel.hpp000066400000000000000000000047441264522266000262370ustar00rootroot00000000000000#ifndef CSM_WOLRD_NESTEDTABLEPROXYMODEL_H #define CSM_WOLRD_NESTEDTABLEPROXYMODEL_H #include #include #include "universalid.hpp" #include "columns.hpp" #include "columnbase.hpp" /*! \brief * Proxy model used to connect view in the dialogue into the nested columns of the main model. */ namespace CSMWorld { class CollectionBase; struct RecordBase; class IdTree; class NestedTableProxyModel : public QAbstractProxyModel { Q_OBJECT const int mParentColumn; IdTree* mMainModel; std::string mId; public: NestedTableProxyModel(const QModelIndex& parent, ColumnBase::Display displayType, IdTree* parentModel); //parent is the parent of columns to work with. Columnid provides information about the column std::string getParentId() const; int getParentColumn() const; CSMWorld::IdTree* model() const; virtual QModelIndex mapFromSource(const QModelIndex& sourceIndex) const; virtual QModelIndex mapToSource(const QModelIndex& proxyIndex) const; virtual int rowCount(const QModelIndex& parent) const; virtual int columnCount(const QModelIndex& parent) const; virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex& index) const; virtual QVariant headerData (int section, Qt::Orientation orientation, int role) const; virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; virtual bool setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); virtual Qt::ItemFlags flags(const QModelIndex& index) const; private: void setupHeaderVectors(ColumnBase::Display columnId); bool indexIsParent(const QModelIndex& index); private slots: void forwardRowsAboutToInserted(const QModelIndex & parent, int first, int last); void forwardRowsInserted(const QModelIndex & parent, int first, int last); void forwardRowsAboutToRemoved(const QModelIndex & parent, int first, int last); void forwardRowsRemoved(const QModelIndex & parent, int first, int last); void forwardResetStart(const QString& id); void forwardResetEnd(const QString& id); void forwardDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/nestedtablewrapper.cpp000066400000000000000000000003501264522266000254750ustar00rootroot00000000000000#include "nestedtablewrapper.hpp" CSMWorld::NestedTableWrapperBase::NestedTableWrapperBase() {} CSMWorld::NestedTableWrapperBase::~NestedTableWrapperBase() {} int CSMWorld::NestedTableWrapperBase::size() const { return -5; } openmw-openmw-0.38.0/apps/opencs/model/world/nestedtablewrapper.hpp000066400000000000000000000013031264522266000255010ustar00rootroot00000000000000#ifndef CSM_WOLRD_NESTEDTABLEWRAPPER_H #define CSM_WOLRD_NESTEDTABLEWRAPPER_H namespace CSMWorld { struct NestedTableWrapperBase { virtual ~NestedTableWrapperBase(); virtual int size() const; NestedTableWrapperBase(); }; template struct NestedTableWrapper : public NestedTableWrapperBase { NestedTable mNestedTable; NestedTableWrapper(const NestedTable& nestedTable) : mNestedTable(nestedTable) {} virtual ~NestedTableWrapper() {} virtual int size() const { return mNestedTable.size(); //i hope that this will be enough } }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/pathgrid.cpp000066400000000000000000000013251264522266000234070ustar00rootroot00000000000000#include "cell.hpp" #include "idcollection.hpp" #include "pathgrid.hpp" #include void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection& cells) { load (esm, isDeleted); // correct ID if (!mId.empty() && mId[0]!='#' && cells.searchId (mId)==-1) { std::ostringstream stream; stream << "#" << mData.mX << " " << mData.mY; mId = stream.str(); } } void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted) { ESM::Pathgrid::load (esm, isDeleted); mId = mCell; if (mCell.empty()) { std::ostringstream stream; stream << "#" << mData.mX << " " << mData.mY; mId = stream.str(); } } openmw-openmw-0.38.0/apps/opencs/model/world/pathgrid.hpp000066400000000000000000000012611264522266000234130ustar00rootroot00000000000000#ifndef CSM_WOLRD_PATHGRID_H #define CSM_WOLRD_PATHGRID_H #include #include #include namespace CSMWorld { struct Cell; template class IdCollection; /// \brief Wrapper for Pathgrid record /// /// \attention The mData.mX and mData.mY fields of the ESM::Pathgrid struct are not used. /// Exterior cell coordinates are encoded in the pathgrid ID. struct Pathgrid : public ESM::Pathgrid { std::string mId; void load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection& cells); void load (ESM::ESMReader &esm, bool &isDeleted); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/record.cpp000066400000000000000000000005621264522266000230650ustar00rootroot00000000000000#include "record.hpp" CSMWorld::RecordBase::~RecordBase() {} bool CSMWorld::RecordBase::isDeleted() const { return mState==State_Deleted || mState==State_Erased; } bool CSMWorld::RecordBase::isErased() const { return mState==State_Erased; } bool CSMWorld::RecordBase::isModified() const { return mState==State_Modified || mState==State_ModifiedOnly; } openmw-openmw-0.38.0/apps/opencs/model/world/record.hpp000066400000000000000000000102131264522266000230640ustar00rootroot00000000000000#ifndef CSM_WOLRD_RECORD_H #define CSM_WOLRD_RECORD_H #include namespace CSMWorld { struct RecordBase { enum State { State_BaseOnly = 0, // defined in base only State_Modified = 1, // exists in base, but has been modified State_ModifiedOnly = 2, // newly created in modified State_Deleted = 3, // exists in base, but has been deleted State_Erased = 4 // does not exist at all (we mostly treat that the same way as deleted) }; State mState; virtual ~RecordBase(); virtual RecordBase *clone() const = 0; virtual RecordBase *modifiedCopy() const = 0; virtual void assign (const RecordBase& record) = 0; ///< Will throw an exception if the types don't match. bool isDeleted() const; bool isErased() const; bool isModified() const; }; template struct Record : public RecordBase { ESXRecordT mBase; ESXRecordT mModified; Record(); Record(State state, const ESXRecordT *base = 0, const ESXRecordT *modified = 0); virtual RecordBase *clone() const; virtual RecordBase *modifiedCopy() const; virtual void assign (const RecordBase& record); const ESXRecordT& get() const; ///< Throws an exception, if the record is deleted. ESXRecordT& get(); ///< Throws an exception, if the record is deleted. const ESXRecordT& getBase() const; ///< Throws an exception, if the record is deleted. Returns modified, if there is no base. void setModified (const ESXRecordT& modified); ///< Throws an exception, if the record is deleted. void merge(); ///< Merge modified into base. }; template Record::Record() : mBase(), mModified() { } template Record::Record(State state, const ESXRecordT *base, const ESXRecordT *modified) { if(base) mBase = *base; if(modified) mModified = *modified; this->mState = state; } template RecordBase *Record::modifiedCopy() const { return new Record (State_ModifiedOnly, 0, &(this->get())); } template RecordBase *Record::clone() const { return new Record (*this); } template void Record::assign (const RecordBase& record) { *this = dynamic_cast& > (record); } template const ESXRecordT& Record::get() const { if (mState==State_Erased) throw std::logic_error ("attempt to access a deleted record"); return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified; } template ESXRecordT& Record::get() { if (mState==State_Erased) throw std::logic_error ("attempt to access a deleted record"); return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified; } template const ESXRecordT& Record::getBase() const { if (mState==State_Erased) throw std::logic_error ("attempt to access a deleted record"); return mState==State_ModifiedOnly ? mModified : mBase; } template void Record::setModified (const ESXRecordT& modified) { if (mState==State_Erased) throw std::logic_error ("attempt to modify a deleted record"); mModified = modified; if (mState!=State_ModifiedOnly) mState = State_Modified; } template void Record::merge() { if (isModified()) { mBase = mModified; mState = State_BaseOnly; } else if (mState==State_Deleted) { mState = State_Erased; } } } #endif openmw-openmw-0.38.0/apps/opencs/model/world/ref.cpp000066400000000000000000000005111264522266000223550ustar00rootroot00000000000000#include "ref.hpp" #include CSMWorld::CellRef::CellRef() { mRefNum.mIndex = 0; mRefNum.mContentFile = 0; } std::pair CSMWorld::CellRef::getCellIndex() const { const int cellSize = 8192; return std::make_pair ( std::floor (mPos.pos[0]/cellSize), std::floor (mPos.pos[1]/cellSize)); } openmw-openmw-0.38.0/apps/opencs/model/world/ref.hpp000066400000000000000000000007151264522266000223700ustar00rootroot00000000000000#ifndef CSM_WOLRD_REF_H #define CSM_WOLRD_REF_H #include #include namespace CSMWorld { /// \brief Wrapper for CellRef sub record struct CellRef : public ESM::CellRef { std::string mId; std::string mCell; std::string mOriginalCell; CellRef(); /// Calculate cell index based on coordinates (x and y) std::pair getCellIndex() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/refcollection.cpp000066400000000000000000000113731264522266000244410ustar00rootroot00000000000000#include "refcollection.hpp" #include #include #include #include #include "ref.hpp" #include "cell.hpp" #include "universalid.hpp" #include "record.hpp" void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base, std::map& cache, CSMDoc::Messages& messages) { Record cell = mCells.getRecord (cellIndex); Cell& cell2 = base ? cell.mBase : cell.mModified; CellRef ref; ESM::MovedCellRef mref; bool isDeleted = false; // hack to initialise mindex while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref)) { // Keep mOriginalCell empty when in modified (as an indicator that the // original cell will always be equal the current cell). ref.mOriginalCell = base ? cell2.mId : ""; if (cell.get().isExterior()) { // ignoring moved references sub-record; instead calculate cell from coordinates std::pair index = ref.getCellIndex(); std::ostringstream stream; stream << "#" << index.first << " " << index.second; ref.mCell = stream.str(); if (!base && // don't try to update base records mref.mRefNum.mIndex != 0) // MVRF tag found { // there is a requirement for a placeholder where the original object was // // see the forum discussions here for more details: // https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30 ref.mOriginalCell = cell2.mId; // It is not always possibe to ignore moved references sub-record and // calculate from coordinates. Some mods may place the ref in positions // outside normal bounds, resulting in non sensical cell id's. This often // happens if the moved ref was deleted. // // Use the target cell from the MVRF tag but if different output an error // message if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) { std::cerr << "The Position of moved ref " << ref.mRefID << " does not match the target cell" << std::endl; std::cerr << "Position: #" << index.first << " " << index.second <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; std::ostringstream stream; stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; ref.mCell = stream.str(); // overwrite } } } else ref.mCell = cell2.mId; // ignore content file number std::map::iterator iter = cache.begin(); for (; iter != cache.end(); ++iter) { if (ref.mRefNum.mIndex == iter->first.mIndex) break; } if (isDeleted) { if (iter==cache.end()) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, mCells.getId (cellIndex)); messages.add (id, "Attempt to delete a non-existing reference"); continue; } int index = getIndex (iter->second); Record record = getRecord (index); if (base) { removeRows (index, 1); cache.erase (iter); } else { record.mState = RecordBase::State_Deleted; setRecord (index, record); } continue; } if (iter==cache.end()) { // new reference ref.mId = getNewId(); Record record; record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record.mBase : record.mModified) = ref; appendRecord (record); cache.insert (std::make_pair (ref.mRefNum, ref.mId)); } else { // old reference -> merge ref.mId = iter->second; int index = getIndex (ref.mId); Record record = getRecord (index); record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; (base ? record.mBase : record.mModified) = ref; setRecord (index, record); } } } std::string CSMWorld::RefCollection::getNewId() { std::ostringstream stream; stream << "ref#" << mNextId++; return stream.str(); } openmw-openmw-0.38.0/apps/opencs/model/world/refcollection.hpp000066400000000000000000000015751264522266000244510ustar00rootroot00000000000000#ifndef CSM_WOLRD_REFCOLLECTION_H #define CSM_WOLRD_REFCOLLECTION_H #include #include "../doc/stage.hpp" #include "collection.hpp" #include "ref.hpp" #include "record.hpp" namespace CSMWorld { struct Cell; class UniversalId; /// \brief References in cells class RefCollection : public Collection { Collection& mCells; int mNextId; public: // MSVC needs the constructor for a class inheriting a template to be defined in header RefCollection (Collection& cells) : mCells (cells), mNextId (0) {} void load (ESM::ESMReader& reader, int cellIndex, bool base, std::map& cache, CSMDoc::Messages& messages); ///< Load a sequence of references. std::string getNewId(); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/refidadapter.cpp000066400000000000000000000003621264522266000242370ustar00rootroot00000000000000#include "refidadapter.hpp" CSMWorld::RefIdAdapter::RefIdAdapter() {} CSMWorld::RefIdAdapter::~RefIdAdapter() {} CSMWorld::NestedRefIdAdapterBase::NestedRefIdAdapterBase() {} CSMWorld::NestedRefIdAdapterBase::~NestedRefIdAdapterBase() {} openmw-openmw-0.38.0/apps/opencs/model/world/refidadapter.hpp000066400000000000000000000057221264522266000242510ustar00rootroot00000000000000#ifndef CSM_WOLRD_REFIDADAPTER_H #define CSM_WOLRD_REFIDADAPTER_H #include #include /*! \brief * Adapters acts as indirection layer, abstracting details of the record types (in the wrappers) from the higher levels of model. * Please notice that nested adaptor uses helper classes for actually performing any actions. Different record types require different helpers (needs to be created in the subclass and then fetched via member function). * * Important point: don't forget to make sure that getData on the nestedColumn returns true (otherwise code will not treat the index pointing to the column as having childs! */ class QVariant; namespace CSMWorld { class RefIdColumn; class RefIdData; struct RecordBase; struct NestedTableWrapperBase; class HelperBase; class RefIdAdapter { // not implemented RefIdAdapter (const RefIdAdapter&); RefIdAdapter& operator= (const RefIdAdapter&); public: RefIdAdapter(); virtual ~RefIdAdapter(); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int idnex) const = 0; ///< If called on the nest column, should return QVariant(true). virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const = 0; ///< If the data type does not match an exception is thrown. virtual std::string getId (const RecordBase& record) const = 0; virtual void setId(RecordBase& record, const std::string& id) = 0; // used by RefIdCollection::cloneRecord() }; class NestedRefIdAdapterBase { public: NestedRefIdAdapterBase(); virtual ~NestedRefIdAdapterBase(); virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const = 0; virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const = 0; virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const = 0; virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const = 0; virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const = 0; virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const = 0; virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const = 0; virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const = 0; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/refidadapterimp.cpp000066400000000000000000001515001264522266000247460ustar00rootroot00000000000000#include "refidadapterimp.hpp" #include #include #include #include #include #include "nestedtablewrapper.hpp" CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns) : InventoryColumns (columns) {} CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const PotionColumns& columns, const RefIdColumn *autoCalc) : InventoryRefIdAdapter (UniversalId::Type_Potion, columns), mColumns(columns), mAutoCalc (autoCalc) {} QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); if (column==mAutoCalc) return record.get().mData.mAutoCalc!=0; // to show nested tables in dialogue subview, see IdTree::hasChildren() if (column==mColumns.mEffects) return QVariant::fromValue(ColumnBase::TableEdit_Full); return InventoryRefIdAdapter::getData (column, data, index); } void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); ESM::Potion potion = record.get(); if (column==mAutoCalc) potion.mData.mAutoCalc = value.toInt(); else { InventoryRefIdAdapter::setData (column, data, index, value); return; } record.setModified(potion); } CSMWorld::IngredientColumns::IngredientColumns (const InventoryColumns& columns) : InventoryColumns (columns) {} CSMWorld::IngredientRefIdAdapter::IngredientRefIdAdapter (const IngredientColumns& columns) : InventoryRefIdAdapter (UniversalId::Type_Ingredient, columns), mColumns(columns) {} QVariant CSMWorld::IngredientRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { if (column==mColumns.mEffects) return QVariant::fromValue(ColumnBase::TableEdit_FixedRows); return InventoryRefIdAdapter::getData (column, data, index); } void CSMWorld::IngredientRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { InventoryRefIdAdapter::setData (column, data, index, value); return; } CSMWorld::IngredEffectRefIdAdapter::IngredEffectRefIdAdapter() : mType(UniversalId::Type_Ingredient) {} CSMWorld::IngredEffectRefIdAdapter::~IngredEffectRefIdAdapter() {} void CSMWorld::IngredEffectRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::IngredEffectRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::IngredEffectRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESM::Ingredient ingredient = record.get(); ingredient.mData = static_cast >&>(nestedTable).mNestedTable.at(0); record.setModified (ingredient); } CSMWorld::NestedTableWrapperBase* CSMWorld::IngredEffectRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // return the whole struct std::vector wrap; wrap.push_back(record.get().mData); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(wrap); } QVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); if (subRowIndex < 0 || subRowIndex >= 4) throw std::runtime_error ("index out of range"); switch (subColIndex) { case 0: return record.get().mData.mEffectID[subRowIndex]; case 1: { switch (record.get().mData.mEffectID[subRowIndex]) { case ESM::MagicEffect::DrainSkill: case ESM::MagicEffect::DamageSkill: case ESM::MagicEffect::RestoreSkill: case ESM::MagicEffect::FortifySkill: case ESM::MagicEffect::AbsorbSkill: return record.get().mData.mSkills[subRowIndex]; default: return QVariant(); } } case 2: { switch (record.get().mData.mEffectID[subRowIndex]) { case ESM::MagicEffect::DrainAttribute: case ESM::MagicEffect::DamageAttribute: case ESM::MagicEffect::RestoreAttribute: case ESM::MagicEffect::FortifyAttribute: case ESM::MagicEffect::AbsorbAttribute: return record.get().mData.mAttributes[subRowIndex]; default: return QVariant(); } } default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void CSMWorld::IngredEffectRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); ESM::Ingredient ingredient = record.get(); if (subRowIndex < 0 || subRowIndex >= 4) throw std::runtime_error ("index out of range"); switch(subColIndex) { case 0: ingredient.mData.mEffectID[subRowIndex] = value.toInt(); break; case 1: ingredient.mData.mSkills[subRowIndex] = value.toInt(); break; case 2: ingredient.mData.mAttributes[subRowIndex] = value.toInt(); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified (ingredient); } int CSMWorld::IngredEffectRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 3; // effect, skill, attribute } int CSMWorld::IngredEffectRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { return 4; // up to 4 effects } CSMWorld::ApparatusRefIdAdapter::ApparatusRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *type, const RefIdColumn *quality) : InventoryRefIdAdapter (UniversalId::Type_Apparatus, columns), mType (type), mQuality (quality) {} QVariant CSMWorld::ApparatusRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); if (column==mType) return record.get().mData.mType; if (column==mQuality) return record.get().mData.mQuality; return InventoryRefIdAdapter::getData (column, data, index); } void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); ESM::Apparatus apparatus = record.get(); if (column==mType) apparatus.mData.mType = value.toInt(); else if (column==mQuality) apparatus.mData.mQuality = value.toFloat(); else { InventoryRefIdAdapter::setData (column, data, index, value); return; } record.setModified(apparatus); } CSMWorld::ArmorRefIdAdapter::ArmorRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type, const RefIdColumn *health, const RefIdColumn *armor, const RefIdColumn *partRef) : EnchantableRefIdAdapter (UniversalId::Type_Armor, columns), mType (type), mHealth (health), mArmor (armor), mPartRef(partRef) {} QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); if (column==mType) return record.get().mData.mType; if (column==mHealth) return record.get().mData.mHealth; if (column==mArmor) return record.get().mData.mArmor; if (column==mPartRef) return QVariant::fromValue(ColumnBase::TableEdit_Full); return EnchantableRefIdAdapter::getData (column, data, index); } void CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); ESM::Armor armor = record.get(); if (column==mType) armor.mData.mType = value.toInt(); else if (column==mHealth) armor.mData.mHealth = value.toInt(); else if (column==mArmor) armor.mData.mArmor = value.toInt(); else { EnchantableRefIdAdapter::setData (column, data, index, value); return; } record.setModified(armor); } CSMWorld::BookRefIdAdapter::BookRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *scroll, const RefIdColumn *skill) : EnchantableRefIdAdapter (UniversalId::Type_Book, columns), mScroll (scroll), mSkill (skill) {} QVariant CSMWorld::BookRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); if (column==mScroll) return record.get().mData.mIsScroll!=0; if (column==mSkill) return record.get().mData.mSkillID; return EnchantableRefIdAdapter::getData (column, data, index); } void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); ESM::Book book = record.get(); if (column==mScroll) book.mData.mIsScroll = value.toInt(); else if (column==mSkill) book.mData.mSkillID = value.toInt(); else { EnchantableRefIdAdapter::setData (column, data, index, value); return; } record.setModified(book); } CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type, const RefIdColumn *partRef) : EnchantableRefIdAdapter (UniversalId::Type_Clothing, columns), mType (type), mPartRef(partRef) {} QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); if (column==mType) return record.get().mData.mType; if (column==mPartRef) return QVariant::fromValue(ColumnBase::TableEdit_Full); return EnchantableRefIdAdapter::getData (column, data, index); } void CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); ESM::Clothing clothing = record.get(); if (column==mType) clothing.mData.mType = value.toInt(); else { EnchantableRefIdAdapter::setData (column, data, index, value); return; } record.setModified(clothing); } CSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns, const RefIdColumn *weight, const RefIdColumn *organic, const RefIdColumn *respawn, const RefIdColumn *content) : NameRefIdAdapter (UniversalId::Type_Container, columns), mWeight (weight), mOrganic (organic), mRespawn (respawn), mContent(content) {} QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); if (column==mWeight) return record.get().mWeight; if (column==mOrganic) return (record.get().mFlags & ESM::Container::Organic)!=0; if (column==mRespawn) return (record.get().mFlags & ESM::Container::Respawn)!=0; if (column==mContent) return QVariant::fromValue(ColumnBase::TableEdit_Full); return NameRefIdAdapter::getData (column, data, index); } void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); ESM::Container container = record.get(); if (column==mWeight) container.mWeight = value.toFloat(); else if (column==mOrganic) { if (value.toInt()) container.mFlags |= ESM::Container::Organic; else container.mFlags &= ~ESM::Container::Organic; } else if (column==mRespawn) { if (value.toInt()) container.mFlags |= ESM::Container::Respawn; else container.mFlags &= ~ESM::Container::Respawn; } else { NameRefIdAdapter::setData (column, data, index, value); return; } record.setModified(container); } CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), mType(NULL), mScale(NULL), mOriginal(NULL), mAttributes(NULL), mAttacks(NULL), mMisc(NULL) {} CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) : ActorRefIdAdapter (UniversalId::Type_Creature, columns), mColumns (columns) {} QVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); if (column==mColumns.mType) return record.get().mData.mType; if (column==mColumns.mScale) return record.get().mScale; if (column==mColumns.mOriginal) return QString::fromUtf8 (record.get().mOriginal.c_str()); if (column==mColumns.mAttributes) return QVariant::fromValue(ColumnBase::TableEdit_FixedRows); if (column==mColumns.mAttacks) return QVariant::fromValue(ColumnBase::TableEdit_FixedRows); if (column==mColumns.mMisc) return QVariant::fromValue(ColumnBase::TableEdit_Full); std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) return (record.get().mFlags & iter->second)!=0; return ActorRefIdAdapter::getData (column, data, index); } void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); ESM::Creature creature = record.get(); if (column==mColumns.mType) creature.mData.mType = value.toInt(); else if (column==mColumns.mScale) creature.mScale = value.toFloat(); else if (column==mColumns.mOriginal) creature.mOriginal = value.toString().toUtf8().constData(); else { std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) creature.mFlags |= iter->second; else creature.mFlags &= ~iter->second; } else { ActorRefIdAdapter::setData (column, data, index, value); return; } } record.setModified(creature); } CSMWorld::DoorRefIdAdapter::DoorRefIdAdapter (const NameColumns& columns, const RefIdColumn *openSound, const RefIdColumn *closeSound) : NameRefIdAdapter (UniversalId::Type_Door, columns), mOpenSound (openSound), mCloseSound (closeSound) {} QVariant CSMWorld::DoorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); if (column==mOpenSound) return QString::fromUtf8 (record.get().mOpenSound.c_str()); if (column==mCloseSound) return QString::fromUtf8 (record.get().mCloseSound.c_str()); return NameRefIdAdapter::getData (column, data, index); } void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); ESM::Door door = record.get(); if (column==mOpenSound) door.mOpenSound = value.toString().toUtf8().constData(); else if (column==mCloseSound) door.mCloseSound = value.toString().toUtf8().constData(); else { NameRefIdAdapter::setData (column, data, index, value); return; } record.setModified(door); } CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns) : InventoryColumns (columns) {} CSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns) : InventoryRefIdAdapter (UniversalId::Type_Light, columns), mColumns (columns) {} QVariant CSMWorld::LightRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); if (column==mColumns.mTime) return record.get().mData.mTime; if (column==mColumns.mRadius) return record.get().mData.mRadius; if (column==mColumns.mColor) return record.get().mData.mColor; if (column==mColumns.mSound) return QString::fromUtf8 (record.get().mSound.c_str()); std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) return (record.get().mData.mFlags & iter->second)!=0; return InventoryRefIdAdapter::getData (column, data, index); } void CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); ESM::Light light = record.get(); if (column==mColumns.mTime) light.mData.mTime = value.toInt(); else if (column==mColumns.mRadius) light.mData.mRadius = value.toInt(); else if (column==mColumns.mColor) light.mData.mColor = value.toInt(); else if (column==mColumns.mSound) light.mSound = value.toString().toUtf8().constData(); else { std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) light.mData.mFlags |= iter->second; else light.mData.mFlags &= ~iter->second; } else { InventoryRefIdAdapter::setData (column, data, index, value); return; } } record.setModified (light); } CSMWorld::MiscRefIdAdapter::MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key) : InventoryRefIdAdapter (UniversalId::Type_Miscellaneous, columns), mKey (key) {} QVariant CSMWorld::MiscRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); if (column==mKey) return record.get().mData.mIsKey!=0; return InventoryRefIdAdapter::getData (column, data, index); } void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); ESM::Miscellaneous misc = record.get(); if (column==mKey) misc.mData.mIsKey = value.toInt(); else { InventoryRefIdAdapter::setData (column, data, index, value); return; } record.setModified(misc); } CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), mRace(NULL), mClass(NULL), mFaction(NULL), mHair(NULL), mHead(NULL), mAttributes(NULL), mSkills(NULL), mMisc(NULL) {} CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) : ActorRefIdAdapter (UniversalId::Type_Npc, columns), mColumns (columns) {} QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); if (column==mColumns.mRace) return QString::fromUtf8 (record.get().mRace.c_str()); if (column==mColumns.mClass) return QString::fromUtf8 (record.get().mClass.c_str()); if (column==mColumns.mFaction) return QString::fromUtf8 (record.get().mFaction.c_str()); if (column==mColumns.mHair) return QString::fromUtf8 (record.get().mHair.c_str()); if (column==mColumns.mHead) return QString::fromUtf8 (record.get().mHead.c_str()); if (column==mColumns.mAttributes || column==mColumns.mSkills) { if ((record.get().mFlags & ESM::NPC::Autocalc) != 0) return QVariant::fromValue(ColumnBase::TableEdit_None); else return QVariant::fromValue(ColumnBase::TableEdit_FixedRows); } if (column==mColumns.mMisc) return QVariant::fromValue(ColumnBase::TableEdit_Full); std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) return (record.get().mFlags & iter->second)!=0; return ActorRefIdAdapter::getData (column, data, index); } void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); if (column==mColumns.mRace) npc.mRace = value.toString().toUtf8().constData(); else if (column==mColumns.mClass) npc.mClass = value.toString().toUtf8().constData(); else if (column==mColumns.mFaction) npc.mFaction = value.toString().toUtf8().constData(); else if (column==mColumns.mHair) npc.mHair = value.toString().toUtf8().constData(); else if (column==mColumns.mHead) npc.mHead = value.toString().toUtf8().constData(); else { std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) npc.mFlags |= iter->second; else npc.mFlags &= ~iter->second; if (iter->second == ESM::NPC::Autocalc) npc.mNpdtType = (value.toInt() != 0) ? ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS : ESM::NPC::NPC_DEFAULT; } else { ActorRefIdAdapter::setData (column, data, index, value); return; } } record.setModified (npc); } CSMWorld::NpcAttributesRefIdAdapter::NpcAttributesRefIdAdapter () {} void CSMWorld::NpcAttributesRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::NpcAttributesRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::NpcAttributesRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); // store the whole struct npc.mNpdt52 = static_cast > &>(nestedTable).mNestedTable.at(0); record.setModified (npc); } CSMWorld::NestedTableWrapperBase* CSMWorld::NpcAttributesRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); // return the whole struct std::vector wrap; wrap.push_back(record.get().mNpdt52); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(wrap); } QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52; if (subColIndex == 0) return subRowIndex; else if (subColIndex == 1) switch (subRowIndex) { case 0: return static_cast(npcStruct.mStrength); case 1: return static_cast(npcStruct.mIntelligence); case 2: return static_cast(npcStruct.mWillpower); case 3: return static_cast(npcStruct.mAgility); case 4: return static_cast(npcStruct.mSpeed); case 5: return static_cast(npcStruct.mEndurance); case 6: return static_cast(npcStruct.mPersonality); case 7: return static_cast(npcStruct.mLuck); default: return QVariant(); // throw an exception here? } else return QVariant(); // throw an exception here? } void CSMWorld::NpcAttributesRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; if (subColIndex == 1) switch(subRowIndex) { case 0: npcStruct.mStrength = static_cast(value.toInt()); break; case 1: npcStruct.mIntelligence = static_cast(value.toInt()); break; case 2: npcStruct.mWillpower = static_cast(value.toInt()); break; case 3: npcStruct.mAgility = static_cast(value.toInt()); break; case 4: npcStruct.mSpeed = static_cast(value.toInt()); break; case 5: npcStruct.mEndurance = static_cast(value.toInt()); break; case 6: npcStruct.mPersonality = static_cast(value.toInt()); break; case 7: npcStruct.mLuck = static_cast(value.toInt()); break; default: return; // throw an exception here? } else return; // throw an exception here? record.setModified (npc); } int CSMWorld::NpcAttributesRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 2; } int CSMWorld::NpcAttributesRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { // There are 8 attributes return 8; } CSMWorld::NpcSkillsRefIdAdapter::NpcSkillsRefIdAdapter () {} void CSMWorld::NpcSkillsRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::NpcSkillsRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::NpcSkillsRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); // store the whole struct npc.mNpdt52 = static_cast > &>(nestedTable).mNestedTable.at(0); record.setModified (npc); } CSMWorld::NestedTableWrapperBase* CSMWorld::NpcSkillsRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); // return the whole struct std::vector wrap; wrap.push_back(record.get().mNpdt52); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(wrap); } QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52; if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) throw std::runtime_error ("index out of range"); if (subColIndex == 0) return subRowIndex; else if (subColIndex == 1) return static_cast(npcStruct.mSkills[subRowIndex]); else return QVariant(); // throw an exception here? } void CSMWorld::NpcSkillsRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) throw std::runtime_error ("index out of range"); if (subColIndex == 1) npcStruct.mSkills[subRowIndex] = static_cast(value.toInt()); else return; // throw an exception here? record.setModified (npc); } int CSMWorld::NpcSkillsRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 2; } int CSMWorld::NpcSkillsRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { // There are 27 skills return ESM::Skill::Length; } CSMWorld::NpcMiscRefIdAdapter::NpcMiscRefIdAdapter () {} CSMWorld::NpcMiscRefIdAdapter::~NpcMiscRefIdAdapter() {} void CSMWorld::NpcMiscRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { throw std::logic_error ("cannot add a row to a fixed table"); } void CSMWorld::NpcMiscRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { throw std::logic_error ("cannot remove a row to a fixed table"); } void CSMWorld::NpcMiscRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { throw std::logic_error ("table operation not supported"); } CSMWorld::NestedTableWrapperBase* CSMWorld::NpcMiscRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { throw std::logic_error ("table operation not supported"); } QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0; if (autoCalc) switch (subColIndex) { case 0: return static_cast(record.get().mNpdt12.mLevel); case 1: return QVariant(QVariant::UserType); case 2: return QVariant(QVariant::UserType); case 3: return QVariant(QVariant::UserType); case 4: return QVariant(QVariant::UserType); case 5: return static_cast(record.get().mNpdt12.mDisposition); case 6: return static_cast(record.get().mNpdt12.mReputation); case 7: return static_cast(record.get().mNpdt12.mRank); case 8: return record.get().mNpdt12.mGold; case 9: return record.get().mPersistent == true; default: return QVariant(); // throw an exception here? } else switch (subColIndex) { case 0: return static_cast(record.get().mNpdt52.mLevel); case 1: return static_cast(record.get().mNpdt52.mFactionID); case 2: return static_cast(record.get().mNpdt52.mHealth); case 3: return static_cast(record.get().mNpdt52.mMana); case 4: return static_cast(record.get().mNpdt52.mFatigue); case 5: return static_cast(record.get().mNpdt52.mDisposition); case 6: return static_cast(record.get().mNpdt52.mReputation); case 7: return static_cast(record.get().mNpdt52.mRank); case 8: return record.get().mNpdt52.mGold; case 9: return record.get().mPersistent == true; default: return QVariant(); // throw an exception here? } } void CSMWorld::NpcMiscRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0; if (autoCalc) switch(subColIndex) { case 0: npc.mNpdt12.mLevel = static_cast(value.toInt()); break; case 1: return; case 2: return; case 3: return; case 4: return; case 5: npc.mNpdt12.mDisposition = static_cast(value.toInt()); break; case 6: npc.mNpdt12.mReputation = static_cast(value.toInt()); break; case 7: npc.mNpdt12.mRank = static_cast(value.toInt()); break; case 8: npc.mNpdt12.mGold = value.toInt(); break; case 9: npc.mPersistent = value.toBool(); break; default: return; // throw an exception here? } else switch(subColIndex) { case 0: npc.mNpdt52.mLevel = static_cast(value.toInt()); break; case 1: npc.mNpdt52.mFactionID = static_cast(value.toInt()); break; case 2: npc.mNpdt52.mHealth = static_cast(value.toInt()); break; case 3: npc.mNpdt52.mMana = static_cast(value.toInt()); break; case 4: npc.mNpdt52.mFatigue = static_cast(value.toInt()); break; case 5: npc.mNpdt52.mDisposition = static_cast(value.toInt()); break; case 6: npc.mNpdt52.mReputation = static_cast(value.toInt()); break; case 7: npc.mNpdt52.mRank = static_cast(value.toInt()); break; case 8: npc.mNpdt52.mGold = value.toInt(); break; case 9: npc.mPersistent = value.toBool(); break; default: return; // throw an exception here? } record.setModified (npc); } int CSMWorld::NpcMiscRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 10; // Level, FactionID, Health, Mana, Fatigue, Disposition, Reputation, Rank, Gold, Persist } int CSMWorld::NpcMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { return 1; // fixed at size 1 } CSMWorld::CreatureAttributesRefIdAdapter::CreatureAttributesRefIdAdapter() {} void CSMWorld::CreatureAttributesRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::CreatureAttributesRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::CreatureAttributesRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); ESM::Creature creature = record.get(); // store the whole struct creature.mData = static_cast > &>(nestedTable).mNestedTable.at(0); record.setModified (creature); } CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttributesRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); // return the whole struct std::vector wrap; wrap.push_back(record.get().mData); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(wrap); } QVariant CSMWorld::CreatureAttributesRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); const ESM::Creature& creature = record.get(); if (subColIndex == 0) return subRowIndex; else if (subColIndex == 1) switch (subRowIndex) { case 0: return creature.mData.mStrength; case 1: return creature.mData.mIntelligence; case 2: return creature.mData.mWillpower; case 3: return creature.mData.mAgility; case 4: return creature.mData.mSpeed; case 5: return creature.mData.mEndurance; case 6: return creature.mData.mPersonality; case 7: return creature.mData.mLuck; default: return QVariant(); // throw an exception here? } else return QVariant(); // throw an exception here? } void CSMWorld::CreatureAttributesRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Creature))); ESM::Creature creature = record.get(); if (subColIndex == 1) switch(subRowIndex) { case 0: creature.mData.mStrength = value.toInt(); break; case 1: creature.mData.mIntelligence = value.toInt(); break; case 2: creature.mData.mWillpower = value.toInt(); break; case 3: creature.mData.mAgility = value.toInt(); break; case 4: creature.mData.mSpeed = value.toInt(); break; case 5: creature.mData.mEndurance = value.toInt(); break; case 6: creature.mData.mPersonality = value.toInt(); break; case 7: creature.mData.mLuck = value.toInt(); break; default: return; // throw an exception here? } else return; // throw an exception here? record.setModified (creature); } int CSMWorld::CreatureAttributesRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 2; } int CSMWorld::CreatureAttributesRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { // There are 8 attributes return 8; } CSMWorld::CreatureAttackRefIdAdapter::CreatureAttackRefIdAdapter() {} void CSMWorld::CreatureAttackRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::CreatureAttackRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::CreatureAttackRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); ESM::Creature creature = record.get(); // store the whole struct creature.mData = static_cast > &>(nestedTable).mNestedTable.at(0); record.setModified (creature); } CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttackRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); // return the whole struct std::vector wrap; wrap.push_back(record.get().mData); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(wrap); } QVariant CSMWorld::CreatureAttackRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); const ESM::Creature& creature = record.get(); if (subRowIndex < 0 || subRowIndex > 2 || subColIndex < 0 || subColIndex > 2) throw std::runtime_error ("index out of range"); if (subColIndex == 0) return subRowIndex + 1; else if (subColIndex < 3) // 1 or 2 return creature.mData.mAttack[(subRowIndex * 2) + (subColIndex - 1)]; else return QVariant(); // throw an exception here? } void CSMWorld::CreatureAttackRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Creature))); ESM::Creature creature = record.get(); if (subRowIndex < 0 || subRowIndex > 2) throw std::runtime_error ("index out of range"); if (subColIndex == 1 || subColIndex == 2) creature.mData.mAttack[(subRowIndex * 2) + (subColIndex - 1)] = value.toInt(); else return; // throw an exception here? record.setModified (creature); } int CSMWorld::CreatureAttackRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 3; } int CSMWorld::CreatureAttackRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { // There are 3 attacks return 3; } CSMWorld::CreatureMiscRefIdAdapter::CreatureMiscRefIdAdapter() {} CSMWorld::CreatureMiscRefIdAdapter::~CreatureMiscRefIdAdapter() {} void CSMWorld::CreatureMiscRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { throw std::logic_error ("cannot add a row to a fixed table"); } void CSMWorld::CreatureMiscRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { throw std::logic_error ("cannot remove a row to a fixed table"); } void CSMWorld::CreatureMiscRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { throw std::logic_error ("table operation not supported"); } CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureMiscRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { throw std::logic_error ("table operation not supported"); } QVariant CSMWorld::CreatureMiscRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); const ESM::Creature& creature = record.get(); switch (subColIndex) { case 0: return creature.mData.mLevel; case 1: return creature.mData.mHealth; case 2: return creature.mData.mMana; case 3: return creature.mData.mFatigue; case 4: return creature.mData.mSoul; case 5: return creature.mData.mCombat; case 6: return creature.mData.mMagic; case 7: return creature.mData.mStealth; case 8: return creature.mData.mGold; default: return QVariant(); // throw an exception here? } } void CSMWorld::CreatureMiscRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Creature))); ESM::Creature creature = record.get(); switch(subColIndex) { case 0: creature.mData.mLevel = value.toInt(); break; case 1: creature.mData.mHealth = value.toInt(); break; case 2: creature.mData.mMana = value.toInt(); break; case 3: creature.mData.mFatigue = value.toInt(); break; case 4: creature.mData.mSoul = value.toInt(); break; case 5: creature.mData.mCombat = value.toInt(); break; case 6: creature.mData.mMagic = value.toInt(); break; case 7: creature.mData.mStealth = value.toInt(); break; case 8: creature.mData.mGold = value.toInt(); break; default: return; // throw an exception here? } record.setModified (creature); } int CSMWorld::CreatureMiscRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 9; // Level, Health, Mana, Fatigue, Soul, Combat, Magic, Steath, Gold } int CSMWorld::CreatureMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { return 1; // fixed at size 1 } CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns) : EnchantableColumns (columns) {} CSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns) : EnchantableRefIdAdapter (UniversalId::Type_Weapon, columns), mColumns (columns) {} QVariant CSMWorld::WeaponRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon))); if (column==mColumns.mType) return record.get().mData.mType; if (column==mColumns.mHealth) return record.get().mData.mHealth; if (column==mColumns.mSpeed) return record.get().mData.mSpeed; if (column==mColumns.mReach) return record.get().mData.mReach; for (int i=0; i<2; ++i) { if (column==mColumns.mChop[i]) return record.get().mData.mChop[i]; if (column==mColumns.mSlash[i]) return record.get().mData.mSlash[i]; if (column==mColumns.mThrust[i]) return record.get().mData.mThrust[i]; } std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) return (record.get().mData.mFlags & iter->second)!=0; return EnchantableRefIdAdapter::getData (column, data, index); } void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon))); if (column==mColumns.mType) record.get().mData.mType = value.toInt(); else if (column==mColumns.mHealth) record.get().mData.mHealth = value.toInt(); else if (column==mColumns.mSpeed) record.get().mData.mSpeed = value.toFloat(); else if (column==mColumns.mReach) record.get().mData.mReach = value.toFloat(); else if (column==mColumns.mChop[0]) record.get().mData.mChop[0] = value.toInt(); else if (column==mColumns.mChop[1]) record.get().mData.mChop[1] = value.toInt(); else if (column==mColumns.mSlash[0]) record.get().mData.mSlash[0] = value.toInt(); else if (column==mColumns.mSlash[1]) record.get().mData.mSlash[1] = value.toInt(); else if (column==mColumns.mThrust[0]) record.get().mData.mThrust[0] = value.toInt(); else if (column==mColumns.mThrust[1]) record.get().mData.mThrust[1] = value.toInt(); else { std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) record.get().mData.mFlags |= iter->second; else record.get().mData.mFlags &= ~iter->second; } else EnchantableRefIdAdapter::setData (column, data, index, value); } } openmw-openmw-0.38.0/apps/opencs/model/world/refidadapterimp.hpp000066400000000000000000002646761264522266000247760ustar00rootroot00000000000000#ifndef CSM_WOLRD_REFIDADAPTERIMP_H #define CSM_WOLRD_REFIDADAPTERIMP_H #include #include #include #include #include #include #include #include "columnbase.hpp" #include "record.hpp" #include "refiddata.hpp" #include "universalid.hpp" #include "refidadapter.hpp" #include "nestedtablewrapper.hpp" namespace CSMWorld { struct BaseColumns { const RefIdColumn *mId; const RefIdColumn *mModified; const RefIdColumn *mType; }; /// \brief Base adapter for all refereceable record types /// Adapters that can handle nested tables, needs to return valid qvariant for parent columns template class BaseRefIdAdapter : public RefIdAdapter { UniversalId::Type mType; BaseColumns mBase; public: BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base); virtual std::string getId (const RecordBase& record) const; virtual void setId (RecordBase& record, const std::string& id); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. UniversalId::Type getType() const; }; template BaseRefIdAdapter::BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base) : mType (type), mBase (base) {} template void BaseRefIdAdapter::setId (RecordBase& record, const std::string& id) { (dynamic_cast&> (record).get().mId) = id; } template std::string BaseRefIdAdapter::getId (const RecordBase& record) const { return dynamic_cast&> (record).get().mId; } template QVariant BaseRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, mType))); if (column==mBase.mId) return QString::fromUtf8 (record.get().mId.c_str()); if (column==mBase.mModified) { if (record.mState==Record::State_Erased) return static_cast (Record::State_Deleted); return static_cast (record.mState); } if (column==mBase.mType) return static_cast (mType); return QVariant(); } template void BaseRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, mType))); if (column==mBase.mModified) record.mState = static_cast (value.toInt()); } template UniversalId::Type BaseRefIdAdapter::getType() const { return mType; } struct ModelColumns : public BaseColumns { const RefIdColumn *mModel; ModelColumns (const BaseColumns& base) : BaseColumns (base) {} }; /// \brief Adapter for IDs with models (all but levelled lists) template class ModelRefIdAdapter : public BaseRefIdAdapter { ModelColumns mModel; public: ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; template ModelRefIdAdapter::ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns) : BaseRefIdAdapter (type, columns), mModel (columns) {} template QVariant ModelRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); if (column==mModel.mModel) return QString::fromUtf8 (record.get().mModel.c_str()); return BaseRefIdAdapter::getData (column, data, index); } template void ModelRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column==mModel.mModel) record2.mModel = value.toString().toUtf8().constData(); else { BaseRefIdAdapter::setData (column, data, index, value); return; } record.setModified(record2); } struct NameColumns : public ModelColumns { const RefIdColumn *mName; const RefIdColumn *mScript; NameColumns (const ModelColumns& base) : ModelColumns (base) {} }; /// \brief Adapter for IDs with names (all but levelled lists and statics) template class NameRefIdAdapter : public ModelRefIdAdapter { NameColumns mName; public: NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; template NameRefIdAdapter::NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns) : ModelRefIdAdapter (type, columns), mName (columns) {} template QVariant NameRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); if (column==mName.mName) return QString::fromUtf8 (record.get().mName.c_str()); if (column==mName.mScript) return QString::fromUtf8 (record.get().mScript.c_str()); return ModelRefIdAdapter::getData (column, data, index); } template void NameRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column==mName.mName) record2.mName = value.toString().toUtf8().constData(); else if (column==mName.mScript) record2.mScript = value.toString().toUtf8().constData(); else { ModelRefIdAdapter::setData (column, data, index, value); return; } record.setModified(record2); } struct InventoryColumns : public NameColumns { const RefIdColumn *mIcon; const RefIdColumn *mWeight; const RefIdColumn *mValue; InventoryColumns (const NameColumns& base) : NameColumns (base) {} }; /// \brief Adapter for IDs that can go into an inventory template class InventoryRefIdAdapter : public NameRefIdAdapter { InventoryColumns mInventory; public: InventoryRefIdAdapter (UniversalId::Type type, const InventoryColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; template InventoryRefIdAdapter::InventoryRefIdAdapter (UniversalId::Type type, const InventoryColumns& columns) : NameRefIdAdapter (type, columns), mInventory (columns) {} template QVariant InventoryRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); if (column==mInventory.mIcon) return QString::fromUtf8 (record.get().mIcon.c_str()); if (column==mInventory.mWeight) return record.get().mData.mWeight; if (column==mInventory.mValue) return record.get().mData.mValue; return NameRefIdAdapter::getData (column, data, index); } template void InventoryRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column==mInventory.mIcon) record2.mIcon = value.toString().toUtf8().constData(); else if (column==mInventory.mWeight) record2.mData.mWeight = value.toFloat(); else if (column==mInventory.mValue) record2.mData.mValue = value.toInt(); else { NameRefIdAdapter::setData (column, data, index, value); return; } record.setModified(record2); } struct PotionColumns : public InventoryColumns { const RefIdColumn *mEffects; PotionColumns (const InventoryColumns& columns); }; class PotionRefIdAdapter : public InventoryRefIdAdapter { PotionColumns mColumns; const RefIdColumn *mAutoCalc; public: PotionRefIdAdapter (const PotionColumns& columns, const RefIdColumn *autoCalc); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; struct IngredientColumns : public InventoryColumns { const RefIdColumn *mEffects; IngredientColumns (const InventoryColumns& columns); }; class IngredientRefIdAdapter : public InventoryRefIdAdapter { IngredientColumns mColumns; public: IngredientRefIdAdapter (const IngredientColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; class IngredEffectRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented IngredEffectRefIdAdapter (const IngredEffectRefIdAdapter&); IngredEffectRefIdAdapter& operator= (const IngredEffectRefIdAdapter&); public: IngredEffectRefIdAdapter(); virtual ~IngredEffectRefIdAdapter(); virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const; virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const; virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const; virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const; virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; }; struct EnchantableColumns : public InventoryColumns { const RefIdColumn *mEnchantment; const RefIdColumn *mEnchantmentPoints; EnchantableColumns (const InventoryColumns& base) : InventoryColumns (base) {} }; /// \brief Adapter for enchantable IDs template class EnchantableRefIdAdapter : public InventoryRefIdAdapter { EnchantableColumns mEnchantable; public: EnchantableRefIdAdapter (UniversalId::Type type, const EnchantableColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; template EnchantableRefIdAdapter::EnchantableRefIdAdapter (UniversalId::Type type, const EnchantableColumns& columns) : InventoryRefIdAdapter (type, columns), mEnchantable (columns) {} template QVariant EnchantableRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); if (column==mEnchantable.mEnchantment) return QString::fromUtf8 (record.get().mEnchant.c_str()); if (column==mEnchantable.mEnchantmentPoints) return static_cast (record.get().mData.mEnchant); return InventoryRefIdAdapter::getData (column, data, index); } template void EnchantableRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column==mEnchantable.mEnchantment) record2.mEnchant = value.toString().toUtf8().constData(); else if (column==mEnchantable.mEnchantmentPoints) record2.mData.mEnchant = value.toInt(); else { InventoryRefIdAdapter::setData (column, data, index, value); return; } record.setModified(record2); } struct ToolColumns : public InventoryColumns { const RefIdColumn *mQuality; const RefIdColumn *mUses; ToolColumns (const InventoryColumns& base) : InventoryColumns (base) {} }; /// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes) template class ToolRefIdAdapter : public InventoryRefIdAdapter { ToolColumns mTools; public: ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; template ToolRefIdAdapter::ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns) : InventoryRefIdAdapter (type, columns), mTools (columns) {} template QVariant ToolRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); if (column==mTools.mQuality) return record.get().mData.mQuality; if (column==mTools.mUses) return record.get().mData.mUses; return InventoryRefIdAdapter::getData (column, data, index); } template void ToolRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column==mTools.mQuality) record2.mData.mQuality = value.toFloat(); else if (column==mTools.mUses) record2.mData.mUses = value.toInt(); else { InventoryRefIdAdapter::setData (column, data, index, value); return; } record.setModified(record2); } struct ActorColumns : public NameColumns { const RefIdColumn *mHello; const RefIdColumn *mFlee; const RefIdColumn *mFight; const RefIdColumn *mAlarm; const RefIdColumn *mInventory; const RefIdColumn *mSpells; const RefIdColumn *mDestinations; const RefIdColumn *mAiPackages; std::map mServices; ActorColumns (const NameColumns& base) : NameColumns (base) {} }; /// \brief Adapter for actor IDs (handles common AI functionality) template class ActorRefIdAdapter : public NameRefIdAdapter { ActorColumns mActors; public: ActorRefIdAdapter (UniversalId::Type type, const ActorColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; template ActorRefIdAdapter::ActorRefIdAdapter (UniversalId::Type type, const ActorColumns& columns) : NameRefIdAdapter (type, columns), mActors (columns) {} template QVariant ActorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); if (column==mActors.mHello) return record.get().mAiData.mHello; if (column==mActors.mFlee) return record.get().mAiData.mFlee; if (column==mActors.mFight) return record.get().mAiData.mFight; if (column==mActors.mAlarm) return record.get().mAiData.mAlarm; if (column==mActors.mInventory) return QVariant::fromValue(ColumnBase::TableEdit_Full); if (column==mActors.mSpells) return QVariant::fromValue(ColumnBase::TableEdit_Full); if (column==mActors.mDestinations) return QVariant::fromValue(ColumnBase::TableEdit_Full); if (column==mActors.mAiPackages) return QVariant::fromValue(ColumnBase::TableEdit_Full); std::map::const_iterator iter = mActors.mServices.find (column); if (iter!=mActors.mServices.end()) return (record.get().mAiData.mServices & iter->second)!=0; return NameRefIdAdapter::getData (column, data, index); } template void ActorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); RecordT record2 = record.get(); if (column==mActors.mHello) record2.mAiData.mHello = value.toInt(); else if (column==mActors.mFlee) record2.mAiData.mFlee = value.toInt(); else if (column==mActors.mFight) record2.mAiData.mFight = value.toInt(); else if (column==mActors.mAlarm) record2.mAiData.mAlarm = value.toInt(); else { typename std::map::const_iterator iter = mActors.mServices.find (column); if (iter!=mActors.mServices.end()) { if (value.toInt()!=0) record2.mAiData.mServices |= iter->second; else record2.mAiData.mServices &= ~iter->second; } else { NameRefIdAdapter::setData (column, data, index, value); return; } } record.setModified(record2); } class ApparatusRefIdAdapter : public InventoryRefIdAdapter { const RefIdColumn *mType; const RefIdColumn *mQuality; public: ApparatusRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *type, const RefIdColumn *quality); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; class ArmorRefIdAdapter : public EnchantableRefIdAdapter { const RefIdColumn *mType; const RefIdColumn *mHealth; const RefIdColumn *mArmor; const RefIdColumn *mPartRef; public: ArmorRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type, const RefIdColumn *health, const RefIdColumn *armor, const RefIdColumn *partRef); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; class BookRefIdAdapter : public EnchantableRefIdAdapter { const RefIdColumn *mScroll; const RefIdColumn *mSkill; public: BookRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *scroll, const RefIdColumn *skill); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; class ClothingRefIdAdapter : public EnchantableRefIdAdapter { const RefIdColumn *mType; const RefIdColumn *mPartRef; public: ClothingRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type, const RefIdColumn *partRef); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; class ContainerRefIdAdapter : public NameRefIdAdapter { const RefIdColumn *mWeight; const RefIdColumn *mOrganic; const RefIdColumn *mRespawn; const RefIdColumn *mContent; public: ContainerRefIdAdapter (const NameColumns& columns, const RefIdColumn *weight, const RefIdColumn *organic, const RefIdColumn *respawn, const RefIdColumn *content); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; struct CreatureColumns : public ActorColumns { std::map mFlags; const RefIdColumn *mType; const RefIdColumn *mScale; const RefIdColumn *mOriginal; const RefIdColumn *mAttributes; const RefIdColumn *mAttacks; const RefIdColumn *mMisc; CreatureColumns (const ActorColumns& actorColumns); }; class CreatureRefIdAdapter : public ActorRefIdAdapter { CreatureColumns mColumns; public: CreatureRefIdAdapter (const CreatureColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; class DoorRefIdAdapter : public NameRefIdAdapter { const RefIdColumn *mOpenSound; const RefIdColumn *mCloseSound; public: DoorRefIdAdapter (const NameColumns& columns, const RefIdColumn *openSound, const RefIdColumn *closeSound); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; struct LightColumns : public InventoryColumns { const RefIdColumn *mTime; const RefIdColumn *mRadius; const RefIdColumn *mColor; const RefIdColumn *mSound; std::map mFlags; LightColumns (const InventoryColumns& columns); }; class LightRefIdAdapter : public InventoryRefIdAdapter { LightColumns mColumns; public: LightRefIdAdapter (const LightColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; class MiscRefIdAdapter : public InventoryRefIdAdapter { const RefIdColumn *mKey; public: MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; struct NpcColumns : public ActorColumns { std::map mFlags; const RefIdColumn *mRace; const RefIdColumn *mClass; const RefIdColumn *mFaction; const RefIdColumn *mHair; const RefIdColumn *mHead; const RefIdColumn *mAttributes; // depends on npc type const RefIdColumn *mSkills; // depends on npc type const RefIdColumn *mMisc; // may depend on npc type, e.g. FactionID NpcColumns (const ActorColumns& actorColumns); }; class NpcRefIdAdapter : public ActorRefIdAdapter { NpcColumns mColumns; public: NpcRefIdAdapter (const NpcColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; struct WeaponColumns : public EnchantableColumns { const RefIdColumn *mType; const RefIdColumn *mHealth; const RefIdColumn *mSpeed; const RefIdColumn *mReach; const RefIdColumn *mChop[2]; const RefIdColumn *mSlash[2]; const RefIdColumn *mThrust[2]; std::map mFlags; WeaponColumns (const EnchantableColumns& columns); }; class WeaponRefIdAdapter : public EnchantableRefIdAdapter { WeaponColumns mColumns; public: WeaponRefIdAdapter (const WeaponColumns& columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; class NestedRefIdAdapterBase; class NpcAttributesRefIdAdapter : public NestedRefIdAdapterBase { public: NpcAttributesRefIdAdapter (); virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const; virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const; virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const; virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const; virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; }; class NpcSkillsRefIdAdapter : public NestedRefIdAdapterBase { public: NpcSkillsRefIdAdapter (); virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const; virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const; virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const; virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const; virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; }; class NpcMiscRefIdAdapter : public NestedRefIdAdapterBase { NpcMiscRefIdAdapter (const NpcMiscRefIdAdapter&); NpcMiscRefIdAdapter& operator= (const NpcMiscRefIdAdapter&); public: NpcMiscRefIdAdapter (); virtual ~NpcMiscRefIdAdapter(); virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const; virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const; virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const; virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const; virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; }; class CreatureAttributesRefIdAdapter : public NestedRefIdAdapterBase { public: CreatureAttributesRefIdAdapter (); virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const; virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const; virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const; virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const; virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; }; class CreatureAttackRefIdAdapter : public NestedRefIdAdapterBase { public: CreatureAttackRefIdAdapter (); virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const; virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const; virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const; virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const; virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; }; class CreatureMiscRefIdAdapter : public NestedRefIdAdapterBase { CreatureMiscRefIdAdapter (const CreatureMiscRefIdAdapter&); CreatureMiscRefIdAdapter& operator= (const CreatureMiscRefIdAdapter&); public: CreatureMiscRefIdAdapter (); virtual ~CreatureMiscRefIdAdapter(); virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const; virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const; virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const; virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const; virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; }; template class EffectsListAdapter; template class EffectsRefIdAdapter : public EffectsListAdapter, public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented EffectsRefIdAdapter (const EffectsRefIdAdapter&); EffectsRefIdAdapter& operator= (const EffectsRefIdAdapter&); public: EffectsRefIdAdapter(UniversalId::Type type) :mType(type) {} virtual ~EffectsRefIdAdapter() {} virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); EffectsListAdapter::addRow(record, position); } virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); EffectsListAdapter::removeRow(record, rowToRemove); } virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); EffectsListAdapter::setTable(record, nestedTable); } virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); return EffectsListAdapter::table(record); } virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); return EffectsListAdapter::getData(record, subRowIndex, subColIndex); } virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); EffectsListAdapter::setData(record, value, subRowIndex, subColIndex); } virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { const Record record; // not used, just a dummy return EffectsListAdapter::getColumnsCount(record); } virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); return EffectsListAdapter::getRowsCount(record); } }; template class NestedInventoryRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedInventoryRefIdAdapter (const NestedInventoryRefIdAdapter&); NestedInventoryRefIdAdapter& operator= (const NestedInventoryRefIdAdapter&); public: NestedInventoryRefIdAdapter(UniversalId::Type type) :mType(type) {} virtual ~NestedInventoryRefIdAdapter() {} virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT container = record.get(); std::vector& list = container.mInventory.mList; ESM::ContItem newRow = {0, {""}}; if (position >= (int)list.size()) list.push_back(newRow); else list.insert(list.begin()+position, newRow); record.setModified (container); } virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT container = record.get(); std::vector& list = container.mInventory.mList; if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) throw std::runtime_error ("index out of range"); list.erase (list.begin () + rowToRemove); record.setModified (container); } virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT container = record.get(); container.mInventory.mList = static_cast >&>(nestedTable).mNestedTable; record.setModified (container); } virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mInventory.mList); } virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); const std::vector& list = record.get().mInventory.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); const ESM::ContItem& content = list.at(subRowIndex); switch (subColIndex) { case 0: return QString::fromUtf8(content.mItem.toString().c_str()); case 1: return content.mCount; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); ESXRecordT container = record.get(); std::vector& list = container.mInventory.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); switch(subColIndex) { case 0: list.at(subRowIndex).mItem.assign(std::string(value.toString().toUtf8().constData())); break; case 1: list.at(subRowIndex).mCount = value.toInt(); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified (container); } virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 2; } virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); return static_cast(record.get().mInventory.mList.size()); } }; template class NestedSpellRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedSpellRefIdAdapter (const NestedSpellRefIdAdapter&); NestedSpellRefIdAdapter& operator= (const NestedSpellRefIdAdapter&); public: NestedSpellRefIdAdapter(UniversalId::Type type) :mType(type) {} virtual ~NestedSpellRefIdAdapter() {} virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT caster = record.get(); std::vector& list = caster.mSpells.mList; std::string newString; if (position >= (int)list.size()) list.push_back(newString); else list.insert(list.begin()+position, newString); record.setModified (caster); } virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT caster = record.get(); std::vector& list = caster.mSpells.mList; if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) throw std::runtime_error ("index out of range"); list.erase (list.begin () + rowToRemove); record.setModified (caster); } virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT caster = record.get(); caster.mSpells.mList = static_cast >&>(nestedTable).mNestedTable; record.setModified (caster); } virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mSpells.mList); } virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); const std::vector& list = record.get().mSpells.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); const std::string& content = list.at(subRowIndex); if (subColIndex == 0) return QString::fromUtf8(content.c_str()); else throw std::runtime_error("Trying to access non-existing column in the nested table!"); } virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); ESXRecordT caster = record.get(); std::vector& list = caster.mSpells.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); if (subColIndex == 0) list.at(subRowIndex) = std::string(value.toString().toUtf8()); else throw std::runtime_error("Trying to access non-existing column in the nested table!"); record.setModified (caster); } virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 1; } virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); return static_cast(record.get().mSpells.mList.size()); } }; template class NestedTravelRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedTravelRefIdAdapter (const NestedTravelRefIdAdapter&); NestedTravelRefIdAdapter& operator= (const NestedTravelRefIdAdapter&); public: NestedTravelRefIdAdapter(UniversalId::Type type) :mType(type) {} virtual ~NestedTravelRefIdAdapter() {} virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT traveller = record.get(); std::vector& list = traveller.mTransport.mList; ESM::Position newPos; for (unsigned i = 0; i < 3; ++i) { newPos.pos[i] = 0; newPos.rot[i] = 0; } ESM::Transport::Dest newRow; newRow.mPos = newPos; newRow.mCellName = ""; if (position >= (int)list.size()) list.push_back(newRow); else list.insert(list.begin()+position, newRow); record.setModified (traveller); } virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT traveller = record.get(); std::vector& list = traveller.mTransport.mList; if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) throw std::runtime_error ("index out of range"); list.erase (list.begin () + rowToRemove); record.setModified (traveller); } virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT traveller = record.get(); traveller.mTransport.mList = static_cast >&>(nestedTable).mNestedTable; record.setModified (traveller); } virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mTransport.mList); } virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); const std::vector& list = record.get().mTransport.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); const ESM::Transport::Dest& content = list.at(subRowIndex); switch (subColIndex) { case 0: return QString::fromUtf8(content.mCellName.c_str()); case 1: return content.mPos.pos[0]; case 2: return content.mPos.pos[1]; case 3: return content.mPos.pos[2]; case 4: return content.mPos.rot[0]; case 5: return content.mPos.rot[1]; case 6: return content.mPos.rot[2]; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); ESXRecordT traveller = record.get(); std::vector& list = traveller.mTransport.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); switch(subColIndex) { case 0: list.at(subRowIndex).mCellName = std::string(value.toString().toUtf8().constData()); break; case 1: list.at(subRowIndex).mPos.pos[0] = value.toFloat(); break; case 2: list.at(subRowIndex).mPos.pos[1] = value.toFloat(); break; case 3: list.at(subRowIndex).mPos.pos[2] = value.toFloat(); break; case 4: list.at(subRowIndex).mPos.rot[0] = value.toFloat(); break; case 5: list.at(subRowIndex).mPos.rot[1] = value.toFloat(); break; case 6: list.at(subRowIndex).mPos.rot[2] = value.toFloat(); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified (traveller); } virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 7; } virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); return static_cast(record.get().mTransport.mList.size()); } }; template class ActorAiRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented ActorAiRefIdAdapter (const ActorAiRefIdAdapter&); ActorAiRefIdAdapter& operator= (const ActorAiRefIdAdapter&); public: ActorAiRefIdAdapter(UniversalId::Type type) :mType(type) {} virtual ~ActorAiRefIdAdapter() {} // FIXME: should check if the AI package type is already in the list and use a default // that wasn't used already (in extreme case do not add anything at all? virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT actor = record.get(); std::vector& list = actor.mAiPackage.mList; ESM::AIPackage newRow; newRow.mType = ESM::AI_Wander; newRow.mWander.mDistance = 0; newRow.mWander.mDuration = 0; newRow.mWander.mTimeOfDay = 0; for (int i = 0; i < 8; ++i) newRow.mWander.mIdle[i] = 0; newRow.mWander.mShouldRepeat = 0; newRow.mCellName = ""; if (position >= (int)list.size()) list.push_back(newRow); else list.insert(list.begin()+position, newRow); record.setModified (actor); } virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT actor = record.get(); std::vector& list = actor.mAiPackage.mList; if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) throw std::runtime_error ("index out of range"); list.erase (list.begin () + rowToRemove); record.setModified (actor); } virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT actor = record.get(); actor.mAiPackage.mList = static_cast >&>(nestedTable).mNestedTable; record.setModified (actor); } virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mAiPackage.mList); } virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); const std::vector& list = record.get().mAiPackage.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); const ESM::AIPackage& content = list.at(subRowIndex); switch (subColIndex) { case 0: // FIXME: should more than one AI package type be allowed? Check vanilla switch (content.mType) { case ESM::AI_Wander: return 0; case ESM::AI_Travel: return 1; case ESM::AI_Follow: return 2; case ESM::AI_Escort: return 3; case ESM::AI_Activate: return 4; case ESM::AI_CNDT: default: return QVariant(); } case 1: // wander dist if (content.mType == ESM::AI_Wander) return content.mWander.mDistance; else return QVariant(); case 2: // wander dur if (content.mType == ESM::AI_Wander || content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mWander.mDuration; else return QVariant(); case 3: // wander ToD if (content.mType == ESM::AI_Wander) return content.mWander.mTimeOfDay; // FIXME: not sure of the format else return QVariant(); case 4: // wander idle case 5: case 6: case 7: case 8: case 9: case 10: case 11: if (content.mType == ESM::AI_Wander) return static_cast(content.mWander.mIdle[subColIndex-4]); else return QVariant(); case 12: // wander repeat if (content.mType == ESM::AI_Wander) return content.mWander.mShouldRepeat != 0; else return QVariant(); case 13: // activate name if (content.mType == ESM::AI_Activate) return QString(content.mActivate.mName.toString().c_str()); else return QVariant(); case 14: // target id if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return QString(content.mTarget.mId.toString().c_str()); else return QVariant(); case 15: // target cell if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return QString::fromUtf8(content.mCellName.c_str()); else return QVariant(); case 16: if (content.mType == ESM::AI_Travel) return content.mTravel.mX; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mX; else return QVariant(); case 17: if (content.mType == ESM::AI_Travel) return content.mTravel.mY; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mY; else return QVariant(); case 18: if (content.mType == ESM::AI_Travel) return content.mTravel.mZ; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mZ; else return QVariant(); default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); ESXRecordT actor = record.get(); std::vector& list = actor.mAiPackage.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); ESM::AIPackage& content = list.at(subRowIndex); switch(subColIndex) { case 0: // ai package type switch (value.toInt()) { case 0: content.mType = ESM::AI_Wander; break; case 1: content.mType = ESM::AI_Travel; break; case 2: content.mType = ESM::AI_Follow; break; case 3: content.mType = ESM::AI_Escort; break; case 4: content.mType = ESM::AI_Activate; break; default: return; // return without saving } break; // always save case 1: if (content.mType == ESM::AI_Wander) content.mWander.mDistance = static_cast(value.toInt()); else return; // return without saving break; // always save case 2: if (content.mType == ESM::AI_Wander || content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mWander.mDuration = static_cast(value.toInt()); else return; // return without saving case 3: if (content.mType == ESM::AI_Wander) content.mWander.mTimeOfDay = static_cast(value.toInt()); else return; // return without saving break; // always save case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: if (content.mType == ESM::AI_Wander) content.mWander.mIdle[subColIndex-4] = static_cast(value.toInt()); else return; // return without saving break; // always save case 12: if (content.mType == ESM::AI_Wander) content.mWander.mShouldRepeat = static_cast(value.toInt()); else return; // return without saving break; // always save case 13: // NAME32 if (content.mType == ESM::AI_Activate) content.mActivate.mName.assign(value.toString().toUtf8().constData()); else return; // return without saving break; // always save case 14: // NAME32 if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mId.assign(value.toString().toUtf8().constData()); else return; // return without saving break; // always save case 15: if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mCellName = std::string(value.toString().toUtf8().constData()); else return; // return without saving break; // always save case 16: if (content.mType == ESM::AI_Travel) content.mTravel.mZ = value.toFloat(); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mZ = value.toFloat(); else return; // return without saving break; // always save case 17: if (content.mType == ESM::AI_Travel) content.mTravel.mZ = value.toFloat(); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mZ = value.toFloat(); else return; // return without saving break; // always save case 18: if (content.mType == ESM::AI_Travel) content.mTravel.mZ = value.toFloat(); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mZ = value.toFloat(); else return; // return without saving break; // always save default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified (actor); } virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 19; } virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); return static_cast(record.get().mAiPackage.mList.size()); } }; template class BodyPartRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented BodyPartRefIdAdapter (const BodyPartRefIdAdapter&); BodyPartRefIdAdapter& operator= (const BodyPartRefIdAdapter&); public: BodyPartRefIdAdapter(UniversalId::Type type) :mType(type) {} virtual ~BodyPartRefIdAdapter() {} virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT apparel = record.get(); std::vector& list = apparel.mParts.mParts; ESM::PartReference newPart; newPart.mPart = 0; // 0 == head newPart.mMale = ""; newPart.mFemale = ""; if (position >= (int)list.size()) list.push_back(newPart); else list.insert(list.begin()+position, newPart); record.setModified (apparel); } virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT apparel = record.get(); std::vector& list = apparel.mParts.mParts; if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) throw std::runtime_error ("index out of range"); list.erase (list.begin () + rowToRemove); record.setModified (apparel); } virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT apparel = record.get(); apparel.mParts.mParts = static_cast >&>(nestedTable).mNestedTable; record.setModified (apparel); } virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mParts.mParts); } virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); const std::vector& list = record.get().mParts.mParts; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); const ESM::PartReference& content = list.at(subRowIndex); switch (subColIndex) { case 0: { if (content.mPart < ESM::PRT_Count) return content.mPart; else throw std::runtime_error("Part Reference Type unexpected value"); } case 1: return QString(content.mMale.c_str()); case 2: return QString(content.mFemale.c_str()); default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); ESXRecordT apparel = record.get(); std::vector& list = apparel.mParts.mParts; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); switch(subColIndex) { case 0: list.at(subRowIndex).mPart = static_cast(value.toInt()); break; case 1: list.at(subRowIndex).mMale = value.toString().toStdString(); break; case 2: list.at(subRowIndex).mFemale = value.toString().toStdString(); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified (apparel); } virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 3; } virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); return static_cast(record.get().mParts.mParts.size()); } }; struct LevListColumns : public BaseColumns { const RefIdColumn *mLevList; const RefIdColumn *mNestedListLevList; LevListColumns (const BaseColumns& base) : BaseColumns (base) {} }; template class LevelledListRefIdAdapter : public BaseRefIdAdapter { LevListColumns mLevList; public: LevelledListRefIdAdapter (UniversalId::Type type, const LevListColumns &columns); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; template LevelledListRefIdAdapter::LevelledListRefIdAdapter (UniversalId::Type type, const LevListColumns &columns) : BaseRefIdAdapter (type, columns), mLevList (columns) {} template QVariant LevelledListRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { if (column==mLevList.mLevList || column == mLevList.mNestedListLevList) return QVariant::fromValue(ColumnBase::TableEdit_Full); return BaseRefIdAdapter::getData (column, data, index); } template void LevelledListRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { BaseRefIdAdapter::setData (column, data, index, value); return; } // for non-tables template class NestedListLevListRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedListLevListRefIdAdapter (const NestedListLevListRefIdAdapter&); NestedListLevListRefIdAdapter& operator= (const NestedListLevListRefIdAdapter&); public: NestedListLevListRefIdAdapter(UniversalId::Type type) :mType(type) {} virtual ~NestedListLevListRefIdAdapter() {} virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { throw std::logic_error ("cannot add a row to a fixed table"); } virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { throw std::logic_error ("cannot remove a row to a fixed table"); } virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { throw std::logic_error ("table operation not supported"); } virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { throw std::logic_error ("table operation not supported"); } virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); if (mType == UniversalId::Type_CreatureLevelledList) { switch (subColIndex) { case 0: return QVariant(); // disable the checkbox editor case 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels; case 2: return static_cast (record.get().mChanceNone); default: throw std::runtime_error("Trying to access non-existing column in levelled creatues!"); } } else { switch (subColIndex) { case 0: return record.get().mFlags & ESM::ItemLevList::Each; case 1: return record.get().mFlags & ESM::ItemLevList::AllLevels; case 2: return static_cast (record.get().mChanceNone); default: throw std::runtime_error("Trying to access non-existing column in levelled items!"); } } } virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); ESXRecordT leveled = record.get(); if (mType == UniversalId::Type_CreatureLevelledList) { switch(subColIndex) { case 0: return; // return without saving case 1: { if(value.toBool()) { leveled.mFlags |= ESM::CreatureLevList::AllLevels; break; } else { leveled.mFlags &= ~ESM::CreatureLevList::AllLevels; break; } } case 2: leveled.mChanceNone = static_cast(value.toInt()); break; default: throw std::runtime_error("Trying to set non-existing column in levelled creatures!"); } } else { switch(subColIndex) { case 0: { if(value.toBool()) { leveled.mFlags |= ESM::ItemLevList::Each; break; } else { leveled.mFlags &= ~ESM::ItemLevList::Each; break; } } case 1: { if(value.toBool()) { leveled.mFlags |= ESM::ItemLevList::AllLevels; break; } else { leveled.mFlags &= ~ESM::ItemLevList::AllLevels; break; } } case 2: leveled.mChanceNone = static_cast(value.toInt()); break; default: throw std::runtime_error("Trying to set non-existing column in levelled items!"); } } record.setModified (leveled); } virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 3; } virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { return 1; // fixed at size 1 } }; // for tables template class NestedLevListRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedLevListRefIdAdapter (const NestedLevListRefIdAdapter&); NestedLevListRefIdAdapter& operator= (const NestedLevListRefIdAdapter&); public: NestedLevListRefIdAdapter(UniversalId::Type type) :mType(type) {} virtual ~NestedLevListRefIdAdapter() {} virtual void addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT leveled = record.get(); std::vector& list = leveled.mList; ESM::LevelledListBase::LevelItem newItem; newItem.mId = ""; newItem.mLevel = 0; if (position >= (int)list.size()) list.push_back(newItem); else list.insert(list.begin()+position, newItem); record.setModified (leveled); } virtual void removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT leveled = record.get(); std::vector& list = leveled.mList; if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) throw std::runtime_error ("index out of range"); list.erase (list.begin () + rowToRemove); record.setModified (leveled); } virtual void setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); ESXRecordT leveled = record.get(); leveled.mList = static_cast >&>(nestedTable).mNestedTable; record.setModified (leveled); } virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(record.get().mList); } virtual QVariant getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); const std::vector& list = record.get().mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); const ESM::LevelledListBase::LevelItem& content = list.at(subRowIndex); switch (subColIndex) { case 0: return QString(content.mId.c_str()); case 1: return content.mLevel; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } virtual void setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); ESXRecordT leveled = record.get(); std::vector& list = leveled.mList; if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) throw std::runtime_error ("index out of range"); switch(subColIndex) { case 0: list.at(subRowIndex).mId = value.toString().toStdString(); break; case 1: list.at(subRowIndex).mLevel = static_cast(value.toInt()); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified (leveled); } virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 2; } virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); return static_cast(record.get().mList.size()); } }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/refidcollection.cpp000066400000000000000000001275041264522266000247620ustar00rootroot00000000000000#include "refidcollection.hpp" #include #include #include #include "refidadapter.hpp" #include "refidadapterimp.hpp" #include "columns.hpp" #include "nestedtablewrapper.hpp" #include "nestedcoladapterimp.hpp" CSMWorld::RefIdColumn::RefIdColumn (int columnId, Display displayType, int flag, bool editable, bool userEditable) : NestableColumn (columnId, displayType, flag), mEditable (editable), mUserEditable (userEditable) {} bool CSMWorld::RefIdColumn::isEditable() const { return mEditable; } bool CSMWorld::RefIdColumn::isUserEditable() const { return mUserEditable; } const CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdapter (UniversalId::Type type) const { std::map::const_iterator iter = mAdapters.find (type); if (iter==mAdapters.end()) throw std::logic_error ("unsupported type in RefIdCollection"); return *iter->second; } CSMWorld::RefIdCollection::RefIdCollection() { BaseColumns baseColumns; mColumns.push_back (RefIdColumn (Columns::ColumnId_Id, ColumnBase::Display_Id, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); baseColumns.mId = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Modification, ColumnBase::Display_RecordState, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true, false)); baseColumns.mModified = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); baseColumns.mType = &mColumns.back(); ModelColumns modelColumns (baseColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_Model, ColumnBase::Display_Mesh)); modelColumns.mModel = &mColumns.back(); NameColumns nameColumns (modelColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_Name, ColumnBase::Display_String)); nameColumns.mName = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Script, ColumnBase::Display_Script)); nameColumns.mScript = &mColumns.back(); InventoryColumns inventoryColumns (nameColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_Icon, ColumnBase::Display_Icon)); inventoryColumns.mIcon = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Weight, ColumnBase::Display_Float)); inventoryColumns.mWeight = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer)); inventoryColumns.mValue = &mColumns.back(); IngredientColumns ingredientColumns (inventoryColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_EffectList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); ingredientColumns.mEffects = &mColumns.back(); std::map ingredientEffectsMap; ingredientEffectsMap.insert(std::make_pair(UniversalId::Type_Ingredient, new IngredEffectRefIdAdapter ())); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), ingredientEffectsMap)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_IngredEffectId)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute)); // nested table PotionColumns potionColumns (inventoryColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_EffectList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); potionColumns.mEffects = &mColumns.back(); // see refidadapterimp.hpp std::map effectsMap; effectsMap.insert(std::make_pair(UniversalId::Type_Potion, new EffectsRefIdAdapter (UniversalId::Type_Potion))); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), effectsMap)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_EffectArea, ColumnBase::Display_String)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); // reuse from light mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_MinMagnitude, ColumnBase::Display_Integer)); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_MaxMagnitude, ColumnBase::Display_Integer)); EnchantableColumns enchantableColumns (inventoryColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_Enchantment, ColumnBase::Display_Enchantment)); enchantableColumns.mEnchantment = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_EnchantmentPoints, ColumnBase::Display_Integer)); enchantableColumns.mEnchantmentPoints = &mColumns.back(); ToolColumns toolsColumns (inventoryColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_Quality, ColumnBase::Display_Float)); toolsColumns.mQuality = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Charges, ColumnBase::Display_Integer)); toolsColumns.mUses = &mColumns.back(); ActorColumns actorsColumns (nameColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_AiHello, ColumnBase::Display_Integer)); actorsColumns.mHello = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_AiFlee, ColumnBase::Display_Integer)); actorsColumns.mFlee = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_AiFight, ColumnBase::Display_Integer)); actorsColumns.mFight = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_AiAlarm, ColumnBase::Display_Integer)); actorsColumns.mAlarm = &mColumns.back(); // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_ActorInventory, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); actorsColumns.mInventory = &mColumns.back(); std::map inventoryMap; inventoryMap.insert(std::make_pair(UniversalId::Type_Npc, new NestedInventoryRefIdAdapter (UniversalId::Type_Npc))); inventoryMap.insert(std::make_pair(UniversalId::Type_Creature, new NestedInventoryRefIdAdapter (UniversalId::Type_Creature))); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), inventoryMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_Referenceable)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_ItemCount, CSMWorld::ColumnBase::Display_Integer)); // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_SpellList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); actorsColumns.mSpells = &mColumns.back(); std::map spellsMap; spellsMap.insert(std::make_pair(UniversalId::Type_Npc, new NestedSpellRefIdAdapter (UniversalId::Type_Npc))); spellsMap.insert(std::make_pair(UniversalId::Type_Creature, new NestedSpellRefIdAdapter (UniversalId::Type_Creature))); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), spellsMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_SpellId, CSMWorld::ColumnBase::Display_Spell)); // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcDestinations, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); actorsColumns.mDestinations = &mColumns.back(); std::map destMap; destMap.insert(std::make_pair(UniversalId::Type_Npc, new NestedTravelRefIdAdapter (UniversalId::Type_Npc))); destMap.insert(std::make_pair(UniversalId::Type_Creature, new NestedTravelRefIdAdapter (UniversalId::Type_Creature))); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), destMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_DestinationCell, CSMWorld::ColumnBase::Display_Cell)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PosX, CSMWorld::ColumnBase::Display_Float)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PosY, CSMWorld::ColumnBase::Display_Float)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_RotX, CSMWorld::ColumnBase::Display_Float)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_RotY, CSMWorld::ColumnBase::Display_Float)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_RotZ, CSMWorld::ColumnBase::Display_Float)); // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_AiPackageList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); actorsColumns.mAiPackages = &mColumns.back(); std::map aiMap; aiMap.insert(std::make_pair(UniversalId::Type_Npc, new ActorAiRefIdAdapter (UniversalId::Type_Npc))); aiMap.insert(std::make_pair(UniversalId::Type_Creature, new ActorAiRefIdAdapter (UniversalId::Type_Creature))); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), aiMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiPackageType, CSMWorld::ColumnBase::Display_AiPackageType)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiWanderDist, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiDuration, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiWanderToD, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Idle1, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Idle2, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Idle3, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Idle4, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Idle5, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Idle6, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Idle7, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Idle8, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiWanderRepeat, CSMWorld::ColumnBase::Display_Boolean)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiActivateName, CSMWorld::ColumnBase::Display_String)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiTargetId, CSMWorld::ColumnBase::Display_String)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiTargetCell, CSMWorld::ColumnBase::Display_String)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PosX, CSMWorld::ColumnBase::Display_Float)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PosY, CSMWorld::ColumnBase::Display_Float)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float)); static const struct { int mName; unsigned int mFlag; } sServiceTable[] = { { Columns::ColumnId_BuysWeapons, ESM::NPC::Weapon}, { Columns::ColumnId_BuysArmor, ESM::NPC::Armor}, { Columns::ColumnId_BuysClothing, ESM::NPC::Clothing}, { Columns::ColumnId_BuysBooks, ESM::NPC::Books}, { Columns::ColumnId_BuysIngredients, ESM::NPC::Ingredients}, { Columns::ColumnId_BuysLockpicks, ESM::NPC::Picks}, { Columns::ColumnId_BuysProbes, ESM::NPC::Probes}, { Columns::ColumnId_BuysLights, ESM::NPC::Lights}, { Columns::ColumnId_BuysApparati, ESM::NPC::Apparatus}, { Columns::ColumnId_BuysRepairItems, ESM::NPC::RepairItem}, { Columns::ColumnId_BuysMiscItems, ESM::NPC::Misc}, { Columns::ColumnId_BuysPotions, ESM::NPC::Potions}, { Columns::ColumnId_BuysMagicItems, ESM::NPC::MagicItems}, { Columns::ColumnId_SellsSpells, ESM::NPC::Spells}, { Columns::ColumnId_Trainer, ESM::NPC::Training}, { Columns::ColumnId_Spellmaking, ESM::NPC::Spellmaking}, { Columns::ColumnId_EnchantingService, ESM::NPC::Enchanting}, { Columns::ColumnId_RepairService, ESM::NPC::Repair}, { -1, 0 } }; for (int i=0; sServiceTable[i].mName!=-1; ++i) { mColumns.push_back (RefIdColumn (sServiceTable[i].mName, ColumnBase::Display_Boolean)); actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag)); } mColumns.push_back (RefIdColumn (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); const RefIdColumn *autoCalc = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType, ColumnBase::Display_ApparatusType)); const RefIdColumn *apparatusType = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_ArmorType, ColumnBase::Display_ArmorType)); const RefIdColumn *armorType = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Health, ColumnBase::Display_Integer)); const RefIdColumn *health = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_ArmorValue, ColumnBase::Display_Integer)); const RefIdColumn *armor = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Scroll, ColumnBase::Display_Boolean)); const RefIdColumn *scroll = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute)); const RefIdColumn *attribute = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_ClothingType, ColumnBase::Display_ClothingType)); const RefIdColumn *clothingType = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_WeightCapacity, ColumnBase::Display_Float)); const RefIdColumn *weightCapacity = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_OrganicContainer, ColumnBase::Display_Boolean)); const RefIdColumn *organic = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Respawn, ColumnBase::Display_Boolean)); const RefIdColumn *respawn = &mColumns.back(); // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_ContainerContent, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); const RefIdColumn *content = &mColumns.back(); std::map contMap; contMap.insert(std::make_pair(UniversalId::Type_Container, new NestedInventoryRefIdAdapter (UniversalId::Type_Container))); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), contMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_Referenceable)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_ItemCount, CSMWorld::ColumnBase::Display_Integer)); CreatureColumns creatureColumns (actorsColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_CreatureType, ColumnBase::Display_CreatureType)); creatureColumns.mType = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Scale, ColumnBase::Display_Float)); creatureColumns.mScale = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_OriginalCreature, ColumnBase::Display_Creature)); creatureColumns.mOriginal = &mColumns.back(); static const struct { int mName; unsigned int mFlag; } sCreatureFlagTable[] = { { Columns::ColumnId_Biped, ESM::Creature::Bipedal }, { Columns::ColumnId_HasWeapon, ESM::Creature::Weapon }, { Columns::ColumnId_Swims, ESM::Creature::Swims }, { Columns::ColumnId_Flies, ESM::Creature::Flies }, { Columns::ColumnId_Walks, ESM::Creature::Walks }, { Columns::ColumnId_Essential, ESM::Creature::Essential }, { Columns::ColumnId_SkeletonBlood, ESM::Creature::Skeleton }, { Columns::ColumnId_MetalBlood, ESM::Creature::Metal }, { -1, 0 } }; // for re-use in NPC records const RefIdColumn *essential = 0; const RefIdColumn *skeletonBlood = 0; const RefIdColumn *metalBlood = 0; for (int i=0; sCreatureFlagTable[i].mName!=-1; ++i) { mColumns.push_back (RefIdColumn (sCreatureFlagTable[i].mName, ColumnBase::Display_Boolean)); creatureColumns.mFlags.insert (std::make_pair (&mColumns.back(), sCreatureFlagTable[i].mFlag)); switch (sCreatureFlagTable[i].mFlag) { case ESM::Creature::Essential: essential = &mColumns.back(); break; case ESM::Creature::Skeleton: skeletonBlood = &mColumns.back(); break; case ESM::Creature::Metal: metalBlood = &mColumns.back(); break; } } creatureColumns.mFlags.insert (std::make_pair (respawn, ESM::Creature::Respawn)); // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_CreatureAttributes, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); creatureColumns.mAttributes = &mColumns.back(); std::map creaAttrMap; creaAttrMap.insert(std::make_pair(UniversalId::Type_Creature, new CreatureAttributesRefIdAdapter())); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), creaAttrMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Attribute, CSMWorld::ColumnBase::Display_Attribute, false, false)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AttributeValue, CSMWorld::ColumnBase::Display_Integer)); // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_CreatureAttack, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); creatureColumns.mAttacks = &mColumns.back(); std::map attackMap; attackMap.insert(std::make_pair(UniversalId::Type_Creature, new CreatureAttackRefIdAdapter())); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), attackMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_CreatureAttack, CSMWorld::ColumnBase::Display_Integer, false, false)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_MinAttack, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_MaxAttack, CSMWorld::ColumnBase::Display_Integer)); // Nested list mColumns.push_back(RefIdColumn (Columns::ColumnId_CreatureMisc, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); creatureColumns.mMisc = &mColumns.back(); std::map creaMiscMap; creaMiscMap.insert(std::make_pair(UniversalId::Type_Creature, new CreatureMiscRefIdAdapter())); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), creaMiscMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Level, CSMWorld::ColumnBase::Display_Integer, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Health, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Mana, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Fatigue, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_SoulPoints, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_CombatState, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_MagicState, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_StealthState, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Gold, CSMWorld::ColumnBase::Display_Integer)); mColumns.push_back (RefIdColumn (Columns::ColumnId_OpenSound, ColumnBase::Display_Sound)); const RefIdColumn *openSound = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_CloseSound, ColumnBase::Display_Sound)); const RefIdColumn *closeSound = &mColumns.back(); LightColumns lightColumns (inventoryColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); lightColumns.mTime = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer)); lightColumns.mRadius = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer)); lightColumns.mColor = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound)); lightColumns.mSound = &mColumns.back(); static const struct { int mName; unsigned int mFlag; } sLightFlagTable[] = { { Columns::ColumnId_Dynamic, ESM::Light::Dynamic }, { Columns::ColumnId_Portable, ESM::Light::Carry }, { Columns::ColumnId_NegativeLight, ESM::Light::Negative }, { Columns::ColumnId_Flickering, ESM::Light::Flicker }, { Columns::ColumnId_SlowFlickering, ESM::Light::FlickerSlow }, { Columns::ColumnId_Pulsing, ESM::Light::Pulse }, { Columns::ColumnId_SlowPulsing, ESM::Light::PulseSlow }, { Columns::ColumnId_Fire, ESM::Light::Fire }, { Columns::ColumnId_OffByDefault, ESM::Light::OffDefault }, { -1, 0 } }; for (int i=0; sLightFlagTable[i].mName!=-1; ++i) { mColumns.push_back (RefIdColumn (sLightFlagTable[i].mName, ColumnBase::Display_Boolean)); lightColumns.mFlags.insert (std::make_pair (&mColumns.back(), sLightFlagTable[i].mFlag)); } mColumns.push_back (RefIdColumn (Columns::ColumnId_IsKey, ColumnBase::Display_Boolean)); const RefIdColumn *key = &mColumns.back(); NpcColumns npcColumns (actorsColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_Race, ColumnBase::Display_Race)); npcColumns.mRace = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Class, ColumnBase::Display_Class)); npcColumns.mClass = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Faction, ColumnBase::Display_Faction)); npcColumns.mFaction = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::Columnid_Hair, ColumnBase::Display_BodyPart)); npcColumns.mHair = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Head, ColumnBase::Display_BodyPart)); npcColumns.mHead = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Female, ColumnBase::Display_Boolean)); npcColumns.mFlags.insert (std::make_pair (&mColumns.back(), ESM::NPC::Female)); npcColumns.mFlags.insert (std::make_pair (essential, ESM::NPC::Essential)); npcColumns.mFlags.insert (std::make_pair (respawn, ESM::NPC::Respawn)); npcColumns.mFlags.insert (std::make_pair (autoCalc, ESM::NPC::Autocalc)); npcColumns.mFlags.insert (std::make_pair (skeletonBlood, ESM::NPC::Skeleton)); npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal)); // Need a way to add a table of stats and values (rather than adding a long list of // entries in the dialogue subview) E.g. attributes+stats(health, mana, fatigue), skills // These needs to be driven from the autocalculated setting. // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcAttributes, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); npcColumns.mAttributes = &mColumns.back(); std::map attrMap; attrMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcAttributesRefIdAdapter())); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), attrMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Attribute, CSMWorld::ColumnBase::Display_Attribute, false, false)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer)); // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcSkills, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); npcColumns.mSkills = &mColumns.back(); std::map skillsMap; skillsMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcSkillsRefIdAdapter())); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), skillsMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Skill, CSMWorld::ColumnBase::Display_SkillId, false, false)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer)); // Nested list mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcMisc, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); npcColumns.mMisc = &mColumns.back(); std::map miscMap; miscMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcMiscRefIdAdapter())); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), miscMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Level, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_NpcFactionID, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Health, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Mana, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Fatigue, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_NpcDisposition, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_NpcReputation, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_NpcRank, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Gold, CSMWorld::ColumnBase::Display_Integer)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_NpcPersistence, CSMWorld::ColumnBase::Display_Boolean)); WeaponColumns weaponColumns (enchantableColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType)); weaponColumns.mType = &mColumns.back(); weaponColumns.mHealth = health; mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponSpeed, ColumnBase::Display_Float)); weaponColumns.mSpeed = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponReach, ColumnBase::Display_Float)); weaponColumns.mReach = &mColumns.back(); for (int i=0; i<3; ++i) { const RefIdColumn **column = i==0 ? weaponColumns.mChop : (i==1 ? weaponColumns.mSlash : weaponColumns.mThrust); for (int j=0; j<2; ++j) { mColumns.push_back ( RefIdColumn (Columns::ColumnId_MinChop+i*2+j, ColumnBase::Display_Integer)); column[j] = &mColumns.back(); } } static const struct { int mName; unsigned int mFlag; } sWeaponFlagTable[] = { { Columns::ColumnId_Magical, ESM::Weapon::Magical }, { Columns::ColumnId_Silver, ESM::Weapon::Silver }, { -1, 0 } }; for (int i=0; sWeaponFlagTable[i].mName!=-1; ++i) { mColumns.push_back (RefIdColumn (sWeaponFlagTable[i].mName, ColumnBase::Display_Boolean)); weaponColumns.mFlags.insert (std::make_pair (&mColumns.back(), sWeaponFlagTable[i].mFlag)); } // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_PartRefList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); const RefIdColumn *partRef = &mColumns.back(); std::map partMap; partMap.insert(std::make_pair(UniversalId::Type_Armor, new BodyPartRefIdAdapter (UniversalId::Type_Armor))); partMap.insert(std::make_pair(UniversalId::Type_Clothing, new BodyPartRefIdAdapter (UniversalId::Type_Clothing))); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), partMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PartRefType, CSMWorld::ColumnBase::Display_PartRefType)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PartRefMale, CSMWorld::ColumnBase::Display_BodyPart)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PartRefFemale, CSMWorld::ColumnBase::Display_BodyPart)); LevListColumns levListColumns (baseColumns); // Nested table mColumns.push_back(RefIdColumn (Columns::ColumnId_LevelledList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); levListColumns.mLevList = &mColumns.back(); std::map levListMap; levListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList, new NestedLevListRefIdAdapter (UniversalId::Type_CreatureLevelledList))); levListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList, new NestedLevListRefIdAdapter (UniversalId::Type_ItemLevelledList))); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), levListMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_LevelledItemId, CSMWorld::ColumnBase::Display_Referenceable)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_LevelledItemLevel, CSMWorld::ColumnBase::Display_Integer)); // Nested list mColumns.push_back(RefIdColumn (Columns::ColumnId_LevelledList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); levListColumns.mNestedListLevList = &mColumns.back(); std::map nestedListLevListMap; nestedListLevListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList, new NestedListLevListRefIdAdapter (UniversalId::Type_CreatureLevelledList))); nestedListLevListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList, new NestedListLevListRefIdAdapter (UniversalId::Type_ItemLevelledList))); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), nestedListLevListMap)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_LevelledItemTypeEach, CSMWorld::ColumnBase::Display_Boolean)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_LevelledItemType, CSMWorld::ColumnBase::Display_Boolean)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_LevelledItemChanceNone, CSMWorld::ColumnBase::Display_Integer)); mAdapters.insert (std::make_pair (UniversalId::Type_Activator, new NameRefIdAdapter (UniversalId::Type_Activator, nameColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Potion, new PotionRefIdAdapter (potionColumns, autoCalc))); mAdapters.insert (std::make_pair (UniversalId::Type_Apparatus, new ApparatusRefIdAdapter (inventoryColumns, apparatusType, toolsColumns.mQuality))); mAdapters.insert (std::make_pair (UniversalId::Type_Armor, new ArmorRefIdAdapter (enchantableColumns, armorType, health, armor, partRef))); mAdapters.insert (std::make_pair (UniversalId::Type_Book, new BookRefIdAdapter (enchantableColumns, scroll, attribute))); mAdapters.insert (std::make_pair (UniversalId::Type_Clothing, new ClothingRefIdAdapter (enchantableColumns, clothingType, partRef))); mAdapters.insert (std::make_pair (UniversalId::Type_Container, new ContainerRefIdAdapter (nameColumns, weightCapacity, organic, respawn, content))); mAdapters.insert (std::make_pair (UniversalId::Type_Creature, new CreatureRefIdAdapter (creatureColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Door, new DoorRefIdAdapter (nameColumns, openSound, closeSound))); mAdapters.insert (std::make_pair (UniversalId::Type_Ingredient, new IngredientRefIdAdapter (ingredientColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, new LevelledListRefIdAdapter ( UniversalId::Type_CreatureLevelledList, levListColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_ItemLevelledList, new LevelledListRefIdAdapter (UniversalId::Type_ItemLevelledList, levListColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Light, new LightRefIdAdapter (lightColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Lockpick, new ToolRefIdAdapter (UniversalId::Type_Lockpick, toolsColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Miscellaneous, new MiscRefIdAdapter (inventoryColumns, key))); mAdapters.insert (std::make_pair (UniversalId::Type_Npc, new NpcRefIdAdapter (npcColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Probe, new ToolRefIdAdapter (UniversalId::Type_Probe, toolsColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Repair, new ToolRefIdAdapter (UniversalId::Type_Repair, toolsColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Static, new ModelRefIdAdapter (UniversalId::Type_Static, modelColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Weapon, new WeaponRefIdAdapter (weaponColumns))); } CSMWorld::RefIdCollection::~RefIdCollection() { for (std::map::iterator iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter) delete iter->second; for (std::vector > >::iterator iter (mNestedAdapters.begin()); iter!=mNestedAdapters.end(); ++iter) { for (std::map::iterator it ((iter->second).begin()); it!=(iter->second).end(); ++it) delete it->second; } } int CSMWorld::RefIdCollection::getSize() const { return mData.getSize(); } std::string CSMWorld::RefIdCollection::getId (int index) const { return getData (index, 0).toString().toUtf8().constData(); } int CSMWorld::RefIdCollection::getIndex (const std::string& id) const { int index = searchId (id); if (index==-1) throw std::runtime_error ("invalid ID: " + id); return index; } int CSMWorld::RefIdCollection::getColumns() const { return mColumns.size(); } const CSMWorld::ColumnBase& CSMWorld::RefIdCollection::getColumn (int column) const { return mColumns.at (column); } QVariant CSMWorld::RefIdCollection::getData (int index, int column) const { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); const RefIdAdapter& adaptor = findAdapter (localIndex.second); return adaptor.getData (&mColumns.at (column), mData, localIndex.first); } QVariant CSMWorld::RefIdCollection::getNestedData (int row, int column, int subRow, int subColumn) const { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex(row); const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); return nestedAdapter.getNestedData(&mColumns.at (column), mData, localIndex.first, subRow, subColumn); } void CSMWorld::RefIdCollection::setData (int index, int column, const QVariant& data) { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); const RefIdAdapter& adaptor = findAdapter (localIndex.second); adaptor.setData (&mColumns.at (column), mData, localIndex.first, data); } void CSMWorld::RefIdCollection::setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); nestedAdapter.setNestedData(&mColumns.at (column), mData, localIndex.first, data, subRow, subColumn); return; } void CSMWorld::RefIdCollection::removeRows (int index, int count) { mData.erase (index, count); } void CSMWorld::RefIdCollection::removeNestedRows(int row, int column, int subRow) { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); nestedAdapter.removeNestedRow(&mColumns.at (column), mData, localIndex.first, subRow); return; } void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type) { mData.appendRecord (type, id, false); } int CSMWorld::RefIdCollection::searchId (const std::string& id) const { RefIdData::LocalIndex localIndex = mData.searchId (id); if (localIndex.first==-1) return -1; return mData.localToGlobalIndex (localIndex); } void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) { mData.getRecord (mData.globalToLocalIndex (index)).assign (record); } void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const std::string& destination, const CSMWorld::UniversalId::Type type) { std::auto_ptr newRecord(mData.getRecord(mData.searchId(origin)).modifiedCopy()); mAdapters.find(type)->second->setId(*newRecord, destination); mData.insertRecord(*newRecord, type, destination); } void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, UniversalId::Type type) { std::string id = findAdapter (type).getId (record); int index = mData.getAppendIndex (type); mData.appendRecord (type, id, false); mData.getRecord (mData.globalToLocalIndex (index)).assign (record); } const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const { return mData.getRecord (mData.searchId (id)); } const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (int index) const { return mData.getRecord (mData.globalToLocalIndex (index)); } void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type) { mData.load(reader, base, type); } int CSMWorld::RefIdCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const { return mData.getAppendIndex (type); } std::vector CSMWorld::RefIdCollection::getIds (bool listDeleted) const { return mData.getIds (listDeleted); } bool CSMWorld::RefIdCollection::reorderRows (int baseIndex, const std::vector& newOrder) { return false; } void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const { mData.save (index, writer); } const CSMWorld::RefIdData& CSMWorld::RefIdCollection::getDataSet() const { return mData; } int CSMWorld::RefIdCollection::getNestedRowsCount(int row, int column) const { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); return nestedAdapter.getNestedRowsCount(&mColumns.at(column), mData, localIndex.first); } int CSMWorld::RefIdCollection::getNestedColumnsCount(int row, int column) const { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); return nestedAdapter.getNestedColumnsCount(&mColumns.at(column), mData); } CSMWorld::NestableColumn *CSMWorld::RefIdCollection::getNestableColumn(int column) { return &mColumns.at(column); } void CSMWorld::RefIdCollection::addNestedRow(int row, int col, int position) { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(col), localIndex.second); nestedAdapter.addNestedRow(&mColumns.at(col), mData, localIndex.first, position); return; } void CSMWorld::RefIdCollection::setNestedTable(int row, int column, const CSMWorld::NestedTableWrapperBase& nestedTable) { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); nestedAdapter.setNestedTable(&mColumns.at(column), mData, localIndex.first, nestedTable); return; } CSMWorld::NestedTableWrapperBase* CSMWorld::RefIdCollection::nestedTable(int row, int column) const { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); return nestedAdapter.nestedTable(&mColumns.at(column), mData, localIndex.first); } const CSMWorld::NestedRefIdAdapterBase& CSMWorld::RefIdCollection::getNestedAdapter(const CSMWorld::ColumnBase &column, UniversalId::Type type) const { for (std::vector > >::const_iterator iter (mNestedAdapters.begin()); iter!=mNestedAdapters.end(); ++iter) { if ((iter->first) == &column) { std::map::const_iterator it = (iter->second).find(type); if (it == (iter->second).end()) throw std::runtime_error("No such type in the nestedadapters"); return *it->second; } } throw std::runtime_error("No such column in the nestedadapters"); } void CSMWorld::RefIdCollection::copyTo (int index, RefIdCollection& target) const { mData.copyTo (index, target.mData); } openmw-openmw-0.38.0/apps/opencs/model/world/refidcollection.hpp000066400000000000000000000120141264522266000247540ustar00rootroot00000000000000#ifndef CSM_WOLRD_REFIDCOLLECTION_H #define CSM_WOLRD_REFIDCOLLECTION_H #include #include #include #include "columnbase.hpp" #include "collectionbase.hpp" #include "nestedcollection.hpp" #include "refiddata.hpp" namespace ESM { class ESMWriter; } namespace CSMWorld { class RefIdAdapter; struct NestedTableWrapperBase; class NestedRefIdAdapterBase; class RefIdColumn : public NestableColumn { bool mEditable; bool mUserEditable; public: RefIdColumn (int columnId, Display displayType, int flag = Flag_Table | Flag_Dialogue, bool editable = true, bool userEditable = true); virtual bool isEditable() const; virtual bool isUserEditable() const; }; class RefIdCollection : public CollectionBase, public NestedCollection { private: RefIdData mData; std::deque mColumns; std::map mAdapters; std::vector > > mNestedAdapters; private: const RefIdAdapter& findAdapter (UniversalId::Type) const; ///< Throws an exception if no adaptor for \a Type can be found. const NestedRefIdAdapterBase& getNestedAdapter(const ColumnBase &column, UniversalId::Type type) const; public: RefIdCollection(); virtual ~RefIdCollection(); virtual int getSize() const; virtual std::string getId (int index) const; virtual int getIndex (const std::string& id) const; virtual int getColumns() const; virtual const ColumnBase& getColumn (int column) const; virtual QVariant getData (int index, int column) const; virtual void setData (int index, int column, const QVariant& data); virtual void removeRows (int index, int count); virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type); virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types virtual int searchId (const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) virtual void replace (int index, const RecordBase& record); ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. virtual void appendRecord (const RecordBase& record, UniversalId::Type type); ///< If the record type does not match, an exception is thrown. /// ///< \param type Will be ignored, unless the collection supports multiple record types virtual const RecordBase& getRecord (const std::string& id) const; virtual const RecordBase& getRecord (int index) const; void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); virtual int getAppendIndex (const std::string& id, UniversalId::Type type) const; ///< \param type Will be ignored, unless the collection supports multiple record types virtual std::vector getIds (bool listDeleted) const; ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list virtual bool reorderRows (int baseIndex, const std::vector& newOrder); ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// /// \return Success? virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const; virtual NestedTableWrapperBase* nestedTable(int row, int column) const; virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable); virtual int getNestedRowsCount(int row, int column) const; virtual int getNestedColumnsCount(int row, int column) const; NestableColumn *getNestableColumn(int column); virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn); virtual void removeNestedRows(int row, int column, int subRow); virtual void addNestedRow(int row, int col, int position); void save (int index, ESM::ESMWriter& writer) const; const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( void copyTo (int index, RefIdCollection& target) const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/refiddata.cpp000066400000000000000000000306171264522266000235360ustar00rootroot00000000000000#include "refiddata.hpp" #include #include CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {} std::string CSMWorld::RefIdData::getRecordId(const CSMWorld::RefIdData::LocalIndex &index) const { std::map::const_iterator found = mRecordContainers.find (index.second); if (found == mRecordContainers.end()) throw std::logic_error ("invalid local index type"); return found->second->getId(index.first); } CSMWorld::RefIdData::RefIdData() { mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Potion, &mPotions)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Apparatus, &mApparati)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Armor, &mArmors)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Book, &mBooks)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Clothing, &mClothing)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Container, &mContainers)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Creature, &mCreatures)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Door, &mDoors)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Ingredient, &mIngredients)); mRecordContainers.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, &mCreatureLevelledLists)); mRecordContainers.insert (std::make_pair (UniversalId::Type_ItemLevelledList, &mItemLevelledLists)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Light, &mLights)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Lockpick, &mLockpicks)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Miscellaneous, &mMiscellaneous)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Npc, &mNpcs)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Probe, &mProbes)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Repair, &mRepairs)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Static, &mStatics)); mRecordContainers.insert (std::make_pair (UniversalId::Type_Weapon, &mWeapons)); } CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex (int index) const { for (std::map::const_iterator iter ( mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) { if (indexsecond->getSize()) return LocalIndex (index, iter->first); index -= iter->second->getSize(); } throw std::runtime_error ("RefIdData index out of range"); } int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index) const { std::map::const_iterator end = mRecordContainers.find (index.second); if (end==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); int globalIndex = index.first; for (std::map::const_iterator iter ( mRecordContainers.begin()); iter!=end; ++iter) globalIndex += iter->second->getSize(); return globalIndex; } CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId ( const std::string& id) const { std::string id2 = Misc::StringUtils::lowerCase (id); std::map >::const_iterator iter = mIndex.find (id2); if (iter==mIndex.end()) return std::make_pair (-1, CSMWorld::UniversalId::Type_None); return iter->second; } void CSMWorld::RefIdData::erase (int index, int count) { LocalIndex localIndex = globalToLocalIndex (index); std::map::const_iterator iter = mRecordContainers.find (localIndex.second); while (count>0 && iter!=mRecordContainers.end()) { int size = iter->second->getSize(); if (localIndex.first+count>size) { erase (localIndex, size-localIndex.first); count -= size-localIndex.first; ++iter; if (iter==mRecordContainers.end()) throw std::runtime_error ("invalid count value for erase operation"); localIndex.first = 0; localIndex.second = iter->first; } else { erase (localIndex, count); count = 0; } } } const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) const { std::map::const_iterator iter = mRecordContainers.find (index.second); if (iter==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); return iter->second->getRecord (index.first); } CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) { std::map::iterator iter = mRecordContainers.find (index.second); if (iter==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); return iter->second->getRecord (index.first); } void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id, bool base) { std::map::iterator iter = mRecordContainers.find (type); if (iter==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); iter->second->appendRecord (id, base); mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), LocalIndex (iter->second->getSize()-1, type))); } int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const { int index = 0; for (std::map::const_iterator iter ( mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) { index += iter->second->getSize(); if (type==iter->first) break; } return index; } void CSMWorld::RefIdData::load (ESM::ESMReader& reader, bool base, CSMWorld::UniversalId::Type type) { std::map::iterator found = mRecordContainers.find (type); if (found == mRecordContainers.end()) throw std::logic_error ("Invalid Referenceable ID type"); int index = found->second->load(reader, base); if (index != -1) { LocalIndex localIndex = LocalIndex(index, type); if (base && getRecord(localIndex).mState == RecordBase::State_Deleted) { erase(localIndex, 1); } else { mIndex[Misc::StringUtils::lowerCase(getRecordId(localIndex))] = localIndex; } } } void CSMWorld::RefIdData::erase (const LocalIndex& index, int count) { std::map::iterator iter = mRecordContainers.find (index.second); if (iter==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); for (int i=index.first; i::iterator result = mIndex.find (Misc::StringUtils::lowerCase (iter->second->getId (i))); if (result!=mIndex.end()) mIndex.erase (result); } // Adjust the local indexes to avoid gaps between them after removal of records int recordIndex = index.first + count; int recordCount = iter->second->getSize(); while (recordIndex < recordCount) { std::map::iterator recordIndexFound = mIndex.find(Misc::StringUtils::lowerCase(iter->second->getId(recordIndex))); if (recordIndexFound != mIndex.end()) { recordIndexFound->second.first -= count; } ++recordIndex; } iter->second->erase (index.first, count); } int CSMWorld::RefIdData::getSize() const { return mIndex.size(); } std::vector CSMWorld::RefIdData::getIds (bool listDeleted) const { std::vector ids; for (std::map::const_iterator iter (mIndex.begin()); iter!=mIndex.end(); ++iter) { if (listDeleted || !getRecord (iter->second).isDeleted()) { std::map::const_iterator container = mRecordContainers.find (iter->second.second); if (container==mRecordContainers.end()) throw std::logic_error ("Invalid referenceable ID type"); ids.push_back (container->second->getId (iter->second.first)); } } return ids; } void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const { LocalIndex localIndex = globalToLocalIndex (index); std::map::const_iterator iter = mRecordContainers.find (localIndex.second); if (iter==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); iter->second->save (localIndex.first, writer); } const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() const { return mBooks; } const CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getActivators() const { return mActivators; } const CSMWorld::RefIdDataContainer< ESM::Potion >& CSMWorld::RefIdData::getPotions() const { return mPotions; } const CSMWorld::RefIdDataContainer< ESM::Apparatus >& CSMWorld::RefIdData::getApparati() const { return mApparati; } const CSMWorld::RefIdDataContainer< ESM::Armor >& CSMWorld::RefIdData::getArmors() const { return mArmors; } const CSMWorld::RefIdDataContainer< ESM::Clothing >& CSMWorld::RefIdData::getClothing() const { return mClothing; } const CSMWorld::RefIdDataContainer< ESM::Container >& CSMWorld::RefIdData::getContainers() const { return mContainers; } const CSMWorld::RefIdDataContainer< ESM::Creature >& CSMWorld::RefIdData::getCreatures() const { return mCreatures; } const CSMWorld::RefIdDataContainer< ESM::Door >& CSMWorld::RefIdData::getDoors() const { return mDoors; } const CSMWorld::RefIdDataContainer< ESM::Ingredient >& CSMWorld::RefIdData::getIngredients() const { return mIngredients; } const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& CSMWorld::RefIdData::getCreatureLevelledLists() const { return mCreatureLevelledLists; } const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& CSMWorld::RefIdData::getItemLevelledList() const { return mItemLevelledLists; } const CSMWorld::RefIdDataContainer< ESM::Light >& CSMWorld::RefIdData::getLights() const { return mLights; } const CSMWorld::RefIdDataContainer< ESM::Lockpick >& CSMWorld::RefIdData::getLocpicks() const { return mLockpicks; } const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& CSMWorld::RefIdData::getMiscellaneous() const { return mMiscellaneous; } const CSMWorld::RefIdDataContainer< ESM::NPC >& CSMWorld::RefIdData::getNPCs() const { return mNpcs; } const CSMWorld::RefIdDataContainer< ESM::Weapon >& CSMWorld::RefIdData::getWeapons() const { return mWeapons; } const CSMWorld::RefIdDataContainer< ESM::Probe >& CSMWorld::RefIdData::getProbes() const { return mProbes; } const CSMWorld::RefIdDataContainer< ESM::Repair >& CSMWorld::RefIdData::getRepairs() const { return mRepairs; } const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStatics() const { return mStatics; } void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id) { std::map::iterator iter = mRecordContainers.find (type); if (iter==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); iter->second->insertRecord(record); mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), LocalIndex (iter->second->getSize()-1, type))); } void CSMWorld::RefIdData::copyTo (int index, RefIdData& target) const { LocalIndex localIndex = globalToLocalIndex (index); RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second; std::string id = source->getId (localIndex.first); std::auto_ptr newRecord (source->getRecord (localIndex.first).modifiedCopy()); target.insertRecord (*newRecord, localIndex.second, id); } openmw-openmw-0.38.0/apps/opencs/model/world/refiddata.hpp000066400000000000000000000243201264522266000235350ustar00rootroot00000000000000#ifndef CSM_WOLRD_REFIDDATA_H #define CSM_WOLRD_REFIDDATA_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "record.hpp" #include "universalid.hpp" namespace ESM { class ESMReader; } namespace CSMWorld { struct RefIdDataContainerBase { virtual ~RefIdDataContainerBase(); virtual int getSize() const = 0; virtual const RecordBase& getRecord (int index) const = 0; virtual RecordBase& getRecord (int index)= 0; virtual void appendRecord (const std::string& id, bool base) = 0; virtual void insertRecord (RecordBase& record) = 0; virtual int load (ESM::ESMReader& reader, bool base) = 0; ///< \return index of a loaded record or -1 if no record was loaded virtual void erase (int index, int count) = 0; virtual std::string getId (int index) const = 0; virtual void save (int index, ESM::ESMWriter& writer) const = 0; }; template struct RefIdDataContainer : public RefIdDataContainerBase { std::vector > mContainer; virtual int getSize() const; virtual const RecordBase& getRecord (int index) const; virtual RecordBase& getRecord (int index); virtual void appendRecord (const std::string& id, bool base); virtual void insertRecord (RecordBase& record); virtual int load (ESM::ESMReader& reader, bool base); ///< \return index of a loaded record or -1 if no record was loaded virtual void erase (int index, int count); virtual std::string getId (int index) const; virtual void save (int index, ESM::ESMWriter& writer) const; }; template void RefIdDataContainer::insertRecord(RecordBase& record) { Record& newRecord = dynamic_cast& >(record); mContainer.push_back(newRecord); } template int RefIdDataContainer::getSize() const { return static_cast (mContainer.size()); } template const RecordBase& RefIdDataContainer::getRecord (int index) const { return mContainer.at (index); } template RecordBase& RefIdDataContainer::getRecord (int index) { return mContainer.at (index); } template void RefIdDataContainer::appendRecord (const std::string& id, bool base) { Record record; record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; record.mBase.mId = id; record.mModified.mId = id; (base ? record.mBase : record.mModified).blank(); mContainer.push_back (record); } template int RefIdDataContainer::load (ESM::ESMReader& reader, bool base) { RecordT record; bool isDeleted = false; record.load(reader, isDeleted); int index = 0; int numRecords = static_cast(mContainer.size()); for (; index < numRecords; ++index) { if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId)) { break; } } if (isDeleted) { if (index == numRecords) { // deleting a record that does not exist // ignore it for now /// \todo report the problem to the user return -1; } // Flag the record as Deleted even for a base content file. // RefIdData is responsible for its erasure. mContainer[index].mState = RecordBase::State_Deleted; } else { if (index == numRecords) { appendRecord(record.mId, base); if (base) { mContainer.back().mBase = record; } else { mContainer.back().mModified = record; } } else if (!base) { mContainer[index].setModified(record); } } return index; } template void RefIdDataContainer::erase (int index, int count) { if (index<0 || index+count>getSize()) throw std::runtime_error ("invalid RefIdDataContainer index"); mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count); } template std::string RefIdDataContainer::getId (int index) const { return mContainer.at (index).get().mId; } template void RefIdDataContainer::save (int index, ESM::ESMWriter& writer) const { Record record = mContainer.at(index); if (record.isModified() || record.mState == RecordBase::State_Deleted) { RecordT esmRecord = record.get(); writer.startRecord(esmRecord.sRecordId); esmRecord.save(writer, record.mState == RecordBase::State_Deleted); writer.endRecord(esmRecord.sRecordId); } } class RefIdData { public: typedef std::pair LocalIndex; private: RefIdDataContainer mActivators; RefIdDataContainer mPotions; RefIdDataContainer mApparati; RefIdDataContainer mArmors; RefIdDataContainer mBooks; RefIdDataContainer mClothing; RefIdDataContainer mContainers; RefIdDataContainer mCreatures; RefIdDataContainer mDoors; RefIdDataContainer mIngredients; RefIdDataContainer mCreatureLevelledLists; RefIdDataContainer mItemLevelledLists; RefIdDataContainer mLights; RefIdDataContainer mLockpicks; RefIdDataContainer mMiscellaneous; RefIdDataContainer mNpcs; RefIdDataContainer mProbes; RefIdDataContainer mRepairs; RefIdDataContainer mStatics; RefIdDataContainer mWeapons; std::map mIndex; std::map mRecordContainers; void erase (const LocalIndex& index, int count); ///< Must not spill over into another type. std::string getRecordId(const LocalIndex &index) const; public: RefIdData(); LocalIndex globalToLocalIndex (int index) const; int localToGlobalIndex (const LocalIndex& index) const; LocalIndex searchId (const std::string& id) const; void erase (int index, int count); void insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id); const RecordBase& getRecord (const LocalIndex& index) const; RecordBase& getRecord (const LocalIndex& index); void appendRecord (UniversalId::Type type, const std::string& id, bool base); int getAppendIndex (UniversalId::Type type) const; void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); int getSize() const; std::vector getIds (bool listDeleted = true) const; ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list void save (int index, ESM::ESMWriter& writer) const; //RECORD CONTAINERS ACCESS METHODS const RefIdDataContainer& getBooks() const; const RefIdDataContainer& getActivators() const; const RefIdDataContainer& getPotions() const; const RefIdDataContainer& getApparati() const; const RefIdDataContainer& getArmors() const; const RefIdDataContainer& getClothing() const; const RefIdDataContainer& getContainers() const; const RefIdDataContainer& getCreatures() const; const RefIdDataContainer& getDoors() const; const RefIdDataContainer& getIngredients() const; const RefIdDataContainer& getCreatureLevelledLists() const; const RefIdDataContainer& getItemLevelledList() const; const RefIdDataContainer& getLights() const; const RefIdDataContainer& getLocpicks() const; const RefIdDataContainer& getMiscellaneous() const; const RefIdDataContainer& getNPCs() const; const RefIdDataContainer& getWeapons() const; const RefIdDataContainer& getProbes() const; const RefIdDataContainer& getRepairs() const; const RefIdDataContainer& getStatics() const; void copyTo (int index, RefIdData& target) const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/regionmap.cpp000066400000000000000000000337101264522266000235710ustar00rootroot00000000000000#include "regionmap.hpp" #include #include #include #include #include "data.hpp" #include "universalid.hpp" CSMWorld::RegionMap::CellDescription::CellDescription() : mDeleted (false) {} CSMWorld::RegionMap::CellDescription::CellDescription (const Record& cell) { const Cell& cell2 = cell.get(); if (!cell2.isExterior()) throw std::logic_error ("Interior cell in region map"); mDeleted = cell.isDeleted(); mRegion = cell2.mRegion; mName = cell2.mName; } CSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { return mMin.move (index.column(), mMax.getY()-mMin.getY() - index.row()-1); } QModelIndex CSMWorld::RegionMap::getIndex (const CellCoordinates& index) const { // I hate you, Qt API naming scheme! return QAbstractTableModel::index (mMax.getY()-mMin.getY() - (index.getY()-mMin.getY())-1, index.getX()-mMin.getX()); } CSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex (const Cell& cell) const { std::istringstream stream (cell.mId); char ignore; int x = 0; int y = 0; stream >> ignore >> x >> y; return CellCoordinates (x, y); } void CSMWorld::RegionMap::buildRegions() { const IdCollection& regions = mData.getRegions(); int size = regions.getSize(); for (int i=0; i& region = regions.getRecord (i); if (!region.isDeleted()) mColours.insert (std::make_pair (Misc::StringUtils::lowerCase (region.get().mId), region.get().mMapColor)); } } void CSMWorld::RegionMap::buildMap() { const IdCollection& cells = mData.getCells(); int size = cells.getSize(); for (int i=0; i& cell = cells.getRecord (i); const Cell& cell2 = cell.get(); if (cell2.isExterior()) { CellDescription description (cell); CellCoordinates index = getIndex (cell2); mMap.insert (std::make_pair (index, description)); } } std::pair mapSize = getSize(); mMin = mapSize.first; mMax = mapSize.second; } void CSMWorld::RegionMap::addCell (const CellCoordinates& index, const CellDescription& description) { std::map::iterator cell = mMap.find (index); if (cell!=mMap.end()) { cell->second = description; } else { updateSize(); mMap.insert (std::make_pair (index, description)); } QModelIndex index2 = getIndex (index); dataChanged (index2, index2); } void CSMWorld::RegionMap::addCells (int start, int end) { const IdCollection& cells = mData.getCells(); for (int i=start; i<=end; ++i) { const Record& cell = cells.getRecord (i); const Cell& cell2 = cell.get(); if (cell2.isExterior()) { CellCoordinates index = getIndex (cell2); CellDescription description (cell); addCell (index, description); } } } void CSMWorld::RegionMap::removeCell (const CellCoordinates& index) { std::map::iterator cell = mMap.find (index); if (cell!=mMap.end()) { mMap.erase (cell); QModelIndex index2 = getIndex (index); dataChanged (index2, index2); updateSize(); } } void CSMWorld::RegionMap::addRegion (const std::string& region, unsigned int colour) { mColours[Misc::StringUtils::lowerCase (region)] = colour; } void CSMWorld::RegionMap::removeRegion (const std::string& region) { std::map::iterator iter ( mColours.find (Misc::StringUtils::lowerCase (region))); if (iter!=mColours.end()) mColours.erase (iter); } void CSMWorld::RegionMap::updateRegions (const std::vector& regions) { std::vector regions2 (regions); std::for_each (regions2.begin(), regions2.end(), Misc::StringUtils::lowerCaseInPlace); std::sort (regions2.begin(), regions2.end()); for (std::map::const_iterator iter (mMap.begin()); iter!=mMap.end(); ++iter) { if (!iter->second.mRegion.empty() && std::find (regions2.begin(), regions2.end(), Misc::StringUtils::lowerCase (iter->second.mRegion))!=regions2.end()) { QModelIndex index = getIndex (iter->first); dataChanged (index, index); } } } void CSMWorld::RegionMap::updateSize() { std::pair size = getSize(); if (int diff = size.first.getX() - mMin.getX()) { beginInsertColumns (QModelIndex(), 0, std::abs (diff)-1); mMin = CellCoordinates (size.first.getX(), mMin.getY()); endInsertColumns(); } if (int diff = size.first.getY() - mMin.getY()) { beginInsertRows (QModelIndex(), 0, std::abs (diff)-1); mMin = CellCoordinates (mMin.getX(), size.first.getY()); endInsertRows(); } if (int diff = size.second.getX() - mMax.getX()) { int columns = columnCount(); if (diff>0) beginInsertColumns (QModelIndex(), columns, columns+diff-1); else beginRemoveColumns (QModelIndex(), columns+diff, columns-1); mMax = CellCoordinates (size.second.getX(), mMax.getY()); endInsertColumns(); } if (int diff = size.second.getY() - mMax.getY()) { int rows = rowCount(); if (diff>0) beginInsertRows (QModelIndex(), rows, rows+diff-1); else beginRemoveRows (QModelIndex(), rows+diff, rows-1); mMax = CellCoordinates (mMax.getX(), size.second.getY()); endInsertRows(); } } std::pair CSMWorld::RegionMap::getSize() const { const IdCollection& cells = mData.getCells(); int size = cells.getSize(); CellCoordinates min (0, 0); CellCoordinates max (0, 0); for (int i=0; i& cell = cells.getRecord (i); const Cell& cell2 = cell.get(); if (cell2.isExterior()) { CellCoordinates index = getIndex (cell2); if (min==max) { min = index; max = min.move (1, 1); } else { if (index.getX()=max.getX()) max = CellCoordinates (index.getX()+1, max.getY()); if (index.getY()=max.getY()) max = CellCoordinates (max.getX(), index.getY() + 1); } } } return std::make_pair (min, max); } CSMWorld::RegionMap::RegionMap (Data& data) : mData (data) { buildRegions(); buildMap(); QAbstractItemModel *regions = data.getTableModel (UniversalId (UniversalId::Type_Regions)); connect (regions, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (regionsAboutToBeRemoved (const QModelIndex&, int, int))); connect (regions, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (regionsInserted (const QModelIndex&, int, int))); connect (regions, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (regionsChanged (const QModelIndex&, const QModelIndex&))); QAbstractItemModel *cells = data.getTableModel (UniversalId (UniversalId::Type_Cells)); connect (cells, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (cellsAboutToBeRemoved (const QModelIndex&, int, int))); connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (cellsInserted (const QModelIndex&, int, int))); connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (cellsChanged (const QModelIndex&, const QModelIndex&))); } int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const { if (parent.isValid()) return 0; return mMax.getY()-mMin.getY(); } int CSMWorld::RegionMap::columnCount (const QModelIndex& parent) const { if (parent.isValid()) return 0; return mMax.getX()-mMin.getX(); } QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const { if (role==Qt::SizeHintRole) return QSize (16, 16); if (role==Qt::BackgroundRole) { /// \todo GUI class in non-GUI code. Needs to be addressed eventually. std::map::const_iterator cell = mMap.find (getIndex (index)); if (cell!=mMap.end()) { if (cell->second.mDeleted) return QBrush (Qt::red, Qt::DiagCrossPattern); std::map::const_iterator iter = mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion)); if (iter!=mColours.end()) return QBrush (QColor (iter->second & 0xff, (iter->second >> 8) & 0xff, (iter->second >> 16) & 0xff)); if (cell->second.mRegion.empty()) return QBrush (Qt::Dense6Pattern); // no region return QBrush (Qt::red, Qt::Dense6Pattern); // invalid region } return QBrush (Qt::DiagCrossPattern); } if (role==Qt::ToolTipRole) { CellCoordinates cellIndex = getIndex (index); std::ostringstream stream; stream << cellIndex; std::map::const_iterator cell = mMap.find (cellIndex); if (cell!=mMap.end()) { if (!cell->second.mName.empty()) stream << " " << cell->second.mName; if (cell->second.mDeleted) stream << " (deleted)"; if (!cell->second.mRegion.empty()) { stream << "
"; std::map::const_iterator iter = mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion)); if (iter!=mColours.end()) stream << cell->second.mRegion; else stream << "" << cell->second.mRegion << ""; } } else stream << " (no cell)"; return QString::fromUtf8 (stream.str().c_str()); } if (role==Role_Region) { CellCoordinates cellIndex = getIndex (index); std::map::const_iterator cell = mMap.find (cellIndex); if (cell!=mMap.end() && !cell->second.mRegion.empty()) return QString::fromUtf8 (Misc::StringUtils::lowerCase (cell->second.mRegion).c_str()); } if (role==Role_CellId) { CellCoordinates cellIndex = getIndex (index); std::ostringstream stream; stream << "#" << cellIndex.getX() << " " << cellIndex.getY(); return QString::fromUtf8 (stream.str().c_str()); } return QVariant(); } Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } void CSMWorld::RegionMap::regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end) { std::vector update; const IdCollection& regions = mData.getRegions(); for (int i=start; i<=end; ++i) { const Record& region = regions.getRecord (i); update.push_back (region.get().mId); removeRegion (region.get().mId); } updateRegions (update); } void CSMWorld::RegionMap::regionsInserted (const QModelIndex& parent, int start, int end) { std::vector update; const IdCollection& regions = mData.getRegions(); for (int i=start; i<=end; ++i) { const Record& region = regions.getRecord (i); if (!region.isDeleted()) { update.push_back (region.get().mId); addRegion (region.get().mId, region.get().mMapColor); } } updateRegions (update); } void CSMWorld::RegionMap::regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { // Note: At this point an additional check could be inserted to see if there is any change to the // columns we are interested in. If not we can exit the function here and avoid all updating. std::vector update; const IdCollection& regions = mData.getRegions(); for (int i=topLeft.row(); i<=bottomRight.column(); ++i) { const Record& region = regions.getRecord (i); update.push_back (region.get().mId); if (!region.isDeleted()) addRegion (region.get().mId, region.get().mMapColor); else removeRegion (region.get().mId); } updateRegions (update); } void CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end) { const IdCollection& cells = mData.getCells(); for (int i=start; i<=end; ++i) { const Record& cell = cells.getRecord (i); const Cell& cell2 = cell.get(); if (cell2.isExterior()) removeCell (getIndex (cell2)); } } void CSMWorld::RegionMap::cellsInserted (const QModelIndex& parent, int start, int end) { addCells (start, end); } void CSMWorld::RegionMap::cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { // Note: At this point an additional check could be inserted to see if there is any change to the // columns we are interested in. If not we can exit the function here and avoid all updating. addCells (topLeft.row(), bottomRight.row()); } openmw-openmw-0.38.0/apps/opencs/model/world/regionmap.hpp000066400000000000000000000074251264522266000236020ustar00rootroot00000000000000#ifndef CSM_WOLRD_REGIONMAP_H #define CSM_WOLRD_REGIONMAP_H #include #include #include #include #include "record.hpp" #include "cell.hpp" #include "cellcoordinates.hpp" namespace CSMWorld { class Data; /// \brief Model for the region map /// /// This class does not holds any record data (other than for the purpose of buffering). class RegionMap : public QAbstractTableModel { Q_OBJECT public: enum Role { Role_Region = Qt::UserRole, Role_CellId = Qt::UserRole+1 }; private: struct CellDescription { bool mDeleted; std::string mRegion; std::string mName; CellDescription(); CellDescription (const Record& cell); }; Data& mData; std::map mMap; CellCoordinates mMin; ///< inclusive CellCoordinates mMax; ///< exclusive std::map mColours; ///< region ID, colour (RGBA) CellCoordinates getIndex (const QModelIndex& index) const; ///< Translates a Qt model index into a cell index (which can contain negative components) QModelIndex getIndex (const CellCoordinates& index) const; CellCoordinates getIndex (const Cell& cell) const; void buildRegions(); void buildMap(); void addCell (const CellCoordinates& index, const CellDescription& description); ///< May be called on a cell that is already in the map (in which case an update is // performed) void addCells (int start, int end); void removeCell (const CellCoordinates& index); ///< May be called on a cell that is not in the map (in which case the call is ignored) void addRegion (const std::string& region, unsigned int colour); ///< May be called on a region that is already listed (in which case an update is /// performed) /// /// \note This function does not update the region map. void removeRegion (const std::string& region); ///< May be called on a region that is not listed (in which case the call is ignored) /// /// \note This function does not update the region map. void updateRegions (const std::vector& regions); ///< Update cells affected by the listed regions void updateSize(); std::pair getSize() const; public: RegionMap (Data& data); virtual int rowCount (const QModelIndex& parent = QModelIndex()) const; virtual int columnCount (const QModelIndex& parent = QModelIndex()) const; virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const; ///< \note Calling this function with role==Role_CellId may return the ID of a cell /// that does not exist. virtual Qt::ItemFlags flags (const QModelIndex& index) const; private slots: void regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end); void regionsInserted (const QModelIndex& parent, int start, int end); void regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end); void cellsInserted (const QModelIndex& parent, int start, int end); void cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/resources.cpp000066400000000000000000000046511264522266000236240ustar00rootroot00000000000000#include "resources.hpp" #include #include #include #include #include CSMWorld::Resources::Resources (const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type, const char * const *extensions) : mBaseDirectory (baseDirectory), mType (type) { int baseSize = mBaseDirectory.size(); const std::map& index = vfs->getIndex(); for (std::map::const_iterator it = index.begin(); it != index.end(); ++it) { std::string filepath = it->first; if (static_cast (filepath.size()) (mFiles.size())-1)); } } int CSMWorld::Resources::getSize() const { return mFiles.size(); } std::string CSMWorld::Resources::getId (int index) const { return mFiles.at (index); } int CSMWorld::Resources::getIndex (const std::string& id) const { int index = searchId (id); if (index==-1) { std::ostringstream stream; stream << "Invalid resource: " << mBaseDirectory << '/' << id; throw std::runtime_error (stream.str().c_str()); } return index; } int CSMWorld::Resources::searchId (const std::string& id) const { std::string id2 = Misc::StringUtils::lowerCase (id); std::replace (id2.begin(), id2.end(), '\\', '/'); std::map::const_iterator iter = mIndex.find (id2); if (iter==mIndex.end()) return -1; return iter->second; } CSMWorld::UniversalId::Type CSMWorld::Resources::getType() const { return mType; } openmw-openmw-0.38.0/apps/opencs/model/world/resources.hpp000066400000000000000000000016001264522266000236200ustar00rootroot00000000000000#ifndef CSM_WOLRD_RESOURCES_H #define CSM_WOLRD_RESOURCES_H #include #include #include #include "universalid.hpp" namespace VFS { class Manager; } namespace CSMWorld { class Resources { std::map mIndex; std::vector mFiles; std::string mBaseDirectory; UniversalId::Type mType; public: /// \param type Type of resources in this table. Resources (const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type, const char * const *extensions = 0); int getSize() const; std::string getId (int index) const; int getIndex (const std::string& id) const; int searchId (const std::string& id) const; UniversalId::Type getType() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/resourcesmanager.cpp000066400000000000000000000027451264522266000251610ustar00rootroot00000000000000#include "resourcesmanager.hpp" #include CSMWorld::ResourcesManager::ResourcesManager() : mVFS(NULL) { } void CSMWorld::ResourcesManager::addResources (const Resources& resources) { mResources.insert (std::make_pair (resources.getType(), resources)); mResources.insert (std::make_pair (UniversalId::getParentType (resources.getType()), resources)); } void CSMWorld::ResourcesManager::setVFS(const VFS::Manager *vfs) { mVFS = vfs; mResources.clear(); // maybe we could go over the osgDB::Registry to list all supported node formats static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", 0 }; addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, sMeshTypes)); addResources (Resources (vfs, "icons", UniversalId::Type_Icon)); addResources (Resources (vfs, "music", UniversalId::Type_Music)); addResources (Resources (vfs, "sound", UniversalId::Type_SoundRes)); addResources (Resources (vfs, "textures", UniversalId::Type_Texture)); addResources (Resources (vfs, "videos", UniversalId::Type_Video)); } const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const { return mVFS; } const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const { std::map::const_iterator iter = mResources.find (type); if (iter==mResources.end()) throw std::logic_error ("Unknown resource type"); return iter->second; } openmw-openmw-0.38.0/apps/opencs/model/world/resourcesmanager.hpp000066400000000000000000000012111264522266000251510ustar00rootroot00000000000000#ifndef CSM_WOLRD_RESOURCESMANAGER_H #define CSM_WOLRD_RESOURCESMANAGER_H #include #include "universalid.hpp" #include "resources.hpp" namespace VFS { class Manager; } namespace CSMWorld { class ResourcesManager { std::map mResources; const VFS::Manager* mVFS; private: void addResources (const Resources& resources); public: ResourcesManager(); const VFS::Manager* getVFS() const; void setVFS(const VFS::Manager* vfs); const Resources& get (UniversalId::Type type) const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/resourcetable.cpp000066400000000000000000000070511264522266000244460ustar00rootroot00000000000000#include "resourcetable.hpp" #include #include "resources.hpp" #include "columnbase.hpp" #include "universalid.hpp" CSMWorld::ResourceTable::ResourceTable (const Resources *resources, unsigned int features) : IdTableBase (features | Feature_Constant), mResources (resources) {} CSMWorld::ResourceTable::~ResourceTable() {} int CSMWorld::ResourceTable::rowCount (const QModelIndex & parent) const { if (parent.isValid()) return 0; return mResources->getSize(); } int CSMWorld::ResourceTable::columnCount (const QModelIndex & parent) const { if (parent.isValid()) return 0; return 2; // ID, type } QVariant CSMWorld::ResourceTable::data (const QModelIndex & index, int role) const { if (role!=Qt::DisplayRole) return QVariant(); if (index.column()==0) return QString::fromUtf8 (mResources->getId (index.row()).c_str()); if (index.column()==1) return static_cast (mResources->getType()); throw std::logic_error ("Invalid column in resource table"); } QVariant CSMWorld::ResourceTable::headerData (int section, Qt::Orientation orientation, int role ) const { if (orientation==Qt::Vertical) return QVariant(); if (role==ColumnBase::Role_Flags) return section==0 ? ColumnBase::Flag_Table : 0; switch (section) { case 0: if (role==Qt::DisplayRole) return Columns::getName (Columns::ColumnId_Id).c_str(); if (role==ColumnBase::Role_Display) return ColumnBase::Display_Id; break; case 1: if (role==Qt::DisplayRole) return Columns::getName (Columns::ColumnId_RecordType).c_str(); if (role==ColumnBase::Role_Display) return ColumnBase::Display_Integer; break; } return QVariant(); } bool CSMWorld::ResourceTable::setData ( const QModelIndex &index, const QVariant &value, int role) { return false; } Qt::ItemFlags CSMWorld::ResourceTable::flags (const QModelIndex & index) const { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } QModelIndex CSMWorld::ResourceTable::index (int row, int column, const QModelIndex& parent) const { if (parent.isValid()) return QModelIndex(); if (row<0 || row>=mResources->getSize()) return QModelIndex(); if (column<0 || column>1) return QModelIndex(); return createIndex (row, column); } QModelIndex CSMWorld::ResourceTable::parent (const QModelIndex& index) const { return QModelIndex(); } QModelIndex CSMWorld::ResourceTable::getModelIndex (const std::string& id, int column) const { return index (mResources->getIndex (id), column); } int CSMWorld::ResourceTable::searchColumnIndex (Columns::ColumnId id) const { if (id==Columns::ColumnId_Id) return 0; if (id==Columns::ColumnId_RecordType) return 1; return -1; } int CSMWorld::ResourceTable::findColumnIndex (Columns::ColumnId id) const { int index = searchColumnIndex (id); if (index==-1) throw std::logic_error ("invalid column index"); return index; } std::pair CSMWorld::ResourceTable::view (int row) const { return std::make_pair (UniversalId::Type_None, ""); } bool CSMWorld::ResourceTable::isDeleted (const std::string& id) const { return false; } int CSMWorld::ResourceTable::getColumnId (int column) const { switch (column) { case 0: return Columns::ColumnId_Id; case 1: return Columns::ColumnId_RecordType; } return -1; } openmw-openmw-0.38.0/apps/opencs/model/world/resourcetable.hpp000066400000000000000000000041531264522266000244530ustar00rootroot00000000000000#ifndef CSM_WOLRD_RESOURCETABLE_H #define CSM_WOLRD_RESOURCETABLE_H #include "idtablebase.hpp" namespace CSMWorld { class Resources; class ResourceTable : public IdTableBase { const Resources *mResources; public: /// \note The feature Feature_Constant will be added implicitly. ResourceTable (const Resources *resources, unsigned int features = 0); virtual ~ResourceTable(); virtual int rowCount (const QModelIndex & parent = QModelIndex()) const; virtual int columnCount (const QModelIndex & parent = QModelIndex()) const; virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); virtual Qt::ItemFlags flags (const QModelIndex & index) const; virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) const; virtual QModelIndex parent (const QModelIndex& index) const; virtual QModelIndex getModelIndex (const std::string& id, int column) const; /// Return index of column with the given \a id. If no such column exists, -1 is /// returned. virtual int searchColumnIndex (Columns::ColumnId id) const; /// Return index of column with the given \a id. If no such column exists, an /// exception is thrown. virtual int findColumnIndex (Columns::ColumnId id) const; /// Return the UniversalId and the hint for viewing \a row. If viewing is not /// supported by this table, return (UniversalId::Type_None, ""). virtual std::pair view (int row) const; /// Is \a id flagged as deleted? virtual bool isDeleted (const std::string& id) const; virtual int getColumnId (int column) const; }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/scope.cpp000066400000000000000000000007751264522266000227260ustar00rootroot00000000000000#include "scope.hpp" #include #include CSMWorld::Scope CSMWorld::getScopeFromId (const std::string& id) { // get root namespace std::string namespace_; std::string::size_type i = id.find ("::"); if (i!=std::string::npos) namespace_ = Misc::StringUtils::lowerCase (id.substr (0, i)); if (namespace_=="project") return Scope_Project; if (namespace_=="session") return Scope_Session; return Scope_Content; } openmw-openmw-0.38.0/apps/opencs/model/world/scope.hpp000066400000000000000000000006451264522266000227270ustar00rootroot00000000000000#ifndef CSM_WOLRD_SCOPE_H #define CSM_WOLRD_SCOPE_H #include namespace CSMWorld { enum Scope { // record stored in content file Scope_Content = 1, // record stored in project file Scope_Project = 2, // record that exists only for the duration of one editing session Scope_Session = 4 }; Scope getScopeFromId (const std::string& id); } #endif openmw-openmw-0.38.0/apps/opencs/model/world/scriptcontext.cpp000066400000000000000000000070371264522266000245240ustar00rootroot00000000000000#include "scriptcontext.hpp" #include #include #include #include #include #include "data.hpp" CSMWorld::ScriptContext::ScriptContext (const Data& data) : mData (data), mIdsUpdated (false) {} bool CSMWorld::ScriptContext::canDeclareLocals() const { return true; } char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const { int index = mData.getGlobals().searchId (name); if (index!=-1) { switch (mData.getGlobals().getRecord (index).get().mValue.getType()) { case ESM::VT_Short: return 's'; case ESM::VT_Long: return 'l'; case ESM::VT_Float: return 'f'; default: return ' '; } } return ' '; } std::pair CSMWorld::ScriptContext::getMemberType (const std::string& name, const std::string& id) const { std::string id2 = Misc::StringUtils::lowerCase (id); int index = mData.getScripts().searchId (id2); bool reference = false; if (index==-1) { // ID is not a script ID. Search for a matching referenceable instead. index = mData.getReferenceables().searchId (id2); if (index!=-1) { // Referenceable found. int columnIndex = mData.getReferenceables().findColumnIndex (Columns::ColumnId_Script); id2 = Misc::StringUtils::lowerCase (mData.getReferenceables(). getData (index, columnIndex).toString().toUtf8().constData()); if (!id2.empty()) { // Referenceable has a script -> use it. index = mData.getScripts().searchId (id2); reference = true; } } } if (index==-1) return std::make_pair (' ', false); std::map::iterator iter = mLocals.find (id2); if (iter==mLocals.end()) { Compiler::Locals locals; Compiler::NullErrorHandler errorHandler; std::istringstream stream (mData.getScripts().getRecord (index).get().mScriptText); Compiler::QuickFileParser parser (errorHandler, *this, locals); Compiler::Scanner scanner (errorHandler, stream, getExtensions()); scanner.scan (parser); iter = mLocals.insert (std::make_pair (id2, locals)).first; } return std::make_pair (iter->second.getType (Misc::StringUtils::lowerCase (name)), reference); } bool CSMWorld::ScriptContext::isId (const std::string& name) const { if (!mIdsUpdated) { mIds = mData.getIds(); std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCaseInPlace); std::sort (mIds.begin(), mIds.end()); mIdsUpdated = true; } return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name)); } bool CSMWorld::ScriptContext::isJournalId (const std::string& name) const { return mData.getJournals().searchId (name)!=-1; } void CSMWorld::ScriptContext::invalidateIds() { mIdsUpdated = false; } void CSMWorld::ScriptContext::clear() { mIds.clear(); mIdsUpdated = false; mLocals.clear(); } bool CSMWorld::ScriptContext::clearLocals (const std::string& script) { std::map::iterator iter = mLocals.find (Misc::StringUtils::lowerCase (script)); if (iter!=mLocals.end()) { mLocals.erase (iter); mIdsUpdated = false; return true; } return false; } openmw-openmw-0.38.0/apps/opencs/model/world/scriptcontext.hpp000066400000000000000000000032541264522266000245260ustar00rootroot00000000000000#ifndef CSM_WORLD_SCRIPTCONTEXT_H #define CSM_WORLD_SCRIPTCONTEXT_H #include #include #include #include #include namespace CSMWorld { class Data; class ScriptContext : public Compiler::Context { const Data& mData; mutable std::vector mIds; mutable bool mIdsUpdated; mutable std::map mLocals; public: ScriptContext (const Data& data); virtual bool canDeclareLocals() const; ///< Is the compiler allowed to declare local variables? virtual char getGlobalType (const std::string& name) const; ///< 'l: long, 's': short, 'f': float, ' ': does not exist. virtual std::pair getMemberType (const std::string& name, const std::string& id) const; ///< Return type of member variable \a name in script \a id or in script of reference of /// \a id /// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist. /// second: true: script of reference virtual bool isId (const std::string& name) const; ///< Does \a name match an ID, that can be referenced? virtual bool isJournalId (const std::string& name) const; ///< Does \a name match a journal ID? void invalidateIds(); void clear(); ///< Remove all cached data. /// \return Were there any locals that needed clearing? bool clearLocals (const std::string& script); }; } #endif openmw-openmw-0.38.0/apps/opencs/model/world/subcellcollection.hpp000066400000000000000000000024461264522266000253240ustar00rootroot00000000000000#ifndef CSM_WOLRD_SUBCOLLECTION_H #define CSM_WOLRD_SUBCOLLECTION_H #include "nestedidcollection.hpp" namespace ESM { class ESMReader; } namespace CSMWorld { struct Cell; template class IdCollection; /// \brief Single type collection of top level records that are associated with cells template > class SubCellCollection : public NestedIdCollection { const IdCollection& mCells; virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted); public: SubCellCollection (const IdCollection& cells); }; template void SubCellCollection::loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted) { record.load (reader, isDeleted, mCells); } template SubCellCollection::SubCellCollection ( const IdCollection& cells) : mCells (cells) {} } #endif openmw-openmw-0.38.0/apps/opencs/model/world/tablemimedata.cpp000066400000000000000000000276421264522266000244100ustar00rootroot00000000000000#include "tablemimedata.hpp" #include #include #include "universalid.hpp" #include "columnbase.hpp" CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : mDocument(document) { mUniversalId.push_back (id); mObjectsFormats << QString::fromUtf8 (("tabledata/" + id.getTypeName()).c_str()); } CSMWorld::TableMimeData::TableMimeData (const std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : mUniversalId (id), mDocument(document) { for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) { mObjectsFormats << QString::fromUtf8 (("tabledata/" + it->getTypeName()).c_str()); } } QStringList CSMWorld::TableMimeData::formats() const { return mObjectsFormats; } CSMWorld::TableMimeData::~TableMimeData() { } std::string CSMWorld::TableMimeData::getIcon() const { if (mUniversalId.empty()) { qDebug()<<"TableMimeData object does not hold any records!"; //because throwing in the event loop tends to be problematic throw std::runtime_error ("TableMimeData object does not hold any records!"); } std::string tmpIcon; bool firstIteration = true; for (unsigned i = 0; i < mUniversalId.size(); ++i) { if (firstIteration) { firstIteration = false; tmpIcon = mUniversalId[i].getIcon(); continue; } if (tmpIcon != mUniversalId[i].getIcon()) { return ":/multitype.png"; //icon stolen from gnome TODO: get new icon } tmpIcon = mUniversalId[i].getIcon(); } return mUniversalId.begin()->getIcon(); //All objects are of the same type; } std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const { return mUniversalId; } bool CSMWorld::TableMimeData::isReferencable(CSMWorld::ColumnBase::Display type) const { return ( type == CSMWorld::ColumnBase::Display_Activator || type == CSMWorld::ColumnBase::Display_Potion || type == CSMWorld::ColumnBase::Display_Apparatus || type == CSMWorld::ColumnBase::Display_Armor || type == CSMWorld::ColumnBase::Display_Book || type == CSMWorld::ColumnBase::Display_Clothing || type == CSMWorld::ColumnBase::Display_Container || type == CSMWorld::ColumnBase::Display_Creature || type == CSMWorld::ColumnBase::Display_Door || type == CSMWorld::ColumnBase::Display_Ingredient || type == CSMWorld::ColumnBase::Display_CreatureLevelledList || type == CSMWorld::ColumnBase::Display_ItemLevelledList || type == CSMWorld::ColumnBase::Display_Light || type == CSMWorld::ColumnBase::Display_Lockpick || type == CSMWorld::ColumnBase::Display_Miscellaneous || type == CSMWorld::ColumnBase::Display_Npc || type == CSMWorld::ColumnBase::Display_Probe || type == CSMWorld::ColumnBase::Display_Repair || type == CSMWorld::ColumnBase::Display_Static || type == CSMWorld::ColumnBase::Display_Weapon); } bool CSMWorld::TableMimeData::isReferencable(CSMWorld::UniversalId::Type type) { return ( type == CSMWorld::UniversalId::Type_Activator || type == CSMWorld::UniversalId::Type_Potion || type == CSMWorld::UniversalId::Type_Apparatus || type == CSMWorld::UniversalId::Type_Armor || type == CSMWorld::UniversalId::Type_Book || type == CSMWorld::UniversalId::Type_Clothing || type == CSMWorld::UniversalId::Type_Container || type == CSMWorld::UniversalId::Type_Creature || type == CSMWorld::UniversalId::Type_Door || type == CSMWorld::UniversalId::Type_Ingredient || type == CSMWorld::UniversalId::Type_CreatureLevelledList || type == CSMWorld::UniversalId::Type_ItemLevelledList || type == CSMWorld::UniversalId::Type_Light || type == CSMWorld::UniversalId::Type_Lockpick || type == CSMWorld::UniversalId::Type_Miscellaneous || type == CSMWorld::UniversalId::Type_Npc || type == CSMWorld::UniversalId::Type_Probe || type == CSMWorld::UniversalId::Type_Repair || type == CSMWorld::UniversalId::Type_Static || type == CSMWorld::UniversalId::Type_Weapon); } bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const { bool referencable = (type == CSMWorld::UniversalId::Type_Referenceable); for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { if (referencable) { if (isReferencable(it->getType())) { return true; } } else { if (it->getType() == type) { return true; } } } return false; } bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const { bool referencable = (type == CSMWorld::ColumnBase::Display_Referenceable); for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { if (referencable) { if (isReferencable(it->getType())) { return true; } } else { if (it->getType() == convertEnums (type)) { return true; } } } return false; } CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const { bool referencable = (type == CSMWorld::UniversalId::Type_Referenceable); for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { if (referencable) { if (isReferencable(it->getType())) { return *it; } } else { if (it->getType() == type) { return *it; } } } throw std::runtime_error ("TableMimeData object does not hold object of the sought type"); } CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const { bool referencable = (type == CSMWorld::ColumnBase::Display_Referenceable); for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { if (referencable) { if (isReferencable(it->getType())) { return *it; } } else { if (it->getType() == convertEnums (type)) { return *it; } } } throw std::runtime_error ("TableMimeData object does not hold object of the sought type"); } bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const { return &document == &mDocument; } namespace { struct Mapping { CSMWorld::UniversalId::Type mUniversalIdType; CSMWorld::ColumnBase::Display mDisplayType; }; const Mapping mapping[] = { { CSMWorld::UniversalId::Type_Race, CSMWorld::ColumnBase::Display_Race }, { CSMWorld::UniversalId::Type_Skill, CSMWorld::ColumnBase::Display_Skill }, { CSMWorld::UniversalId::Type_Class, CSMWorld::ColumnBase::Display_Class }, { CSMWorld::UniversalId::Type_Faction, CSMWorld::ColumnBase::Display_Faction }, { CSMWorld::UniversalId::Type_Sound, CSMWorld::ColumnBase::Display_Sound }, { CSMWorld::UniversalId::Type_Region, CSMWorld::ColumnBase::Display_Region }, { CSMWorld::UniversalId::Type_Birthsign, CSMWorld::ColumnBase::Display_Birthsign }, { CSMWorld::UniversalId::Type_Spell, CSMWorld::ColumnBase::Display_Spell }, { CSMWorld::UniversalId::Type_Cell, CSMWorld::ColumnBase::Display_Cell }, { CSMWorld::UniversalId::Type_Referenceable, CSMWorld::ColumnBase::Display_Referenceable }, { CSMWorld::UniversalId::Type_Activator, CSMWorld::ColumnBase::Display_Activator }, { CSMWorld::UniversalId::Type_Potion, CSMWorld::ColumnBase::Display_Potion }, { CSMWorld::UniversalId::Type_Apparatus, CSMWorld::ColumnBase::Display_Apparatus }, { CSMWorld::UniversalId::Type_Armor, CSMWorld::ColumnBase::Display_Armor }, { CSMWorld::UniversalId::Type_Book, CSMWorld::ColumnBase::Display_Book }, { CSMWorld::UniversalId::Type_Clothing, CSMWorld::ColumnBase::Display_Clothing }, { CSMWorld::UniversalId::Type_Container, CSMWorld::ColumnBase::Display_Container }, { CSMWorld::UniversalId::Type_Creature, CSMWorld::ColumnBase::Display_Creature }, { CSMWorld::UniversalId::Type_Door, CSMWorld::ColumnBase::Display_Door }, { CSMWorld::UniversalId::Type_Ingredient, CSMWorld::ColumnBase::Display_Ingredient }, { CSMWorld::UniversalId::Type_CreatureLevelledList, CSMWorld::ColumnBase::Display_CreatureLevelledList }, { CSMWorld::UniversalId::Type_ItemLevelledList, CSMWorld::ColumnBase::Display_ItemLevelledList }, { CSMWorld::UniversalId::Type_Light, CSMWorld::ColumnBase::Display_Light }, { CSMWorld::UniversalId::Type_Lockpick, CSMWorld::ColumnBase::Display_Lockpick }, { CSMWorld::UniversalId::Type_Miscellaneous, CSMWorld::ColumnBase::Display_Miscellaneous }, { CSMWorld::UniversalId::Type_Npc, CSMWorld::ColumnBase::Display_Npc }, { CSMWorld::UniversalId::Type_Probe, CSMWorld::ColumnBase::Display_Probe }, { CSMWorld::UniversalId::Type_Repair, CSMWorld::ColumnBase::Display_Repair }, { CSMWorld::UniversalId::Type_Static, CSMWorld::ColumnBase::Display_Static }, { CSMWorld::UniversalId::Type_Weapon, CSMWorld::ColumnBase::Display_Weapon }, { CSMWorld::UniversalId::Type_Reference, CSMWorld::ColumnBase::Display_Reference }, { CSMWorld::UniversalId::Type_Filter, CSMWorld::ColumnBase::Display_Filter }, { CSMWorld::UniversalId::Type_Topic, CSMWorld::ColumnBase::Display_Topic }, { CSMWorld::UniversalId::Type_Journal, CSMWorld::ColumnBase::Display_Journal }, { CSMWorld::UniversalId::Type_TopicInfo, CSMWorld::ColumnBase::Display_TopicInfo }, { CSMWorld::UniversalId::Type_JournalInfo, CSMWorld::ColumnBase::Display_JournalInfo }, { CSMWorld::UniversalId::Type_Scene, CSMWorld::ColumnBase::Display_Scene }, { CSMWorld::UniversalId::Type_Script, CSMWorld::ColumnBase::Display_Script }, { CSMWorld::UniversalId::Type_Mesh, CSMWorld::ColumnBase::Display_Mesh }, { CSMWorld::UniversalId::Type_Icon, CSMWorld::ColumnBase::Display_Icon }, { CSMWorld::UniversalId::Type_Music, CSMWorld::ColumnBase::Display_Music }, { CSMWorld::UniversalId::Type_SoundRes, CSMWorld::ColumnBase::Display_SoundRes }, { CSMWorld::UniversalId::Type_Texture, CSMWorld::ColumnBase::Display_Texture }, { CSMWorld::UniversalId::Type_Video, CSMWorld::ColumnBase::Display_Video }, { CSMWorld::UniversalId::Type_Global, CSMWorld::ColumnBase::Display_GlobalVariable }, { CSMWorld::UniversalId::Type_BodyPart, CSMWorld::ColumnBase::Display_BodyPart }, { CSMWorld::UniversalId::Type_Enchantment, CSMWorld::ColumnBase::Display_Enchantment }, { CSMWorld::UniversalId::Type_None, CSMWorld::ColumnBase::Display_None } // end marker }; } CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (ColumnBase::Display type) { for (int i=0; mapping[i].mUniversalIdType!=CSMWorld::UniversalId::Type_None; ++i) if (mapping[i].mDisplayType==type) return mapping[i].mUniversalIdType; return CSMWorld::UniversalId::Type_None; } CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (UniversalId::Type type) { for (int i=0; mapping[i].mUniversalIdType!=CSMWorld::UniversalId::Type_None; ++i) if (mapping[i].mUniversalIdType==type) return mapping[i].mDisplayType; return CSMWorld::ColumnBase::Display_None; } const CSMDoc::Document* CSMWorld::TableMimeData::getDocumentPtr() const { return &mDocument; } openmw-openmw-0.38.0/apps/opencs/model/world/tablemimedata.hpp000066400000000000000000000037741264522266000244150ustar00rootroot00000000000000#ifndef TABLEMIMEDATA_H #define TABLEMIMEDATA_H #include #include #include #include "universalid.hpp" #include "columnbase.hpp" namespace CSMDoc { class Document; } namespace CSMWorld { /// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. /// /// This class provides way to construct mimedata object holding the universalid copy /// Universalid is used in the majority of the tables to store type, id, argument types. /// This way universalid grants a way to retrive record from the concrete table. /// Please note, that tablemimedata object can hold multiple universalIds in the vector. class TableMimeData : public QMimeData { std::vector mUniversalId; QStringList mObjectsFormats; const CSMDoc::Document& mDocument; public: TableMimeData(UniversalId id, const CSMDoc::Document& document); TableMimeData(const std::vector& id, const CSMDoc::Document& document); ~TableMimeData(); virtual QStringList formats() const; std::string getIcon() const; std::vector getData() const; bool holdsType(UniversalId::Type type) const; bool holdsType(CSMWorld::ColumnBase::Display type) const; bool fromDocument(const CSMDoc::Document& document) const; UniversalId returnMatching(UniversalId::Type type) const; const CSMDoc::Document* getDocumentPtr() const; UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); static bool isReferencable(CSMWorld::UniversalId::Type type); private: bool isReferencable(CSMWorld::ColumnBase::Display type) const; }; } #endif // TABLEMIMEDATA_H openmw-openmw-0.38.0/apps/opencs/model/world/universalid.cpp000066400000000000000000000446151264522266000241430ustar00rootroot00000000000000#include "universalid.hpp" #include #include #include #include namespace { struct TypeData { CSMWorld::UniversalId::Class mClass; CSMWorld::UniversalId::Type mType; const char *mName; const char *mIcon; }; static const TypeData sNoArg[] = { { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Objects", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "Instances", 0 }, { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", 0 }, { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", 0 }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIdArg[] = { { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", ":./globvar.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", ":./GMST.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", ":./skill.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", ":./class.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", ":./faction.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", ":./race.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", ":./sound.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", ":./script.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./land.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", 0 }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Armor, "Armor", ":./armor.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Book, "Book", ":./book.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Clothing, "Clothing", ":./clothing.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Container, "Container", ":./container.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Creature, "Creature", ":./creature.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList, "Creature Levelled List", ":./creature.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList, "Item Levelled List", ":./leveled-item.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, "Miscellaneous", ":./miscellaneous.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, "NPC", ":./npc.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, "Probe", ":./probe.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", 0 }, { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", 0 }, { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", 0 }, { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", 0 }, { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", 0 }, { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", 0 }, { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIndexArg[] = { { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", 0 }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", 0 }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; } CSMWorld::UniversalId::UniversalId (const std::string& universalId) : mIndex(0) { std::string::size_type index = universalId.find (':'); if (index!=std::string::npos) { std::string type = universalId.substr (0, index); for (int i=0; sIdArg[i].mName; ++i) if (type==sIdArg[i].mName) { mArgumentType = ArgumentType_Id; mType = sIdArg[i].mType; mClass = sIdArg[i].mClass; mId = universalId.substr (index+2); return; } for (int i=0; sIndexArg[i].mName; ++i) if (type==sIndexArg[i].mName) { mArgumentType = ArgumentType_Index; mType = sIndexArg[i].mType; mClass = sIndexArg[i].mClass; std::istringstream stream (universalId.substr (index+2)); if (stream >> mIndex) return; break; } } else { for (int i=0; sNoArg[i].mName; ++i) if (universalId==sNoArg[i].mName) { mArgumentType = ArgumentType_None; mType = sNoArg[i].mType; mClass = sNoArg[i].mClass; return; } } throw std::runtime_error ("invalid UniversalId: " + universalId); } CSMWorld::UniversalId::UniversalId (Type type) : mArgumentType (ArgumentType_None), mType (type), mIndex (0) { for (int i=0; sNoArg[i].mName; ++i) if (type==sNoArg[i].mType) { mClass = sNoArg[i].mClass; return; } for (int i=0; sIdArg[i].mName; ++i) if (type==sIdArg[i].mType) { mArgumentType = ArgumentType_Id; mClass = sIdArg[i].mClass; return; } for (int i=0; sIndexArg[i].mName; ++i) if (type==sIndexArg[i].mType) { mArgumentType = ArgumentType_Index; mClass = sIndexArg[i].mClass; return; } throw std::logic_error ("invalid argument-less UniversalId type"); } CSMWorld::UniversalId::UniversalId (Type type, const std::string& id) : mArgumentType (ArgumentType_Id), mType (type), mId (id), mIndex (0) { for (int i=0; sIdArg[i].mName; ++i) if (type==sIdArg[i].mType) { mClass = sIdArg[i].mClass; return; } throw std::logic_error ("invalid ID argument UniversalId type"); } CSMWorld::UniversalId::UniversalId (Type type, int index) : mArgumentType (ArgumentType_Index), mType (type), mIndex (index) { for (int i=0; sIndexArg[i].mName; ++i) if (type==sIndexArg[i].mType) { mClass = sIndexArg[i].mClass; return; } throw std::logic_error ("invalid index argument UniversalId type"); } CSMWorld::UniversalId::Class CSMWorld::UniversalId::getClass() const { return mClass; } CSMWorld::UniversalId::ArgumentType CSMWorld::UniversalId::getArgumentType() const { return mArgumentType; } CSMWorld::UniversalId::Type CSMWorld::UniversalId::getType() const { return mType; } const std::string& CSMWorld::UniversalId::getId() const { if (mArgumentType!=ArgumentType_Id) throw std::logic_error ("invalid access to ID of non-ID UniversalId"); return mId; } int CSMWorld::UniversalId::getIndex() const { if (mArgumentType!=ArgumentType_Index) throw std::logic_error ("invalid access to index of non-index UniversalId"); return mIndex; } bool CSMWorld::UniversalId::isEqual (const UniversalId& universalId) const { if (mClass!=universalId.mClass || mArgumentType!=universalId.mArgumentType || mType!=universalId.mType) return false; switch (mArgumentType) { case ArgumentType_Id: return mId==universalId.mId; case ArgumentType_Index: return mIndex==universalId.mIndex; default: return true; } } bool CSMWorld::UniversalId::isLess (const UniversalId& universalId) const { if (mTypeuniversalId.mType) return false; switch (mArgumentType) { case ArgumentType_Id: return mId CSMWorld::UniversalId::listReferenceableTypes() { std::vector list; for (int i=0; sIdArg[i].mName; ++i) if (sIdArg[i].mClass==Class_RefRecord) list.push_back (sIdArg[i].mType); return list; } std::vector CSMWorld::UniversalId::listTypes (int classes) { std::vector list; for (int i=0; sNoArg[i].mName; ++i) if (sNoArg[i].mClass & classes) list.push_back (sNoArg[i].mType); for (int i=0; sIdArg[i].mName; ++i) if (sIdArg[i].mClass & classes) list.push_back (sIdArg[i].mType); for (int i=0; sIndexArg[i].mName; ++i) if (sIndexArg[i].mClass & classes) list.push_back (sIndexArg[i].mType); return list; } CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type) { for (int i=0; sIdArg[i].mType; ++i) if (type==sIdArg[i].mType) { if (sIdArg[i].mClass==Class_RefRecord) return Type_Referenceables; if (sIdArg[i].mClass==Class_SubRecord || sIdArg[i].mClass==Class_Record || sIdArg[i].mClass==Class_Resource) { if (type==Type_Cell_Missing) return Type_Cells; return static_cast (type-1); } break; } return Type_None; } bool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right) { return left.isEqual (right); } bool CSMWorld::operator!= (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right) { return !left.isEqual (right); } bool CSMWorld::operator< (const UniversalId& left, const UniversalId& right) { return left.isLess (right); } std::ostream& CSMWorld::operator< (std::ostream& stream, const CSMWorld::UniversalId& universalId) { return stream << universalId.toString(); } openmw-openmw-0.38.0/apps/opencs/model/world/universalid.hpp000066400000000000000000000143051264522266000241410ustar00rootroot00000000000000#ifndef CSM_WOLRD_UNIVERSALID_H #define CSM_WOLRD_UNIVERSALID_H #include #include #include #include namespace CSMWorld { class UniversalId { public: enum Class { Class_None = 0, Class_Record = 1, Class_RefRecord = 2, // referenceable record Class_SubRecord = 4, Class_RecordList = 8, Class_Collection = 16, // multiple types of records combined Class_Transient = 32, // not part of the world data or the project data Class_NonRecord = 64, // record like data that is not part of the world Class_Resource = 128, ///< \attention Resource IDs are unique only within the /// respective collection Class_ResourceList = 256 }; enum ArgumentType { ArgumentType_None, ArgumentType_Id, ArgumentType_Index }; /// \note A record list type must always be immediately followed by the matching /// record type, if this type is of class SubRecord or Record. enum Type { Type_None = 0, Type_Globals, Type_Global, Type_VerificationResults, Type_Gmsts, Type_Gmst, Type_Skills, Type_Skill, Type_Classes, Type_Class, Type_Factions, Type_Faction, Type_Races, Type_Race, Type_Sounds, Type_Sound, Type_Scripts, Type_Script, Type_Regions, Type_Region, Type_Birthsigns, Type_Birthsign, Type_Spells, Type_Spell, Type_Cells, Type_Cell, Type_Cell_Missing, //For cells that does not exist yet. Type_Referenceables, Type_Referenceable, Type_Activator, Type_Potion, Type_Apparatus, Type_Armor, Type_Book, Type_Clothing, Type_Container, Type_Creature, Type_Door, Type_Ingredient, Type_CreatureLevelledList, Type_ItemLevelledList, Type_Light, Type_Lockpick, Type_Miscellaneous, Type_Npc, Type_Probe, Type_Repair, Type_Static, Type_Weapon, Type_References, Type_Reference, Type_RegionMap, Type_Filters, Type_Filter, Type_Topics, Type_Topic, Type_Journals, Type_Journal, Type_TopicInfos, Type_TopicInfo, Type_JournalInfos, Type_JournalInfo, Type_Scene, Type_Preview, Type_LoadErrorLog, Type_Enchantments, Type_Enchantment, Type_BodyParts, Type_BodyPart, Type_Meshes, Type_Mesh, Type_Icons, Type_Icon, Type_Musics, Type_Music, Type_SoundsRes, Type_SoundRes, Type_Textures, Type_Texture, Type_Videos, Type_Video, Type_DebugProfiles, Type_DebugProfile, Type_SoundGens, Type_SoundGen, Type_MagicEffects, Type_MagicEffect, Type_Pathgrids, Type_Pathgrid, Type_StartScripts, Type_StartScript, Type_Search, Type_MetaDatas, Type_MetaData, Type_RunLog }; enum { NumberOfTypes = Type_RunLog+1 }; private: Class mClass; ArgumentType mArgumentType; Type mType; std::string mId; int mIndex; public: UniversalId (const std::string& universalId); UniversalId (Type type = Type_None); UniversalId (Type type, const std::string& id); ///< Using a type for a non-ID-argument UniversalId will throw an exception. UniversalId (Type type, int index); ///< Using a type for a non-index-argument UniversalId will throw an exception. Class getClass() const; ArgumentType getArgumentType() const; Type getType() const; const std::string& getId() const; ///< Calling this function for a non-ID type will throw an exception. int getIndex() const; ///< Calling this function for a non-index type will throw an exception. bool isEqual (const UniversalId& universalId) const; bool isLess (const UniversalId& universalId) const; std::string getTypeName() const; std::string toString() const; std::string getIcon() const; ///< Will return an empty string, if no icon is available. static std::vector listReferenceableTypes(); static std::vector listTypes (int classes); /// If \a type is a SubRecord, RefRecord or Record type return the type of the table /// that contains records of type \a type. /// Otherwise return Type_None. static Type getParentType (Type type); }; bool operator== (const UniversalId& left, const UniversalId& right); bool operator!= (const UniversalId& left, const UniversalId& right); bool operator< (const UniversalId& left, const UniversalId& right); std::ostream& operator< (std::ostream& stream, const UniversalId& universalId); } Q_DECLARE_METATYPE (CSMWorld::UniversalId) #endif openmw-openmw-0.38.0/apps/opencs/view/000077500000000000000000000000001264522266000176235ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/view/doc/000077500000000000000000000000001264522266000203705ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/view/doc/adjusterwidget.cpp000066400000000000000000000063601264522266000241260ustar00rootroot00000000000000#include "adjusterwidget.hpp" #include #include #include #include #include #include #include CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) : QWidget (parent), mValid (false), mAction (ContentAction_Undefined) { QHBoxLayout *layout = new QHBoxLayout (this); mIcon = new QLabel (this); layout->addWidget (mIcon, 0); mMessage = new QLabel (this); mMessage->setWordWrap (true); mMessage->setSizePolicy (QSizePolicy (QSizePolicy::Minimum, QSizePolicy::Minimum)); layout->addWidget (mMessage, 1); setName ("", false); setLayout (layout); } void CSVDoc::AdjusterWidget::setAction (ContentAction action) { mAction = action; } void CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData) { mLocalData = localData; } boost::filesystem::path CSVDoc::AdjusterWidget::getPath() const { if (!mValid) throw std::logic_error ("invalid content file path"); return mResultPath; } bool CSVDoc::AdjusterWidget::isValid() const { return mValid; } void CSVDoc::AdjusterWidget::setFilenameCheck (bool doCheck) { mDoFilenameCheck = doCheck; } void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) { QString message; mValid = (!name.isEmpty()); bool warning = false; if (!mValid) { message = "No name."; } else { boost::filesystem::path path (name.toUtf8().data()); std::string extension = path.extension().string(); boost::algorithm::to_lower(extension); bool isLegacyPath = (extension == ".esm" || extension == ".esp"); bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); if (isLegacyPath) path.replace_extension (addon ? ".omwaddon" : ".omwgame"); //if the file came from data-local and is not a legacy file to be converted, //don't worry about doing a file check. if (!isFilePathChanged && !isLegacyPath) { // path already points to the local data directory message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); mResultPath = path; } //in all other cases, ensure the path points to data-local and do an existing file check else { // path points somewhere else or is a leaf name. if (isFilePathChanged) path = mLocalData / path.filename(); message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); mResultPath = path; if (boost::filesystem::exists (path)) { /// \todo add an user setting to make this an error. message += "

A file with the same name already exists. If you continue, it will be overwritten."; warning = true; } } } mMessage->setText (message); mIcon->setPixmap (style()->standardIcon ( mValid ? (warning ? QStyle::SP_MessageBoxWarning : QStyle::SP_MessageBoxInformation) : QStyle::SP_MessageBoxCritical). pixmap (QSize (16, 16))); emit stateChanged (mValid); } openmw-openmw-0.38.0/apps/opencs/view/doc/adjusterwidget.hpp000066400000000000000000000022511264522266000241260ustar00rootroot00000000000000#ifndef CSV_DOC_ADJUSTERWIDGET_H #define CSV_DOC_ADJUSTERWIDGET_H #include #include class QLabel; namespace CSVDoc { enum ContentAction { ContentAction_New, ContentAction_Edit, ContentAction_Undefined }; class AdjusterWidget : public QWidget { Q_OBJECT public: boost::filesystem::path mLocalData; QLabel *mMessage; QLabel *mIcon; bool mValid; boost::filesystem::path mResultPath; ContentAction mAction; bool mDoFilenameCheck; public: AdjusterWidget (QWidget *parent = 0); void setLocalData (const boost::filesystem::path& localData); void setAction (ContentAction action); void setFilenameCheck (bool doCheck); bool isValid() const; boost::filesystem::path getPath() const; ///< This function must not be called if there is no valid path. public slots: void setName (const QString& name, bool addon); signals: void stateChanged (bool valid); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/filedialog.cpp000066400000000000000000000133111264522266000231720ustar00rootroot00000000000000#include "filedialog.hpp" #include #include #include #include #include #include #include #include #include #include #include "components/contentselector/model/esmfile.hpp" #include "components/contentselector/view/contentselector.hpp" #include "filewidget.hpp" #include "adjusterwidget.hpp" CSVDoc::FileDialog::FileDialog(QWidget *parent) : QDialog(parent), mSelector (0), mAction(ContentAction_Undefined), mFileWidget (0), mAdjusterWidget (0), mDialogBuilt(false) { ui.setupUi (this); resize(400, 400); setObjectName ("FileDialog"); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); mAdjusterWidget = new AdjusterWidget (this); } void CSVDoc::FileDialog::addFiles(const QString &path) { mSelector->addFiles(path); } void CSVDoc::FileDialog::clearFiles() { mSelector->clearFiles(); } QStringList CSVDoc::FileDialog::selectedFilePaths() { QStringList filePaths; foreach (ContentSelectorModel::EsmFile *file, mSelector->selectedFiles() ) filePaths.append(file->filePath()); return filePaths; } void CSVDoc::FileDialog::setLocalData (const boost::filesystem::path& localData) { mAdjusterWidget->setLocalData (localData); } void CSVDoc::FileDialog::showDialog (ContentAction action) { mAction = action; ui.projectGroupBoxLayout->insertWidget (0, mAdjusterWidget); switch (mAction) { case ContentAction_New: buildNewFileView(); break; case ContentAction_Edit: buildOpenFileView(); break; default: break; } mAdjusterWidget->setFilenameCheck (mAction == ContentAction_New); if(!mDialogBuilt) { //connections common to both dialog view flavors connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)), this, SLOT (slotUpdateAcceptButton (int))); connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); mDialogBuilt = true; } show(); raise(); activateWindow(); } void CSVDoc::FileDialog::buildNewFileView() { setWindowTitle(tr("Create a new addon")); QPushButton* createButton = ui.projectButtonBox->button (QDialogButtonBox::Ok); createButton->setText ("Create"); createButton->setEnabled (false); if(!mFileWidget) { mFileWidget = new FileWidget (this); mFileWidget->setType (true); mFileWidget->extensionLabelIsVisible(true); connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), mAdjusterWidget, SLOT (setName (const QString&, bool))); connect (mFileWidget, SIGNAL (nameChanged(const QString &, bool)), this, SLOT (slotUpdateAcceptButton(const QString &, bool))); } ui.projectGroupBoxLayout->insertWidget (0, mFileWidget); connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile())); } void CSVDoc::FileDialog::buildOpenFileView() { setWindowTitle(tr("Open")); ui.projectGroupBox->setTitle (QString("")); ui.projectButtonBox->button(QDialogButtonBox::Ok)->setText ("Open"); if(mSelector->isGamefileSelected()) ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (true); else ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); if(!mDialogBuilt) { connect (mSelector, SIGNAL (signalAddonDataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (slotAddonDataChanged(const QModelIndex&, const QModelIndex&))); } connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile())); } void CSVDoc::FileDialog::slotAddonDataChanged(const QModelIndex &topleft, const QModelIndex &bottomright) { slotUpdateAcceptButton(0); } void CSVDoc::FileDialog::slotUpdateAcceptButton(int) { QString name = ""; if (mFileWidget && mAction == ContentAction_New) name = mFileWidget->getName(); slotUpdateAcceptButton (name, true); } void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) { bool success = !mSelector->selectedFiles().empty(); bool isNew = (mAction == ContentAction_New); if (isNew) success = success && !(name.isEmpty()); else if (success) { ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back(); mAdjusterWidget->setName (file->filePath(), !file->isGameFile()); } else mAdjusterWidget->setName ("", true); ui.projectButtonBox->button (QDialogButtonBox::Ok)->setEnabled (success); } QString CSVDoc::FileDialog::filename() const { if (mAction == ContentAction_New) return ""; return mSelector->currentFile(); } void CSVDoc::FileDialog::slotRejected() { emit rejected(); disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile())); disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile())); if(mFileWidget) { delete mFileWidget; mFileWidget = NULL; } close(); } void CSVDoc::FileDialog::slotNewFile() { emit signalCreateNewFile (mAdjusterWidget->getPath()); if(mFileWidget) { delete mFileWidget; mFileWidget = NULL; } disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile())); close(); } void CSVDoc::FileDialog::slotOpenFile() { ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back(); mAdjusterWidget->setName (file->filePath(), !file->isGameFile()); emit signalOpenFiles (mAdjusterWidget->getPath()); disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile())); close(); } openmw-openmw-0.38.0/apps/opencs/view/doc/filedialog.hpp000066400000000000000000000033071264522266000232030ustar00rootroot00000000000000#ifndef FILEDIALOG_HPP #define FILEDIALOG_HPP #include #include #include #include "adjusterwidget.hpp" #ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED #define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED Q_DECLARE_METATYPE (boost::filesystem::path) #endif #include "ui_filedialog.h" class DataFilesModel; class PluginsProxyModel; namespace ContentSelectorView { class ContentSelector; } namespace CSVDoc { class FileWidget; class FileDialog : public QDialog { Q_OBJECT private: ContentSelectorView::ContentSelector *mSelector; Ui::FileDialog ui; ContentAction mAction; FileWidget *mFileWidget; AdjusterWidget *mAdjusterWidget; bool mDialogBuilt; public: explicit FileDialog(QWidget *parent = 0); void showDialog (ContentAction action); void addFiles (const QString &path); void clearFiles (); QString filename() const; QStringList selectedFilePaths(); void setLocalData (const boost::filesystem::path& localData); private: void buildNewFileView(); void buildOpenFileView(); signals: void signalOpenFiles (const boost::filesystem::path &path); void signalCreateNewFile (const boost::filesystem::path &path); void signalUpdateAcceptButton (bool, int); private slots: void slotNewFile(); void slotOpenFile(); void slotUpdateAcceptButton (int); void slotUpdateAcceptButton (const QString &, bool); void slotRejected(); void slotAddonDataChanged(const QModelIndex& topleft, const QModelIndex& bottomright); }; } #endif // FILEDIALOG_HPP openmw-openmw-0.38.0/apps/opencs/view/doc/filewidget.cpp000066400000000000000000000024151264522266000232210ustar00rootroot00000000000000#include "filewidget.hpp" #include #include #include #include #include QString CSVDoc::FileWidget::getExtension() const { return mAddon ? ".omwaddon" : ".omwgame"; } CSVDoc::FileWidget::FileWidget (QWidget *parent) : QWidget (parent), mAddon (false) { QHBoxLayout *layout = new QHBoxLayout (this); mInput = new QLineEdit (this); layout->addWidget (mInput, 1); mType = new QLabel (this); layout ->addWidget (mType); connect (mInput, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); setLayout (layout); } void CSVDoc::FileWidget::setType (bool addon) { mAddon = addon; mType->setText (getExtension()); } QString CSVDoc::FileWidget::getName() const { QString text = mInput->text(); if (text.isEmpty()) return ""; return text + getExtension(); } void CSVDoc::FileWidget::textChanged (const QString& text) { emit nameChanged (getName(), mAddon); } void CSVDoc::FileWidget::extensionLabelIsVisible(bool visible) { mType->setVisible(visible); } void CSVDoc::FileWidget::setName (const std::string& text) { QString text2 = QString::fromUtf8 (text.c_str()); mInput->setText (text2); textChanged (text2); } openmw-openmw-0.38.0/apps/opencs/view/doc/filewidget.hpp000066400000000000000000000014211264522266000232220ustar00rootroot00000000000000#ifndef CSV_DOC_FILEWIDGET_H #define CSV_DOC_FILEWIDGET_H #include #include class QLabel; class QString; class QLineEdit; namespace CSVDoc { class FileWidget : public QWidget { Q_OBJECT bool mAddon; QLineEdit *mInput; QLabel *mType; QString getExtension() const; public: FileWidget (QWidget *parent = 0); void setType (bool addon); QString getName() const; void extensionLabelIsVisible(bool visible); void setName (const std::string& text); private slots: void textChanged (const QString& text); signals: void nameChanged (const QString& file, bool addon); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/globaldebugprofilemenu.cpp000066400000000000000000000053171264522266000256170ustar00rootroot00000000000000#include "globaldebugprofilemenu.hpp" #include #include #include #include "../../model/world/idtable.hpp" #include "../../model/world/record.hpp" void CSVDoc::GlobalDebugProfileMenu::rebuild() { clear(); delete mActions; mActions = 0; int idColumn = mDebugProfiles->findColumnIndex (CSMWorld::Columns::ColumnId_Id); int stateColumn = mDebugProfiles->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); int globalColumn = mDebugProfiles->findColumnIndex ( CSMWorld::Columns::ColumnId_GlobalProfile); int size = mDebugProfiles->rowCount(); std::vector ids; for (int i=0; idata (mDebugProfiles->index (i, stateColumn)).toInt(); bool global = mDebugProfiles->data (mDebugProfiles->index (i, globalColumn)).toInt(); if (state!=CSMWorld::RecordBase::State_Deleted && global) ids.push_back ( mDebugProfiles->data (mDebugProfiles->index (i, idColumn)).toString()); } mActions = new QActionGroup (this); connect (mActions, SIGNAL (triggered (QAction *)), this, SLOT (actionTriggered (QAction *))); std::sort (ids.begin(), ids.end()); for (std::vector::const_iterator iter (ids.begin()); iter!=ids.end(); ++iter) { mActions->addAction (addAction (*iter)); } } CSVDoc::GlobalDebugProfileMenu::GlobalDebugProfileMenu (CSMWorld::IdTable *debugProfiles, QWidget *parent) : QMenu (parent), mDebugProfiles (debugProfiles), mActions (0) { rebuild(); connect (mDebugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (profileAboutToBeRemoved (const QModelIndex&, int, int))); connect (mDebugProfiles, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (profileInserted (const QModelIndex&, int, int))); connect (mDebugProfiles, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (profileChanged (const QModelIndex&, const QModelIndex&))); } void CSVDoc::GlobalDebugProfileMenu::updateActions (bool running) { if (mActions) mActions->setEnabled (!running); } void CSVDoc::GlobalDebugProfileMenu::profileAboutToBeRemoved (const QModelIndex& parent, int start, int end) { rebuild(); } void CSVDoc::GlobalDebugProfileMenu::profileInserted (const QModelIndex& parent, int start, int end) { rebuild(); } void CSVDoc::GlobalDebugProfileMenu::profileChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { rebuild(); } void CSVDoc::GlobalDebugProfileMenu::actionTriggered (QAction *action) { emit triggered (std::string (action->text().toUtf8().constData())); } openmw-openmw-0.38.0/apps/opencs/view/doc/globaldebugprofilemenu.hpp000066400000000000000000000017561264522266000256270ustar00rootroot00000000000000#ifndef CSV_DOC_GLOBALDEBUGPROFILEMENU_H #define CSV_DOC_GLOBALDEBUGPROFILEMENU_H #include class QModelIndex; class QActionGroup; namespace CSMWorld { class IdTable; } namespace CSVDoc { class GlobalDebugProfileMenu : public QMenu { Q_OBJECT CSMWorld::IdTable *mDebugProfiles; QActionGroup *mActions; private: void rebuild(); public: GlobalDebugProfileMenu (CSMWorld::IdTable *debugProfiles, QWidget *parent = 0); void updateActions (bool running); private slots: void profileAboutToBeRemoved (const QModelIndex& parent, int start, int end); void profileInserted (const QModelIndex& parent, int start, int end); void profileChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void actionTriggered (QAction *action); signals: void triggered (const std::string& profile); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/loader.cpp000066400000000000000000000122461264522266000223470ustar00rootroot00000000000000#include "loader.hpp" #include #include #include #include #include #include #include #include "../../model/doc/document.hpp" void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event) { event->ignore(); cancel(); } CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document) : mDocument (document), mAborted (false), mMessages (0), mTotalRecords (0) { setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str())); setMinimumWidth (400); mLayout = new QVBoxLayout (this); // file progress mFile = new QLabel (this); mLayout->addWidget (mFile); mFileProgress = new QProgressBar (this); mLayout->addWidget (mFileProgress); int size = static_cast (document->getContentFiles().size())+1; if (document->isNew()) --size; mFileProgress->setMinimum (0); mFileProgress->setMaximum (size); mFileProgress->setTextVisible (true); mFileProgress->setValue (0); // record progress mLayout->addWidget (mRecords = new QLabel ("Records", this)); mRecordProgress = new QProgressBar (this); mLayout->addWidget (mRecordProgress); mRecordProgress->setMinimum (0); mRecordProgress->setTextVisible (true); mRecordProgress->setValue (0); // error message mError = new QLabel (this); mError->setWordWrap (true); mLayout->addWidget (mError); // buttons mButtons = new QDialogButtonBox (QDialogButtonBox::Cancel, Qt::Horizontal, this); mLayout->addWidget (mButtons); setLayout (mLayout); move (QCursor::pos()); show(); connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel())); } void CSVDoc::LoadingDocument::nextStage (const std::string& name, int totalRecords) { mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str())); mFileProgress->setValue (mFileProgress->value()+1); mRecordProgress->setValue (0); mRecordProgress->setMaximum (totalRecords>0 ? totalRecords : 1); mTotalRecords = totalRecords; } void CSVDoc::LoadingDocument::nextRecord (int records) { if (records<=mTotalRecords) { mRecordProgress->setValue (records); std::ostringstream stream; stream << "Records: " << records << " of " << mTotalRecords; mRecords->setText (QString::fromUtf8 (stream.str().c_str())); } } void CSVDoc::LoadingDocument::abort (const std::string& error) { mAborted = true; mError->setText (QString::fromUtf8 (("Loading failed: " + error + "").c_str())); mButtons->setStandardButtons (QDialogButtonBox::Close); } void CSVDoc::LoadingDocument::addMessage (const std::string& message) { if (!mMessages) { mMessages = new QListWidget (this); mLayout->insertWidget (4, mMessages); } new QListWidgetItem (QString::fromUtf8 (message.c_str()), mMessages); } void CSVDoc::LoadingDocument::cancel() { if (!mAborted) emit cancel (mDocument); else { emit close (mDocument); deleteLater(); } } CSVDoc::Loader::Loader() {} CSVDoc::Loader::~Loader() { for (std::map::iterator iter (mDocuments.begin()); iter!=mDocuments.end(); ++iter) delete iter->second; } void CSVDoc::Loader::add (CSMDoc::Document *document) { LoadingDocument *loading = new LoadingDocument (document); mDocuments.insert (std::make_pair (document, loading)); connect (loading, SIGNAL (cancel (CSMDoc::Document *)), this, SIGNAL (cancel (CSMDoc::Document *))); connect (loading, SIGNAL (close (CSMDoc::Document *)), this, SIGNAL (close (CSMDoc::Document *))); } void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed, const std::string& error) { std::map::iterator iter = mDocuments.begin(); for (; iter!=mDocuments.end(); ++iter) if (iter->first==document) break; if (iter==mDocuments.end()) return; if (completed || error.empty()) { delete iter->second; mDocuments.erase (iter); } else if (!completed && !error.empty()) { iter->second->abort (error); // Leave the window open for now (wait for the user to close it) mDocuments.erase (iter); } } void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name, int totalRecords) { std::map::iterator iter = mDocuments.find (document); if (iter!=mDocuments.end()) iter->second->nextStage (name, totalRecords); } void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records) { std::map::iterator iter = mDocuments.find (document); if (iter!=mDocuments.end()) iter->second->nextRecord (records); } void CSVDoc::Loader::loadMessage (CSMDoc::Document *document, const std::string& message) { std::map::iterator iter = mDocuments.find (document); if (iter!=mDocuments.end()) iter->second->addMessage (message); } openmw-openmw-0.38.0/apps/opencs/view/doc/loader.hpp000066400000000000000000000042201264522266000223450ustar00rootroot00000000000000#ifndef CSV_DOC_LOADER_H #define CSV_DOC_LOADER_H #include #include #include #include class QLabel; class QProgressBar; class QDialogButtonBox; class QListWidget; class QVBoxLayout; namespace CSMDoc { class Document; } namespace CSVDoc { class LoadingDocument : public QWidget { Q_OBJECT CSMDoc::Document *mDocument; QLabel *mFile; QLabel *mRecords; QProgressBar *mFileProgress; QProgressBar *mRecordProgress; bool mAborted; QDialogButtonBox *mButtons; QLabel *mError; QListWidget *mMessages; QVBoxLayout *mLayout; int mTotalRecords; private: void closeEvent (QCloseEvent *event); public: LoadingDocument (CSMDoc::Document *document); void nextStage (const std::string& name, int totalRecords); void nextRecord (int records); void abort (const std::string& error); void addMessage (const std::string& message); private slots: void cancel(); signals: void cancel (CSMDoc::Document *document); ///< Stop loading process. void close (CSMDoc::Document *document); ///< Close stopped loading process. }; class Loader : public QObject { Q_OBJECT std::map mDocuments; public: Loader(); virtual ~Loader(); signals: void cancel (CSMDoc::Document *document); void close (CSMDoc::Document *document); public slots: void add (CSMDoc::Document *document); void loadingStopped (CSMDoc::Document *document, bool completed, const std::string& error); void nextStage (CSMDoc::Document *document, const std::string& name, int totalRecords); void nextRecord (CSMDoc::Document *document, int records); void loadMessage (CSMDoc::Document *document, const std::string& message); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/newgame.cpp000066400000000000000000000036321264522266000225230ustar00rootroot00000000000000#include "newgame.hpp" #include #include #include #include #include #include "filewidget.hpp" #include "adjusterwidget.hpp" CSVDoc::NewGameDialogue::NewGameDialogue() { setWindowTitle ("Create New Game"); QVBoxLayout *layout = new QVBoxLayout (this); mFileWidget = new FileWidget (this); mFileWidget->setType (false); layout->addWidget (mFileWidget, 1); mAdjusterWidget = new AdjusterWidget (this); layout->addWidget (mAdjusterWidget, 1); QDialogButtonBox *buttons = new QDialogButtonBox (this); mCreate = new QPushButton ("Create", this); mCreate->setDefault (true); mCreate->setEnabled (false); buttons->addButton (mCreate, QDialogButtonBox::AcceptRole); QPushButton *cancel = new QPushButton ("Cancel", this); buttons->addButton (cancel, QDialogButtonBox::RejectRole); layout->addWidget (buttons); setLayout (layout); connect (mAdjusterWidget, SIGNAL (stateChanged (bool)), this, SLOT (stateChanged (bool))); connect (mCreate, SIGNAL (clicked()), this, SLOT (create())); connect (cancel, SIGNAL (clicked()), this, SLOT (reject())); connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), mAdjusterWidget, SLOT (setName (const QString&, bool))); QRect scr = QApplication::desktop()->screenGeometry(); QRect rect = geometry(); move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); } void CSVDoc::NewGameDialogue::setLocalData (const boost::filesystem::path& localData) { mAdjusterWidget->setLocalData (localData); } void CSVDoc::NewGameDialogue::stateChanged (bool valid) { mCreate->setEnabled (valid); } void CSVDoc::NewGameDialogue::create() { emit createRequest (mAdjusterWidget->getPath()); } void CSVDoc::NewGameDialogue::reject() { emit cancelCreateGame (); QDialog::reject(); } openmw-openmw-0.38.0/apps/opencs/view/doc/newgame.hpp000066400000000000000000000016601264522266000225270ustar00rootroot00000000000000#ifndef CSV_DOC_NEWGAME_H #define CSV_DOC_NEWGAME_H #include #include #include #ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED #define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED Q_DECLARE_METATYPE (boost::filesystem::path) #endif class QPushButton; namespace CSVDoc { class FileWidget; class AdjusterWidget; class NewGameDialogue : public QDialog { Q_OBJECT QPushButton *mCreate; FileWidget *mFileWidget; AdjusterWidget *mAdjusterWidget; public: NewGameDialogue(); void setLocalData (const boost::filesystem::path& localData); signals: void createRequest (const boost::filesystem::path& file); void cancelCreateGame (); private slots: void stateChanged (bool valid); void create(); void reject(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/operation.cpp000066400000000000000000000073771264522266000231120ustar00rootroot00000000000000#include "operation.hpp" #include #include #include #include #include "../../model/doc/document.hpp" void CSVDoc::Operation::updateLabel (int threads) { if (threads==-1 || ((threads==0)!=mStalling)) { std::string name ("unknown operation"); switch (mType) { case CSMDoc::State_Saving: name = "saving"; break; case CSMDoc::State_Verifying: name = "verifying"; break; case CSMDoc::State_Searching: name = "searching"; break; case CSMDoc::State_Merging: name = "merging"; break; } std::ostringstream stream; if ((mStalling = (threads<=0))) { stream << name << " (waiting for a free worker thread)"; } else { stream << name << " (%p%)"; } mProgressBar->setFormat (stream.str().c_str()); } } CSVDoc::Operation::Operation (int type, QWidget* parent) : mType (type), mStalling (false) { /// \todo Add a cancel button or a pop up menu with a cancel item initWidgets(); setBarColor( type); updateLabel(); /// \todo assign different progress bar colours to allow the user to distinguish easily between operation types } CSVDoc::Operation::~Operation() { delete mLayout; delete mProgressBar; delete mAbortButton; } void CSVDoc::Operation::initWidgets() { mProgressBar = new QProgressBar (); mAbortButton = new QPushButton("Abort"); mLayout = new QHBoxLayout(); mLayout->addWidget (mProgressBar); mLayout->addWidget (mAbortButton); connect (mAbortButton, SIGNAL (clicked()), this, SLOT (abortOperation())); } void CSVDoc::Operation::setProgress (int current, int max, int threads) { updateLabel (threads); mProgressBar->setRange (0, max); mProgressBar->setValue (current); } int CSVDoc::Operation::getType() const { return mType; } void CSVDoc::Operation::setBarColor (int type) { QString style ="QProgressBar {" "text-align: center;" "}" "QProgressBar::chunk {" "background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 %1, stop:.50 %2 stop: .51 %3 stop:1 %4);" "text-align: center;" "margin: 2px 1px 1p 2px;" "}"; QString topColor = "#F2F6F8"; QString bottomColor = "#E0EFF9"; QString midTopColor = "#D8E1E7"; QString midBottomColor = "#B5C6D0"; // default gray gloss // colors inspired by samples from: // http://www.colorzilla.com/gradient-editor/ switch (type) { case CSMDoc::State_Saving: topColor = "#FECCB1"; midTopColor = "#F17432"; midBottomColor = "#EA5507"; bottomColor = "#FB955E"; // red gloss #2 break; case CSMDoc::State_Searching: topColor = "#EBF1F6"; midTopColor = "#ABD3EE"; midBottomColor = "#89C3EB"; bottomColor = "#D5EBFB"; //blue gloss #4 break; case CSMDoc::State_Verifying: topColor = "#BFD255"; midTopColor = "#8EB92A"; midBottomColor = "#72AA00"; bottomColor = "#9ECB2D"; //green gloss break; case CSMDoc::State_Merging: topColor = "#F3E2C7"; midTopColor = "#C19E67"; midBottomColor = "#B68D4C"; bottomColor = "#E9D4B3"; //l Brown 3D break; default: topColor = "#F2F6F8"; bottomColor = "#E0EFF9"; midTopColor = "#D8E1E7"; midBottomColor = "#B5C6D0"; // gray gloss for undefined ops } mProgressBar->setStyleSheet(style.arg(topColor).arg(midTopColor).arg(midBottomColor).arg(bottomColor)); } QHBoxLayout *CSVDoc::Operation::getLayout() const { return mLayout; } void CSVDoc::Operation::abortOperation() { emit abortOperation (mType); } openmw-openmw-0.38.0/apps/opencs/view/doc/operation.hpp000066400000000000000000000020111264522266000230730ustar00rootroot00000000000000#ifndef CSV_DOC_OPERATION_H #define CSV_DOC_OPERATION_H #include class QHBoxLayout; class QPushButton; class QProgressBar; namespace CSVDoc { class Operation : public QObject { Q_OBJECT int mType; bool mStalling; QProgressBar *mProgressBar; QPushButton *mAbortButton; QHBoxLayout *mLayout; // not implemented Operation (const Operation&); Operation& operator= (const Operation&); void updateLabel (int threads = -1); public: Operation (int type, QWidget *parent); ~Operation(); void setProgress (int current, int max, int threads); int getType() const; QHBoxLayout *getLayout() const; private: void setBarColor (int type); void initWidgets(); signals: void abortOperation (int type); private slots: void abortOperation(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/operations.cpp000066400000000000000000000036531264522266000232660ustar00rootroot00000000000000#include "operations.hpp" #include #include #include "operation.hpp" CSVDoc::Operations::Operations() { /// \todo make widget height fixed (exactly the height required to display all operations) setFeatures (QDockWidget::NoDockWidgetFeatures); QWidget *widgetContainer = new QWidget (this); mLayout = new QVBoxLayout; widgetContainer->setLayout (mLayout); setWidget (widgetContainer); setVisible (false); setFixedHeight (widgetContainer->height()); setTitleBarWidget (new QWidget (this)); } void CSVDoc::Operations::setProgress (int current, int max, int type, int threads) { for (std::vector::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter) if ((*iter)->getType()==type) { (*iter)->setProgress (current, max, threads); return; } int oldCount = mOperations.size(); int newCount = oldCount + 1; Operation *operation = new Operation (type, this); connect (operation, SIGNAL (abortOperation (int)), this, SIGNAL (abortOperation (int))); mLayout->addLayout (operation->getLayout()); mOperations.push_back (operation); operation->setProgress (current, max, threads); if ( oldCount > 0) setFixedHeight (height()/oldCount * newCount); setVisible (true); } void CSVDoc::Operations::quitOperation (int type) { for (std::vector::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter) if ((*iter)->getType()==type) { int oldCount = mOperations.size(); int newCount = oldCount - 1; mLayout->removeItem ((*iter)->getLayout()); (*iter)->deleteLater(); mOperations.erase (iter); if (oldCount > 1) setFixedHeight (height() / oldCount * newCount); else setVisible (false); break; } } openmw-openmw-0.38.0/apps/opencs/view/doc/operations.hpp000066400000000000000000000015351264522266000232700ustar00rootroot00000000000000#ifndef CSV_DOC_OPERATIONS_H #define CSV_DOC_OPERATIONS_H #include #include class QVBoxLayout; namespace CSVDoc { class Operation; class Operations : public QDockWidget { Q_OBJECT QVBoxLayout *mLayout; std::vector mOperations; // not implemented Operations (const Operations&); Operations& operator= (const Operations&); public: Operations(); void setProgress (int current, int max, int type, int threads); ///< Implicitly starts the operation, if it is not running already. void quitOperation (int type); ///< Calling this function for an operation that is not running is a no-op. signals: void abortOperation (int type); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/runlogsubview.cpp000066400000000000000000000006601264522266000240110ustar00rootroot00000000000000#include "runlogsubview.hpp" #include CSVDoc::RunLogSubView::RunLogSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id) { QTextEdit *edit = new QTextEdit (this); edit->setDocument (document.getRunLog()); edit->setReadOnly (true); setWidget (edit); } void CSVDoc::RunLogSubView::setEditLock (bool locked) { // ignored since this SubView does not have editing } openmw-openmw-0.38.0/apps/opencs/view/doc/runlogsubview.hpp000066400000000000000000000005411264522266000240140ustar00rootroot00000000000000#ifndef CSV_DOC_RUNLOGSUBVIEW_H #define CSV_DOC_RUNLOGSUBVIEW_H #include "subview.hpp" namespace CSVDoc { class RunLogSubView : public SubView { Q_OBJECT public: RunLogSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/sizehint.cpp000066400000000000000000000004551264522266000227350ustar00rootroot00000000000000#include "sizehint.hpp" CSVDoc::SizeHintWidget::SizeHintWidget(QWidget *parent) : QWidget(parent) {} CSVDoc::SizeHintWidget::~SizeHintWidget() {} QSize CSVDoc::SizeHintWidget::sizeHint() const { return mSize; } void CSVDoc::SizeHintWidget::setSizeHint(const QSize &size) { mSize = size; } openmw-openmw-0.38.0/apps/opencs/view/doc/sizehint.hpp000066400000000000000000000006361264522266000227430ustar00rootroot00000000000000#ifndef CSV_DOC_SIZEHINT_H #define CSV_DOC_SIZEHINT_H #include #include namespace CSVDoc { class SizeHintWidget : public QWidget { QSize mSize; public: SizeHintWidget(QWidget *parent = 0); ~SizeHintWidget(); virtual QSize sizeHint() const; void setSizeHint(const QSize &size); }; } #endif // CSV_DOC_SIZEHINT_H openmw-openmw-0.38.0/apps/opencs/view/doc/startup.cpp000066400000000000000000000073171264522266000226060ustar00rootroot00000000000000#include "startup.hpp" #include #include #include #include #include #include #include #include #include QPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon) { int column = mColumn--; QPushButton *button = new QPushButton (this); button->setIcon (QIcon (icon)); button->setSizePolicy (QSizePolicy (QSizePolicy::Preferred, QSizePolicy::Preferred)); mLayout->addWidget (button, 0, column); mLayout->addWidget (new QLabel (label, this), 1, column, Qt::AlignCenter); int width = mLayout->itemAtPosition (1, column)->widget()->sizeHint().width(); if (width>mWidth) mWidth = width; return button; } QWidget *CSVDoc::StartupDialogue::createButtons() { QWidget *widget = new QWidget (this); mLayout = new QGridLayout (widget); /// \todo add icons QPushButton *loadDocument = addButton ("Edit A Content File", QIcon (":startup/edit-content")); connect (loadDocument, SIGNAL (clicked()), this, SIGNAL (loadDocument())); QPushButton *createAddon = addButton ("Create A New Addon", QIcon (":startup/create-addon")); connect (createAddon, SIGNAL (clicked()), this, SIGNAL (createAddon())); QPushButton *createGame = addButton ("Create A New Game", QIcon (":startup/create-game")); connect (createGame, SIGNAL (clicked()), this, SIGNAL (createGame())); for (int i=0; i<3; ++i) mLayout->setColumnMinimumWidth (i, mWidth); mLayout->setRowMinimumHeight (0, mWidth); mLayout->setSizeConstraint (QLayout::SetMinimumSize); mLayout->setHorizontalSpacing (32); mLayout->setContentsMargins (16, 16, 16, 8); loadDocument->setIconSize (QSize (mWidth, mWidth)); createGame->setIconSize (QSize (mWidth, mWidth)); createAddon->setIconSize (QSize (mWidth, mWidth)); widget->setLayout (mLayout); return widget; } QWidget *CSVDoc::StartupDialogue::createTools() { QWidget *widget = new QWidget (this); QHBoxLayout *layout = new QHBoxLayout (widget); layout->setDirection (QBoxLayout::RightToLeft); layout->setContentsMargins (4, 4, 4, 4); QPushButton *config = new QPushButton (widget); config->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); config->setIcon (QIcon (":startup/configure")); config->setToolTip ("Open user settings"); layout->addWidget (config); layout->addWidget (new QWidget, 1); // dummy widget; stops buttons from taking all the space widget->setLayout (layout); connect (config, SIGNAL (clicked()), this, SIGNAL (editConfig())); return widget; } CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) { setWindowTitle ("OpenMW-CS"); QVBoxLayout *layout = new QVBoxLayout (this); layout->setContentsMargins (0, 0, 0, 0); layout->addWidget (createButtons()); layout->addWidget (createTools()); /// \todo remove this label once we are feature complete and convinced that this thing is /// working properly. QLabel *warning = new QLabel ("WARNING: OpenMW-CS is in alpha stage.

The editor is not feature complete and not sufficiently tested.
In theory your data should be safe. But we strongly advice to make backups regularly if you are working with live data.
"); QFont font; font.setPointSize (12); font.setBold (true); warning->setFont (font); warning->setWordWrap (true); layout->addWidget (warning, 1); setLayout (layout); QRect scr = QApplication::desktop()->screenGeometry(); QRect rect = geometry(); move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); } openmw-openmw-0.38.0/apps/opencs/view/doc/startup.hpp000066400000000000000000000013101264522266000225760ustar00rootroot00000000000000#ifndef CSV_DOC_STARTUP_H #define CSV_DOC_STARTUP_H #include class QGridLayout; class QString; class QPushButton; class QWidget; class QIcon; namespace CSVDoc { class StartupDialogue : public QWidget { Q_OBJECT private: int mWidth; int mColumn; QGridLayout *mLayout; QPushButton *addButton (const QString& label, const QIcon& icon); QWidget *createButtons(); QWidget *createTools(); public: StartupDialogue(); signals: void createGame(); void createAddon(); void loadDocument(); void editConfig(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/subview.cpp000066400000000000000000000026311264522266000225620ustar00rootroot00000000000000#include "subview.hpp" #include "view.hpp" #include #include #include bool CSVDoc::SubView::event (QEvent *event) { if (event->type()==QEvent::ShortcutOverride) { QKeyEvent *keyEvent = static_cast (event); if (keyEvent->key()==Qt::Key_W && keyEvent->modifiers()==(Qt::ShiftModifier | Qt::ControlModifier)) emit closeRequest(); return true; } return QDockWidget::event (event); } CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) { /// \todo add a button to the title bar that clones this sub view setWindowTitle (QString::fromUtf8 (mUniversalId.toString().c_str())); setAttribute(Qt::WA_DeleteOnClose); } CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const { return mUniversalId; } void CSVDoc::SubView::setStatusBar (bool show) {} void CSVDoc::SubView::useHint (const std::string& hint) {} void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id) { mUniversalId = id; setWindowTitle (QString::fromUtf8(mUniversalId.toString().c_str())); emit universalIdChanged (mUniversalId); } void CSVDoc::SubView::closeEvent (QCloseEvent *event) { emit updateSubViewIndicies (this); } std::string CSVDoc::SubView::getTitle() const { return mUniversalId.toString(); } void CSVDoc::SubView::closeRequest() { emit closeRequest (this); } openmw-openmw-0.38.0/apps/opencs/view/doc/subview.hpp000066400000000000000000000030561264522266000225710ustar00rootroot00000000000000#ifndef CSV_DOC_SUBVIEW_H #define CSV_DOC_SUBVIEW_H #include "../../model/doc/document.hpp" #include "../../model/world/universalid.hpp" #include "subviewfactory.hpp" #include class QUndoStack; namespace CSMWorld { class Data; } namespace CSVDoc { class View; class SubView : public QDockWidget { Q_OBJECT CSMWorld::UniversalId mUniversalId; // not implemented SubView (const SubView&); SubView& operator= (SubView&); protected: void setUniversalId(const CSMWorld::UniversalId& id); bool event (QEvent *event); public: SubView (const CSMWorld::UniversalId& id); CSMWorld::UniversalId getUniversalId() const; virtual void setEditLock (bool locked) = 0; virtual void setStatusBar (bool show); ///< Default implementation: ignored virtual void useHint (const std::string& hint); ///< Default implementation: ignored virtual std::string getTitle() const; private: void closeEvent (QCloseEvent *event); signals: void focusId (const CSMWorld::UniversalId& universalId, const std::string& hint); void closeRequest (SubView *subView); void updateTitle(); void updateSubViewIndicies (SubView *view = 0); void universalIdChanged (const CSMWorld::UniversalId& universalId); protected slots: void closeRequest(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/subviewfactory.cpp000066400000000000000000000022151264522266000241500ustar00rootroot00000000000000#include "subviewfactory.hpp" #include #include CSVDoc::SubViewFactoryBase::SubViewFactoryBase() {} CSVDoc::SubViewFactoryBase::~SubViewFactoryBase() {} CSVDoc::SubViewFactoryManager::SubViewFactoryManager() {} CSVDoc::SubViewFactoryManager::~SubViewFactoryManager() { for (std::map::iterator iter (mSubViewFactories.begin()); iter!=mSubViewFactories.end(); ++iter) delete iter->second; } void CSVDoc::SubViewFactoryManager::add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory) { assert (mSubViewFactories.find (id)==mSubViewFactories.end()); mSubViewFactories.insert (std::make_pair (id, factory)); } CSVDoc::SubView *CSVDoc::SubViewFactoryManager::makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) { std::map::iterator iter = mSubViewFactories.find (id.getType()); if (iter==mSubViewFactories.end()) throw std::runtime_error ("Failed to create a sub view for: " + id.toString()); return iter->second->makeSubView (id, document); } openmw-openmw-0.38.0/apps/opencs/view/doc/subviewfactory.hpp000066400000000000000000000026451264522266000241640ustar00rootroot00000000000000#ifndef CSV_DOC_SUBVIEWFACTORY_H #define CSV_DOC_SUBVIEWFACTORY_H #include #include "../../model/world/universalid.hpp" namespace CSMDoc { class Document; } namespace CSVDoc { class SubView; class SubViewFactoryBase { // not implemented SubViewFactoryBase (const SubViewFactoryBase&); SubViewFactoryBase& operator= (const SubViewFactoryBase&); public: SubViewFactoryBase(); virtual ~SubViewFactoryBase(); virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) = 0; ///< The ownership of the returned sub view is not transferred. }; class SubViewFactoryManager { std::map mSubViewFactories; // not implemented SubViewFactoryManager (const SubViewFactoryManager&); SubViewFactoryManager& operator= (const SubViewFactoryManager&); public: SubViewFactoryManager(); ~SubViewFactoryManager(); void add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory); ///< The ownership of \a factory is transferred to this. SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); ///< The ownership of the returned sub view is not transferred. }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/subviewfactoryimp.hpp000066400000000000000000000026431264522266000246700ustar00rootroot00000000000000#ifndef CSV_DOC_SUBVIEWFACTORYIMP_H #define CSV_DOC_SUBVIEWFACTORYIMP_H #include "../../model/doc/document.hpp" #include "subviewfactory.hpp" namespace CSVDoc { template class SubViewFactory : public SubViewFactoryBase { public: virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); }; template CSVDoc::SubView *SubViewFactory::makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) { return new SubViewT (id, document); } template class SubViewFactoryWithCreator : public SubViewFactoryBase { bool mSorting; public: SubViewFactoryWithCreator (bool sorting = true); virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); }; template SubViewFactoryWithCreator::SubViewFactoryWithCreator (bool sorting) : mSorting (sorting) {} template CSVDoc::SubView *SubViewFactoryWithCreator::makeSubView ( const CSMWorld::UniversalId& id, CSMDoc::Document& document) { return new SubViewT (id, document, CreatorFactoryT(), mSorting); } } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/view.cpp000066400000000000000000000675641264522266000220700ustar00rootroot00000000000000#include "view.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/prefs/state.hpp" #include "../../model/world/idtable.hpp" #include "../world/subviews.hpp" #include "../world/tablesubview.hpp" #include "../tools/subviews.hpp" #include "viewmanager.hpp" #include "operations.hpp" #include "subview.hpp" #include "globaldebugprofilemenu.hpp" #include "runlogsubview.hpp" #include "subviewfactoryimp.hpp" void CSVDoc::View::closeEvent (QCloseEvent *event) { if (!mViewManager.closeRequest (this)) event->ignore(); else { // closeRequest() returns true if last document mViewManager.removeDocAndView(mDocument); } } void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("&File")); QAction *newGame = new QAction (tr ("New Game"), this); connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest())); file->addAction (newGame); QAction *newAddon = new QAction (tr ("New Addon"), this); connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest())); file->addAction (newAddon); QAction *open = new QAction (tr ("&Open"), this); connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest())); file->addAction (open); mSave = new QAction (tr ("&Save"), this); connect (mSave, SIGNAL (triggered()), this, SLOT (save())); file->addAction (mSave); mVerify = new QAction (tr ("&Verify"), this); connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); file->addAction (mVerify); mMerge = new QAction (tr ("Merge"), this); connect (mMerge, SIGNAL (triggered()), this, SLOT (merge())); file->addAction (mMerge); QAction *loadErrors = new QAction (tr ("Load Error Log"), this); connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog())); file->addAction (loadErrors); QAction *meta = new QAction (tr ("Meta Data"), this); connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView())); file->addAction (meta); QAction *close = new QAction (tr ("&Close"), this); connect (close, SIGNAL (triggered()), this, SLOT (close())); file->addAction(close); QAction *exit = new QAction (tr ("&Exit"), this); connect (exit, SIGNAL (triggered()), this, SLOT (exit())); connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); file->addAction(exit); } void CSVDoc::View::setupEditMenu() { QMenu *edit = menuBar()->addMenu (tr ("&Edit")); mUndo = mDocument->getUndoStack().createUndoAction (this, tr("&Undo")); mUndo->setShortcuts (QKeySequence::Undo); edit->addAction (mUndo); mRedo= mDocument->getUndoStack().createRedoAction (this, tr("&Redo")); mRedo->setShortcuts (QKeySequence::Redo); edit->addAction (mRedo); QAction *userSettings = new QAction (tr ("&Preferences"), this); connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest())); edit->addAction (userSettings); QAction *search = new QAction (tr ("Search"), this); connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView())); edit->addAction (search); } void CSVDoc::View::setupViewMenu() { QMenu *view = menuBar()->addMenu (tr ("&View")); QAction *newWindow = new QAction (tr ("&New View"), this); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); view->addAction (newWindow); mShowStatusBar = new QAction (tr ("Show Status Bar"), this); mShowStatusBar->setCheckable (true); connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); view->addAction (mShowStatusBar); QAction *filters = new QAction (tr ("Filters"), this); connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView())); view->addAction (filters); } void CSVDoc::View::setupWorldMenu() { QMenu *world = menuBar()->addMenu (tr ("&World")); QAction *regions = new QAction (tr ("Regions"), this); connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView())); world->addAction (regions); QAction *cells = new QAction (tr ("Cells"), this); connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); world->addAction (cells); QAction *referenceables = new QAction (tr ("Objects"), this); connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView())); world->addAction (referenceables); QAction *references = new QAction (tr ("Instances"), this); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); world->addAction (references); QAction *grid = new QAction (tr ("Pathgrid"), this); connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); world->addAction (grid); world->addSeparator(); // items that don't represent single record lists follow here QAction *regionMap = new QAction (tr ("Region Map"), this); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); world->addAction (regionMap); } void CSVDoc::View::setupMechanicsMenu() { QMenu *mechanics = menuBar()->addMenu (tr ("&Mechanics")); QAction *globals = new QAction (tr ("Globals"), this); connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView())); mechanics->addAction (globals); QAction *gmsts = new QAction (tr ("Game settings"), this); connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView())); mechanics->addAction (gmsts); QAction *scripts = new QAction (tr ("Scripts"), this); connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView())); mechanics->addAction (scripts); QAction *spells = new QAction (tr ("Spells"), this); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); mechanics->addAction (spells); QAction *enchantments = new QAction (tr ("Enchantments"), this); connect (enchantments, SIGNAL (triggered()), this, SLOT (addEnchantmentsSubView())); mechanics->addAction (enchantments); QAction *effects = new QAction (tr ("Magic Effects"), this); connect (effects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView())); mechanics->addAction (effects); QAction *startScripts = new QAction (tr ("Start Scripts"), this); connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView())); mechanics->addAction (startScripts); } void CSVDoc::View::setupCharacterMenu() { QMenu *characters = menuBar()->addMenu (tr ("Characters")); QAction *skills = new QAction (tr ("Skills"), this); connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView())); characters->addAction (skills); QAction *classes = new QAction (tr ("Classes"), this); connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); characters->addAction (classes); QAction *factions = new QAction (tr ("Factions"), this); connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView())); characters->addAction (factions); QAction *races = new QAction (tr ("Races"), this); connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView())); characters->addAction (races); QAction *birthsigns = new QAction (tr ("Birthsigns"), this); connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); characters->addAction (birthsigns); QAction *topics = new QAction (tr ("Topics"), this); connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView())); characters->addAction (topics); QAction *journals = new QAction (tr ("Journals"), this); connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); characters->addAction (journals); QAction *topicInfos = new QAction (tr ("Topic Infos"), this); connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView())); characters->addAction (topicInfos); QAction *journalInfos = new QAction (tr ("Journal Infos"), this); connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView())); characters->addAction (journalInfos); QAction *bodyParts = new QAction (tr ("Body Parts"), this); connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView())); characters->addAction (bodyParts); } void CSVDoc::View::setupAssetsMenu() { QMenu *assets = menuBar()->addMenu (tr ("&Assets")); QAction *sounds = new QAction (tr ("Sounds"), this); connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView())); assets->addAction (sounds); QAction *soundGens = new QAction (tr ("Sound Generators"), this); connect (soundGens, SIGNAL (triggered()), this, SLOT (addSoundGensSubView())); assets->addAction (soundGens); assets->addSeparator(); // resources follow here QAction *meshes = new QAction (tr ("Meshes"), this); connect (meshes, SIGNAL (triggered()), this, SLOT (addMeshesSubView())); assets->addAction (meshes); QAction *icons = new QAction (tr ("Icons"), this); connect (icons, SIGNAL (triggered()), this, SLOT (addIconsSubView())); assets->addAction (icons); QAction *musics = new QAction (tr ("Music"), this); connect (musics, SIGNAL (triggered()), this, SLOT (addMusicsSubView())); assets->addAction (musics); QAction *soundsRes = new QAction (tr ("Sound Files"), this); connect (soundsRes, SIGNAL (triggered()), this, SLOT (addSoundsResSubView())); assets->addAction (soundsRes); QAction *textures = new QAction (tr ("Textures"), this); connect (textures, SIGNAL (triggered()), this, SLOT (addTexturesSubView())); assets->addAction (textures); QAction *videos = new QAction (tr ("Videos"), this); connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView())); assets->addAction (videos); } void CSVDoc::View::setupDebugMenu() { QMenu *debug = menuBar()->addMenu (tr ("Debug")); QAction *profiles = new QAction (tr ("Debug Profiles"), this); connect (profiles, SIGNAL (triggered()), this, SLOT (addDebugProfilesSubView())); debug->addAction (profiles); debug->addSeparator(); mGlobalDebugProfileMenu = new GlobalDebugProfileMenu ( &dynamic_cast (*mDocument->getData().getTableModel ( CSMWorld::UniversalId::Type_DebugProfiles)), this); connect (mGlobalDebugProfileMenu, SIGNAL (triggered (const std::string&)), this, SLOT (run (const std::string&))); QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu); runDebug->setText (tr ("Run OpenMW")); mStopDebug = new QAction (tr ("Shutdown OpenMW"), this); connect (mStopDebug, SIGNAL (triggered()), this, SLOT (stop())); debug->addAction (mStopDebug); QAction *runLog = new QAction (tr ("Run Log"), this); connect (runLog, SIGNAL (triggered()), this, SLOT (addRunLogSubView())); debug->addAction (runLog); } void CSVDoc::View::setupUi() { setupFileMenu(); setupEditMenu(); setupViewMenu(); setupWorldMenu(); setupMechanicsMenu(); setupCharacterMenu(); setupAssetsMenu(); setupDebugMenu(); } void CSVDoc::View::updateTitle() { std::ostringstream stream; stream << mDocument->getSavePath().filename().string(); if (mDocument->getState() & CSMDoc::State_Modified) stream << " *"; if (mViewTotal>1) stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]"; CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; bool hideTitle = windows["hide-subview"].isTrue() && mSubViews.size()==1 && !mSubViews.at (0)->isFloating(); if (hideTitle) stream << " - " << mSubViews.at (0)->getTitle(); setWindowTitle (QString::fromUtf8(stream.str().c_str())); } void CSVDoc::View::updateSubViewIndicies(SubView *view) { CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; if(view && mSubViews.contains(view)) { mSubViews.removeOne(view); // adjust (reduce) the scroll area (even floating), except when it is "Scrollbar Only" if (windows["mainwindow-scrollbar"].toString() == "Grow then Scroll") updateScrollbar(); } bool hideTitle = windows["hide-subview"].isTrue() && mSubViews.size()==1 && !mSubViews.at (0)->isFloating(); updateTitle(); foreach (SubView *subView, mSubViews) { if (!subView->isFloating()) { if (hideTitle) { subView->setTitleBarWidget (new QWidget (this)); subView->setWindowTitle (QString::fromUtf8 (subView->getTitle().c_str())); } else { delete subView->titleBarWidget(); subView->setTitleBarWidget (0); } } } } void CSVDoc::View::updateActions() { bool editing = !(mDocument->getState() & CSMDoc::State_Locked); bool running = mDocument->getState() & CSMDoc::State_Running; for (std::vector::iterator iter (mEditingActions.begin()); iter!=mEditingActions.end(); ++iter) (*iter)->setEnabled (editing); mUndo->setEnabled (editing & mDocument->getUndoStack().canUndo()); mRedo->setEnabled (editing & mDocument->getUndoStack().canRedo()); mSave->setEnabled (!(mDocument->getState() & CSMDoc::State_Saving) && !running); mVerify->setEnabled (!(mDocument->getState() & CSMDoc::State_Verifying)); mGlobalDebugProfileMenu->updateActions (running); mStopDebug->setEnabled (running); mMerge->setEnabled (mDocument->getContentFiles().size()>1 && !(mDocument->getState() & CSMDoc::State_Merging)); } CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews), mScroll(0), mScrollbarOnly(false) { CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; int width = std::max (windows["default-width"].toInt(), 300); int height = std::max (windows["default-height"].toInt(), 300); resize (width, height); mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); if (windows["mainwindow-scrollbar"].toString() == "Grow Only") { setCentralWidget (&mSubViewWindow); } else { mScroll = new QScrollArea(this); mScroll->setWidgetResizable(true); mScroll->setWidget(&mSubViewWindow); setCentralWidget(mScroll); } mOperations = new Operations; addDockWidget (Qt::BottomDockWidgetArea, mOperations); setContextMenuPolicy(Qt::NoContextMenu); updateTitle(); setupUi(); updateActions(); CSVWorld::addSubViewFactories (mSubViewFactory); CSVTools::addSubViewFactories (mSubViewFactory); mSubViewFactory.add (CSMWorld::UniversalId::Type_RunLog, new SubViewFactory); connect (mOperations, SIGNAL (abortOperation (int)), this, SLOT (abortOperation (int))); connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), this, SLOT (settingChanged (const CSMPrefs::Setting *))); } CSVDoc::View::~View() { } const CSMDoc::Document *CSVDoc::View::getDocument() const { return mDocument; } CSMDoc::Document *CSVDoc::View::getDocument() { return mDocument; } void CSVDoc::View::setIndex (int viewIndex, int totalViews) { mViewIndex = viewIndex; mViewTotal = totalViews; updateTitle(); } void CSVDoc::View::updateDocumentState() { updateTitle(); updateActions(); static const int operations[] = { CSMDoc::State_Saving, CSMDoc::State_Verifying, CSMDoc::State_Searching, CSMDoc::State_Merging, -1 // end marker }; int state = mDocument->getState() ; for (int i=0; operations[i]!=-1; ++i) if (!(state & operations[i])) mOperations->quitOperation (operations[i]); QList subViews = findChildren(); for (QList::iterator iter (subViews.begin()); iter!=subViews.end(); ++iter) (*iter)->setEditLock (state & CSMDoc::State_Locked); } void CSVDoc::View::updateProgress (int current, int max, int type, int threads) { mOperations->setProgress (current, max, type, threads); } void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::string& hint) { CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; bool isReferenceable = id.getClass() == CSMWorld::UniversalId::Class_RefRecord; // User setting to reuse sub views (on a per top level view basis) if (windows["reuse"].isTrue()) { foreach(SubView *sb, mSubViews) { bool isSubViewReferenceable = sb->getUniversalId().getType() == CSMWorld::UniversalId::Type_Referenceable; if((isReferenceable && isSubViewReferenceable && id.getId() == sb->getUniversalId().getId()) || (!isReferenceable && id == sb->getUniversalId())) { sb->setFocus(); if (!hint.empty()) sb->useHint (hint); return; } } } if (mScroll) QObject::connect(mScroll->horizontalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(moveScrollBarToEnd(int,int))); // User setting for limiting the number of sub views per top level view. // Automatically open a new top level view if this number is exceeded // // If the sub view limit setting is one, the sub view title bar is hidden and the // text in the main title bar is adjusted accordingly if(mSubViews.size() >= windows["max-subviews"].toInt()) // create a new top level view { mViewManager.addView(mDocument, id, hint); return; } SubView *view = NULL; if(isReferenceable) { view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument); } else { view = mSubViewFactory.makeSubView (id, *mDocument); } assert(view); view->setParent(this); mSubViews.append(view); // only after assert int minWidth = windows["minimum-width"].toInt(); view->setMinimumWidth (minWidth); view->setStatusBar (mShowStatusBar->isChecked()); // Work out how to deal with additional subviews // // Policy for "Grow then Scroll": // // - Increase the horizontal width of the mainwindow until it becomes greater than or equal // to the screen (monitor) width. // - Move the mainwindow position sideways if necessary to fit within the screen. // - Any more additions increases the size of the mSubViewWindow (horizontal scrollbar // should become visible) // - Move the scroll bar to the newly added subview // mScrollbarOnly = windows["mainwindow-scrollbar"].toString() == "Scrollbar Only"; QDesktopWidget *dw = QApplication::desktop(); QRect rect; if (windows["grow-limit"].isTrue()) rect = dw->screenGeometry(this); else rect = dw->screenGeometry(dw->screen(dw->screenNumber(this))); if (!mScrollbarOnly && mScroll && mSubViews.size() > 1) { int newWidth = width()+minWidth; int frameWidth = frameGeometry().width() - width(); if (newWidth+frameWidth <= rect.width()) { resize(newWidth, height()); // WARNING: below code assumes that new subviews are added to the right if (x() > rect.width()-(newWidth+frameWidth)) move(rect.width()-(newWidth+frameWidth), y()); // shift left to stay within the screen } else { // full width resize(rect.width()-frameWidth, height()); mSubViewWindow.setMinimumWidth(mSubViewWindow.width()+minWidth); move(0, y()); } } mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); updateSubViewIndicies(); connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this, SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&))); connect (view, SIGNAL (closeRequest (SubView *)), this, SLOT (closeRequest (SubView *))); connect (view, SIGNAL (updateTitle()), this, SLOT (updateTitle())); connect (view, SIGNAL (updateSubViewIndicies (SubView *)), this, SLOT (updateSubViewIndicies (SubView *))); view->show(); if (!hint.empty()) view->useHint (hint); } void CSVDoc::View::moveScrollBarToEnd(int min, int max) { if (mScroll) { mScroll->horizontalScrollBar()->setValue(max); QObject::disconnect(mScroll->horizontalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(moveScrollBarToEnd(int,int))); } } void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting) { if (*setting=="Windows/hide-subview") updateSubViewIndicies (0); else if (*setting=="Windows/mainwindow-scrollbar") { if (setting->toString()!="Grow Only") { if (mScroll) { if (setting->toString()=="Scrollbar Only") { mScrollbarOnly = true; mSubViewWindow.setMinimumWidth(0); } else if (mScrollbarOnly) { mScrollbarOnly = false; updateScrollbar(); } } else { mScroll = new QScrollArea(this); mScroll->setWidgetResizable(true); mScroll->setWidget(&mSubViewWindow); setCentralWidget(mScroll); } } else if (mScroll) { mScroll->takeWidget(); setCentralWidget (&mSubViewWindow); mScroll->deleteLater(); mScroll = 0; } } } void CSVDoc::View::newView() { mViewManager.addView (mDocument); } void CSVDoc::View::save() { mDocument->save(); } void CSVDoc::View::verify() { addSubView (mDocument->verify()); } void CSVDoc::View::addGlobalsSubView() { addSubView (CSMWorld::UniversalId::Type_Globals); } void CSVDoc::View::addGmstsSubView() { addSubView (CSMWorld::UniversalId::Type_Gmsts); } void CSVDoc::View::addSkillsSubView() { addSubView (CSMWorld::UniversalId::Type_Skills); } void CSVDoc::View::addClassesSubView() { addSubView (CSMWorld::UniversalId::Type_Classes); } void CSVDoc::View::addFactionsSubView() { addSubView (CSMWorld::UniversalId::Type_Factions); } void CSVDoc::View::addRacesSubView() { addSubView (CSMWorld::UniversalId::Type_Races); } void CSVDoc::View::addSoundsSubView() { addSubView (CSMWorld::UniversalId::Type_Sounds); } void CSVDoc::View::addScriptsSubView() { addSubView (CSMWorld::UniversalId::Type_Scripts); } void CSVDoc::View::addRegionsSubView() { addSubView (CSMWorld::UniversalId::Type_Regions); } void CSVDoc::View::addBirthsignsSubView() { addSubView (CSMWorld::UniversalId::Type_Birthsigns); } void CSVDoc::View::addSpellsSubView() { addSubView (CSMWorld::UniversalId::Type_Spells); } void CSVDoc::View::addCellsSubView() { addSubView (CSMWorld::UniversalId::Type_Cells); } void CSVDoc::View::addReferenceablesSubView() { addSubView (CSMWorld::UniversalId::Type_Referenceables); } void CSVDoc::View::addReferencesSubView() { addSubView (CSMWorld::UniversalId::Type_References); } void CSVDoc::View::addRegionMapSubView() { addSubView (CSMWorld::UniversalId::Type_RegionMap); } void CSVDoc::View::addFiltersSubView() { addSubView (CSMWorld::UniversalId::Type_Filters); } void CSVDoc::View::addTopicsSubView() { addSubView (CSMWorld::UniversalId::Type_Topics); } void CSVDoc::View::addJournalsSubView() { addSubView (CSMWorld::UniversalId::Type_Journals); } void CSVDoc::View::addTopicInfosSubView() { addSubView (CSMWorld::UniversalId::Type_TopicInfos); } void CSVDoc::View::addJournalInfosSubView() { addSubView (CSMWorld::UniversalId::Type_JournalInfos); } void CSVDoc::View::addEnchantmentsSubView() { addSubView (CSMWorld::UniversalId::Type_Enchantments); } void CSVDoc::View::addBodyPartsSubView() { addSubView (CSMWorld::UniversalId::Type_BodyParts); } void CSVDoc::View::addSoundGensSubView() { addSubView (CSMWorld::UniversalId::Type_SoundGens); } void CSVDoc::View::addMeshesSubView() { addSubView (CSMWorld::UniversalId::Type_Meshes); } void CSVDoc::View::addIconsSubView() { addSubView (CSMWorld::UniversalId::Type_Icons); } void CSVDoc::View::addMusicsSubView() { addSubView (CSMWorld::UniversalId::Type_Musics); } void CSVDoc::View::addSoundsResSubView() { addSubView (CSMWorld::UniversalId::Type_SoundsRes); } void CSVDoc::View::addMagicEffectsSubView() { addSubView (CSMWorld::UniversalId::Type_MagicEffects); } void CSVDoc::View::addTexturesSubView() { addSubView (CSMWorld::UniversalId::Type_Textures); } void CSVDoc::View::addVideosSubView() { addSubView (CSMWorld::UniversalId::Type_Videos); } void CSVDoc::View::addDebugProfilesSubView() { addSubView (CSMWorld::UniversalId::Type_DebugProfiles); } void CSVDoc::View::addRunLogSubView() { addSubView (CSMWorld::UniversalId::Type_RunLog); } void CSVDoc::View::addPathgridSubView() { addSubView (CSMWorld::UniversalId::Type_Pathgrids); } void CSVDoc::View::addStartScriptsSubView() { addSubView (CSMWorld::UniversalId::Type_StartScripts); } void CSVDoc::View::addSearchSubView() { addSubView (mDocument->newSearch()); } void CSVDoc::View::addMetaDataSubView() { addSubView (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_MetaData, "sys::meta")); } void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); updateActions(); } CSVDoc::Operations *CSVDoc::View::getOperations() const { return mOperations; } void CSVDoc::View::exit() { emit exitApplicationRequest (this); } void CSVDoc::View::resizeViewWidth (int width) { if (width >= 0) resize (width, geometry().height()); } void CSVDoc::View::resizeViewHeight (int height) { if (height >= 0) resize (geometry().width(), height); } void CSVDoc::View::toggleShowStatusBar (bool show) { foreach (QObject *view, mSubViewWindow.children()) { if (CSVDoc::SubView *subView = dynamic_cast (view)) subView->setStatusBar (show); } } void CSVDoc::View::toggleStatusBar(bool checked) { mShowStatusBar->setChecked(checked); } void CSVDoc::View::loadErrorLog() { addSubView (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_LoadErrorLog, 0)); } void CSVDoc::View::run (const std::string& profile, const std::string& startupInstruction) { mDocument->startRunning (profile, startupInstruction); } void CSVDoc::View::stop() { mDocument->stopRunning(); } void CSVDoc::View::closeRequest (SubView *subView) { CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; if (mSubViews.size()>1 || mViewTotal<=1 || !windows["hide-subview"].isTrue()) { subView->deleteLater(); mSubViews.removeOne (subView); } else if (mViewManager.closeRequest (this)) mViewManager.removeDocAndView (mDocument); } void CSVDoc::View::updateScrollbar() { QRect rect; QWidget *topLevel = QApplication::topLevelAt(pos()); if (topLevel) rect = topLevel->rect(); else rect = this->rect(); int newWidth = 0; for (int i = 0; i < mSubViews.size(); ++i) { newWidth += mSubViews[i]->width(); } int frameWidth = frameGeometry().width() - width(); if ((newWidth+frameWidth) >= rect.width()) mSubViewWindow.setMinimumWidth(newWidth); else mSubViewWindow.setMinimumWidth(0); } void CSVDoc::View::merge() { emit mergeDocument (mDocument); } openmw-openmw-0.38.0/apps/opencs/view/doc/view.hpp000066400000000000000000000122721264522266000220570ustar00rootroot00000000000000#ifndef CSV_DOC_VIEW_H #define CSV_DOC_VIEW_H #include #include #include #include "subviewfactory.hpp" class QAction; class QDockWidget; class QScrollArea; namespace CSMDoc { class Document; } namespace CSMWorld { class UniversalId; } namespace CSMPrefs { class Setting; } namespace CSVDoc { class ViewManager; class Operations; class GlobalDebugProfileMenu; class View : public QMainWindow { Q_OBJECT ViewManager& mViewManager; CSMDoc::Document *mDocument; int mViewIndex; int mViewTotal; QList mSubViews; QAction *mUndo; QAction *mRedo; QAction *mSave; QAction *mVerify; QAction *mShowStatusBar; QAction *mStopDebug; QAction *mMerge; std::vector mEditingActions; Operations *mOperations; SubViewFactoryManager mSubViewFactory; QMainWindow mSubViewWindow; GlobalDebugProfileMenu *mGlobalDebugProfileMenu; QScrollArea *mScroll; bool mScrollbarOnly; // not implemented View (const View&); View& operator= (const View&); private: void closeEvent (QCloseEvent *event); void setupFileMenu(); void setupEditMenu(); void setupViewMenu(); void setupWorldMenu(); void setupMechanicsMenu(); void setupCharacterMenu(); void setupAssetsMenu(); void setupDebugMenu(); void setupUi(); void updateActions(); void exitApplication(); /// User preference function void resizeViewWidth (int width); /// User preference function void resizeViewHeight (int height); void updateScrollbar(); public: View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); ///< The ownership of \a document is not transferred to *this. virtual ~View(); const CSMDoc::Document *getDocument() const; CSMDoc::Document *getDocument(); void setIndex (int viewIndex, int totalViews); void updateDocumentState(); void updateProgress (int current, int max, int type, int threads); void toggleStatusBar(bool checked); Operations *getOperations() const; signals: void newGameRequest(); void newAddonRequest(); void loadDocumentRequest(); void exitApplicationRequest (CSVDoc::View *view); void editSettingsRequest(); void mergeDocument (CSMDoc::Document *document); public slots: void addSubView (const CSMWorld::UniversalId& id, const std::string& hint = ""); ///< \param hint Suggested view point (e.g. coordinates in a 3D scene or a line number /// in a script). void abortOperation (int type); void updateTitle(); // called when subviews are added or removed void updateSubViewIndicies (SubView *view = 0); private slots: void settingChanged (const CSMPrefs::Setting *setting); void newView(); void save(); void exit(); void verify(); void addGlobalsSubView(); void addGmstsSubView(); void addSkillsSubView(); void addClassesSubView(); void addFactionsSubView(); void addRacesSubView(); void addSoundsSubView(); void addScriptsSubView(); void addRegionsSubView(); void addBirthsignsSubView(); void addSpellsSubView(); void addCellsSubView(); void addReferenceablesSubView(); void addReferencesSubView(); void addRegionMapSubView(); void addFiltersSubView(); void addTopicsSubView(); void addJournalsSubView(); void addTopicInfosSubView(); void addJournalInfosSubView(); void addEnchantmentsSubView(); void addBodyPartsSubView(); void addSoundGensSubView(); void addMagicEffectsSubView(); void addMeshesSubView(); void addIconsSubView(); void addMusicsSubView(); void addSoundsResSubView(); void addTexturesSubView(); void addVideosSubView(); void addDebugProfilesSubView(); void addRunLogSubView(); void addPathgridSubView(); void addStartScriptsSubView(); void addSearchSubView(); void addMetaDataSubView(); void toggleShowStatusBar (bool show); void loadErrorLog(); void run (const std::string& profile, const std::string& startupInstruction = ""); void stop(); void closeRequest (SubView *subView); void moveScrollBarToEnd(int min, int max); void merge(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/doc/viewmanager.cpp000066400000000000000000000405561264522266000234130ustar00rootroot00000000000000#include "viewmanager.hpp" #include #include #include #include #include #include #include "../../model/doc/documentmanager.hpp" #include "../../model/doc/document.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/universalid.hpp" #include "../../model/world/idcompletionmanager.hpp" #include "../../model/prefs/state.hpp" #include "../world/util.hpp" #include "../world/enumdelegate.hpp" #include "../world/vartypedelegate.hpp" #include "../world/recordstatusdelegate.hpp" #include "../world/idtypedelegate.hpp" #include "../world/idcompletiondelegate.hpp" #include "../world/colordelegate.hpp" #include "view.hpp" void CSVDoc::ViewManager::updateIndices() { std::map > documents; for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) { std::map >::iterator document = documents.find ((*iter)->getDocument()); if (document==documents.end()) document = documents.insert ( std::make_pair ((*iter)->getDocument(), std::make_pair (0, countViews ((*iter)->getDocument())))). first; (*iter)->setIndex (document->second.first++, document->second.second); } } CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) : mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false) { mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection; mDelegateFactories->add (CSMWorld::ColumnBase::Display_GmstVarType, new CSVWorld::VarTypeDelegateFactory (ESM::VT_None, ESM::VT_String, ESM::VT_Int, ESM::VT_Float)); mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType, new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float)); mDelegateFactories->add (CSMWorld::ColumnBase::Display_RecordState, new CSVWorld::RecordStatusDelegateFactory()); mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType, new CSVWorld::IdTypeDelegateFactory()); mDelegateFactories->add (CSMWorld::ColumnBase::Display_Colour, new CSVWorld::ColorDelegateFactory()); std::vector idCompletionColumns = CSMWorld::IdCompletionManager::getDisplayTypes(); for (std::vector::const_iterator current = idCompletionColumns.begin(); current != idCompletionColumns.end(); ++current) { mDelegateFactories->add(*current, new CSVWorld::IdCompletionDelegateFactory()); } struct Mapping { CSMWorld::ColumnBase::Display mDisplay; CSMWorld::Columns::ColumnId mColumnId; bool mAllowNone; }; static const Mapping sMapping[] = { { CSMWorld::ColumnBase::Display_Specialisation, CSMWorld::Columns::ColumnId_Specialisation, false }, { CSMWorld::ColumnBase::Display_Attribute, CSMWorld::Columns::ColumnId_Attribute, true }, { CSMWorld::ColumnBase::Display_SpellType, CSMWorld::Columns::ColumnId_SpellType, false }, { CSMWorld::ColumnBase::Display_ApparatusType, CSMWorld::Columns::ColumnId_ApparatusType, false }, { CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false }, { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false }, { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false }, { CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false }, { CSMWorld::ColumnBase::Display_QuestStatusType, CSMWorld::Columns::ColumnId_QuestStatusType, false }, { CSMWorld::ColumnBase::Display_EnchantmentType, CSMWorld::Columns::ColumnId_EnchantmentType, false }, { CSMWorld::ColumnBase::Display_BodyPartType, CSMWorld::Columns::ColumnId_BodyPartType, false }, { CSMWorld::ColumnBase::Display_MeshType, CSMWorld::Columns::ColumnId_MeshType, false }, { CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true }, { CSMWorld::ColumnBase::Display_SoundGeneratorType, CSMWorld::Columns::ColumnId_SoundGeneratorType, false }, { CSMWorld::ColumnBase::Display_School, CSMWorld::Columns::ColumnId_School, false }, { CSMWorld::ColumnBase::Display_SkillId, CSMWorld::Columns::ColumnId_Skill, true }, { CSMWorld::ColumnBase::Display_EffectRange, CSMWorld::Columns::ColumnId_EffectRange, false }, { CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false }, { CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, false }, { CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false }, { CSMWorld::ColumnBase::Display_InfoCondFunc, CSMWorld::Columns::ColumnId_InfoCondFunc, false }, { CSMWorld::ColumnBase::Display_InfoCondComp, CSMWorld::Columns::ColumnId_InfoCondComp, false }, { CSMWorld::ColumnBase::Display_IngredEffectId, CSMWorld::Columns::ColumnId_EffectId, true }, { CSMWorld::ColumnBase::Display_EffectSkill, CSMWorld::Columns::ColumnId_Skill, false }, { CSMWorld::ColumnBase::Display_EffectAttribute, CSMWorld::Columns::ColumnId_Attribute, false }, }; for (std::size_t i=0; iadd (sMapping[i].mDisplay, new CSVWorld::EnumDelegateFactory ( CSMWorld::Columns::getEnums (sMapping[i].mColumnId), sMapping[i].mAllowNone)); connect (&mDocumentManager, SIGNAL (loadRequest (CSMDoc::Document *)), &mLoader, SLOT (add (CSMDoc::Document *))); connect ( &mDocumentManager, SIGNAL (loadingStopped (CSMDoc::Document *, bool, const std::string&)), &mLoader, SLOT (loadingStopped (CSMDoc::Document *, bool, const std::string&))); connect ( &mDocumentManager, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)), &mLoader, SLOT (nextStage (CSMDoc::Document *, const std::string&, int))); connect ( &mDocumentManager, SIGNAL (nextRecord (CSMDoc::Document *, int)), &mLoader, SLOT (nextRecord (CSMDoc::Document *, int))); connect ( &mDocumentManager, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)), &mLoader, SLOT (loadMessage (CSMDoc::Document *, const std::string&))); connect ( &mLoader, SIGNAL (cancel (CSMDoc::Document *)), &mDocumentManager, SIGNAL (cancelLoading (CSMDoc::Document *))); connect ( &mLoader, SIGNAL (close (CSMDoc::Document *)), &mDocumentManager, SLOT (removeDocument (CSMDoc::Document *))); } CSVDoc::ViewManager::~ViewManager() { delete mDelegateFactories; for (std::vector::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) delete *iter; } CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) { if (countViews (document)==0) { // new document connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (documentStateChanged (int, CSMDoc::Document *))); connect (document, SIGNAL (progress (int, int, int, int, CSMDoc::Document *)), this, SLOT (progress (int, int, int, int, CSMDoc::Document *))); } View *view = new View (*this, document, countViews (document)+1); mViews.push_back (view); view->toggleStatusBar (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); view->show(); connect (view, SIGNAL (newGameRequest ()), this, SIGNAL (newGameRequest())); connect (view, SIGNAL (newAddonRequest ()), this, SIGNAL (newAddonRequest())); connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest())); connect (view, SIGNAL (editSettingsRequest()), this, SIGNAL (editSettingsRequest())); connect (view, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SIGNAL (mergeDocument (CSMDoc::Document *))); updateIndices(); return view; } CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document, const CSMWorld::UniversalId& id, const std::string& hint) { View* view = addView(document); view->addSubView(id, hint); return view; } int CSVDoc::ViewManager::countViews (const CSMDoc::Document *document) const { int count = 0; for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) if ((*iter)->getDocument()==document) ++count; return count; } bool CSVDoc::ViewManager::closeRequest (View *view) { std::vector::iterator iter = std::find (mViews.begin(), mViews.end(), view); bool continueWithClose = false; if (iter!=mViews.end()) { bool last = countViews (view->getDocument())<=1; if (last) continueWithClose = notifySaveOnClose (view); else { (*iter)->deleteLater(); mViews.erase (iter); updateIndices(); } } return continueWithClose; } // NOTE: This method assumes that it is called only if the last document void CSVDoc::ViewManager::removeDocAndView (CSMDoc::Document *document) { for (std::vector::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) { // the first match should also be the only match if((*iter)->getDocument() == document) { mDocumentManager.removeDocument(document); (*iter)->deleteLater(); mViews.erase (iter); updateIndices(); return; } } } bool CSVDoc::ViewManager::notifySaveOnClose (CSVDoc::View *view) { bool result = true; CSMDoc::Document *document = view->getDocument(); //notify user of saving in progress if ( (document->getState() & CSMDoc::State_Saving) ) result = showSaveInProgressMessageBox (view); //notify user of unsaved changes and process response else if ( document->getState() & CSMDoc::State_Modified) result = showModifiedDocumentMessageBox (view); return result; } bool CSVDoc::ViewManager::showModifiedDocumentMessageBox (CSVDoc::View *view) { emit closeMessageBox(); QMessageBox messageBox(view); CSMDoc::Document *document = view->getDocument(); messageBox.setWindowTitle (QString::fromUtf8(document->getSavePath().filename().string().c_str())); messageBox.setText ("The document has been modified."); messageBox.setInformativeText ("Do you want to save your changes?"); messageBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); messageBox.setDefaultButton (QMessageBox::Save); messageBox.setWindowModality (Qt::NonModal); messageBox.hide(); messageBox.show(); bool retVal = true; connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); mUserWarned = true; int response = messageBox.exec(); mUserWarned = false; switch (response) { case QMessageBox::Save: document->save(); mExitOnSaveStateChange = true; retVal = false; break; case QMessageBox::Discard: disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); break; case QMessageBox::Cancel: //disconnect to prevent unintended view closures disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); retVal = false; break; default: break; } return retVal; } bool CSVDoc::ViewManager::showSaveInProgressMessageBox (CSVDoc::View *view) { QMessageBox messageBox; CSMDoc::Document *document = view->getDocument(); messageBox.setText ("The document is currently being saved."); messageBox.setInformativeText("Do you want to close now and abort saving, or wait until saving has completed?"); QPushButton* waitButton = messageBox.addButton (tr("Wait"), QMessageBox::YesRole); QPushButton* closeButton = messageBox.addButton (tr("Close Now"), QMessageBox::RejectRole); QPushButton* cancelButton = messageBox.addButton (tr("Cancel"), QMessageBox::NoRole); messageBox.setDefaultButton (waitButton); bool retVal = true; //Connections shut down message box if operation ends before user makes a decision. connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); //set / clear the user warned flag to indicate whether or not the message box is currently active. mUserWarned = true; messageBox.exec(); mUserWarned = false; //if closed by the warning handler, defaults to the RejectRole button (closeButton) if (messageBox.clickedButton() == waitButton) { //save the View iterator for shutdown after the save operation ends mExitOnSaveStateChange = true; retVal = false; } else if (messageBox.clickedButton() == closeButton) { //disconnect to avoid segmentation fault disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); view->abortOperation(CSMDoc::State_Saving); mExitOnSaveStateChange = true; } else if (messageBox.clickedButton() == cancelButton) { //abort shutdown, allow save to complete //disconnection to prevent unintended view closures mExitOnSaveStateChange = false; disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); retVal = false; } return retVal; } void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document) { for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) if ((*iter)->getDocument()==document) (*iter)->updateDocumentState(); } void CSVDoc::ViewManager::progress (int current, int max, int type, int threads, CSMDoc::Document *document) { for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) if ((*iter)->getDocument()==document) (*iter)->updateProgress (current, max, type, threads); } void CSVDoc::ViewManager::onExitWarningHandler (int state, CSMDoc::Document *document) { if ( !(state & CSMDoc::State_Saving) ) { //if the user is being warned (message box is active), shut down the message box, //as there is no save operation currently running if ( mUserWarned ) emit closeMessageBox(); //otherwise, the user has closed the message box before the save operation ended. //exit the application else if (mExitOnSaveStateChange) QApplication::instance()->exit(); } } bool CSVDoc::ViewManager::removeDocument (CSVDoc::View *view) { if(!notifySaveOnClose(view)) return false; else { // don't bother closing views or updating indicies, but remove from mViews CSMDoc::Document * document = view->getDocument(); std::vector remainingViews; std::vector::const_iterator iter = mViews.begin(); for (; iter!=mViews.end(); ++iter) { if(document == (*iter)->getDocument()) (*iter)->setVisible(false); else remainingViews.push_back(*iter); } mDocumentManager.removeDocument(document); mViews = remainingViews; } return true; } void CSVDoc::ViewManager::exitApplication (CSVDoc::View *view) { if(!removeDocument(view)) // close the current document first return; while(!mViews.empty()) // attempt to close all other documents { mViews.back()->activateWindow(); mViews.back()->raise(); // raise the window to alert the user if(!removeDocument(mViews.back())) return; } // Editor exits (via a signal) when the last document is deleted } openmw-openmw-0.38.0/apps/opencs/view/doc/viewmanager.hpp000066400000000000000000000044271264522266000234150ustar00rootroot00000000000000#ifndef CSV_DOC_VIEWMANAGER_H #define CSV_DOC_VIEWMANAGER_H #include #include #include "loader.hpp" namespace CSMDoc { class Document; class DocumentManager; } namespace CSVWorld { class CommandDelegateFactoryCollection; } namespace CSMWorld { class UniversalId; } namespace CSVDoc { class View; class ViewManager : public QObject { Q_OBJECT CSMDoc::DocumentManager& mDocumentManager; std::vector mViews; CSVWorld::CommandDelegateFactoryCollection *mDelegateFactories; bool mExitOnSaveStateChange; bool mUserWarned; Loader mLoader; // not implemented ViewManager (const ViewManager&); ViewManager& operator= (const ViewManager&); void updateIndices(); bool notifySaveOnClose (View *view = 0); bool showModifiedDocumentMessageBox (View *view); bool showSaveInProgressMessageBox (View *view); bool removeDocument(View *view); public: ViewManager (CSMDoc::DocumentManager& documentManager); virtual ~ViewManager(); View *addView (CSMDoc::Document *document); ///< The ownership of the returned view is not transferred. View *addView (CSMDoc::Document *document, const CSMWorld::UniversalId& id, const std::string& hint); int countViews (const CSMDoc::Document *document) const; ///< Return number of views for \a document. bool closeRequest (View *view); void removeDocAndView (CSMDoc::Document *document); signals: void newGameRequest(); void newAddonRequest(); void loadDocumentRequest(); void closeMessageBox(); void editSettingsRequest(); void mergeDocument (CSMDoc::Document *document); public slots: void exitApplication (CSVDoc::View *view); private slots: void documentStateChanged (int state, CSMDoc::Document *document); void progress (int current, int max, int type, int threads, CSMDoc::Document *document); void onExitWarningHandler(int state, CSMDoc::Document* document); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/filter/000077500000000000000000000000001264522266000211105ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/view/filter/editwidget.cpp000066400000000000000000000132001264522266000237410ustar00rootroot00000000000000#include "editwidget.hpp" #include #include #include #include "../../model/world/data.hpp" #include "../../model/world/idtablebase.hpp" #include "../../model/world/columns.hpp" CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) : QLineEdit (parent), mParser (data), mIsEmpty(true) { mPalette = palette(); connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); const CSMWorld::IdTableBase *model = static_cast (data.getTableModel (CSMWorld::UniversalId::Type_Filters)); connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), Qt::QueuedConnection); connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), Qt::QueuedConnection); connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), Qt::QueuedConnection); mStateColumnIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Modification); mDescColumnIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Description); } void CSVFilter::EditWidget::textChanged (const QString& text) { //no need to parse and apply filter if it was empty and now is empty too. //e.g. - we modifiing content of filter with already opened some other (big) tables. if (text.length() == 0){ if (mIsEmpty) return; else mIsEmpty = true; }else mIsEmpty = false; if (mParser.parse (text.toUtf8().constData())) { setPalette (mPalette); emit filterChanged (mParser.getFilter()); } else { QPalette palette (mPalette); palette.setColor (QPalette::Text, Qt::red); setPalette (palette); /// \todo improve error reporting; mark only the faulty part } } void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { for (int i = topLeft.column(); i <= bottomRight.column(); ++i) if (i != mStateColumnIndex && i != mDescColumnIndex) textChanged (text()); } void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) { textChanged (text()); } void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) { textChanged (text()); } void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, Qt::DropAction action) { const unsigned count = filterSource.size(); bool multipleElements = false; switch (count) //setting multipleElements; { case 0: //empty return; //nothing to do here case 1: //only single multipleElements = false; break; default: multipleElements = true; break; } Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); QString oldContent (text()); bool replaceMode = false; std::string orAnd; switch (key) //setting replaceMode and string used to glue expressions { case Qt::ShiftModifier: orAnd = "!or("; replaceMode = false; break; case Qt::ControlModifier: orAnd = "!and("; replaceMode = false; break; default: replaceMode = true; break; } if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode { replaceMode = true; } if (!replaceMode) { oldContent.remove ('!'); } std::stringstream ss; if (multipleElements) { if (replaceMode) { ss<<"!or("; } else { ss << orAnd << oldContent.toUtf8().constData() << ','; } for (unsigned i = 0; i < count; ++i) { ss<4) { clear(); insert (QString::fromUtf8(ss.str().c_str())); } } std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const { const unsigned columns = seekedString.second.size(); bool multipleColumns = false; switch (columns) { case 0: //empty return ""; //no column to filter case 1: //one column to look for multipleColumns = false; break; default: multipleColumns = true; break; } std::stringstream ss; if (multipleColumns) { ss<<"or("; for (unsigned i = 0; i < columns; ++i) { ss<<"string("<<'"'< #include #include #include #include "../../model/filter/parser.hpp" #include "../../model/filter/node.hpp" class QModelIndex; namespace CSMWorld { class Data; } namespace CSVFilter { class EditWidget : public QLineEdit { Q_OBJECT CSMFilter::Parser mParser; QPalette mPalette; bool mIsEmpty; int mStateColumnIndex; int mDescColumnIndex; public: EditWidget (CSMWorld::Data& data, QWidget *parent = 0); void createFilterRequest(std::vector > >& filterSource, Qt::DropAction action); signals: void filterChanged (boost::shared_ptr filter); private: std::string generateFilter(std::pair >& seekedString) const; private slots: void textChanged (const QString& text); void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void filterRowsRemoved (const QModelIndex& parent, int start, int end); void filterRowsInserted (const QModelIndex& parent, int start, int end); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/filter/filterbox.cpp000066400000000000000000000032101264522266000236060ustar00rootroot00000000000000#include "filterbox.hpp" #include #include #include "recordfilterbox.hpp" #include CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) : QWidget (parent) { QHBoxLayout *layout = new QHBoxLayout (this); layout->setContentsMargins (0, 0, 0, 0); mRecordFilterBox = new RecordFilterBox (data, this); layout->addWidget (mRecordFilterBox); setLayout (layout); connect (mRecordFilterBox, SIGNAL (filterChanged (boost::shared_ptr)), this, SIGNAL (recordFilterChanged (boost::shared_ptr))); setAcceptDrops(true); } void CSVFilter::FilterBox::setRecordFilter (const std::string& filter) { mRecordFilterBox->setFilter (filter); } void CSVFilter::FilterBox::dropEvent (QDropEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped return; std::vector data = mime->getData(); emit recordDropped(data, event->proposedAction()); } void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) { event->acceptProposedAction(); } void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) { event->accept(); } void CSVFilter::FilterBox::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, Qt::DropAction action) { mRecordFilterBox->createFilterRequest(filterSource, action); } openmw-openmw-0.38.0/apps/opencs/view/filter/filterbox.hpp000066400000000000000000000021711264522266000236200ustar00rootroot00000000000000#ifndef CSV_FILTER_FILTERBOX_H #define CSV_FILTER_FILTERBOX_H #include #include #include #include "../../model/filter/node.hpp" #include "../../model/world/universalid.hpp" namespace CSMWorld { class Data; } namespace CSVFilter { class RecordFilterBox; class FilterBox : public QWidget { Q_OBJECT RecordFilterBox *mRecordFilterBox; public: FilterBox (CSMWorld::Data& data, QWidget *parent = 0); void setRecordFilter (const std::string& filter); void createFilterRequest(std::vector > >& filterSource, Qt::DropAction action); private: void dragEnterEvent (QDragEnterEvent* event); void dropEvent (QDropEvent* event); void dragMoveEvent(QDragMoveEvent *event); signals: void recordFilterChanged (boost::shared_ptr filter); void recordDropped (std::vector& types, Qt::DropAction action); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/filter/recordfilterbox.cpp000066400000000000000000000021241264522266000250100ustar00rootroot00000000000000#include "recordfilterbox.hpp" #include #include #include "editwidget.hpp" CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) : QWidget (parent) { QHBoxLayout *layout = new QHBoxLayout (this); layout->setContentsMargins (0, 6, 5, 0); QLabel *label = new QLabel("Record Filter", this); label->setIndent(2); layout->addWidget (label); mEdit = new EditWidget (data, this); layout->addWidget (mEdit); setLayout (layout); connect ( mEdit, SIGNAL (filterChanged (boost::shared_ptr)), this, SIGNAL (filterChanged (boost::shared_ptr))); } void CSVFilter::RecordFilterBox::setFilter (const std::string& filter) { mEdit->clear(); mEdit->setText (QString::fromUtf8 (filter.c_str())); } void CSVFilter::RecordFilterBox::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, Qt::DropAction action) { mEdit->createFilterRequest(filterSource, action); } openmw-openmw-0.38.0/apps/opencs/view/filter/recordfilterbox.hpp000066400000000000000000000016241264522266000250210ustar00rootroot00000000000000#ifndef CSV_FILTER_RECORDFILTERBOX_H #define CSV_FILTER_RECORDFILTERBOX_H #include #include #include #include #include "../../model/filter/node.hpp" namespace CSMWorld { class Data; } namespace CSVFilter { class EditWidget; class RecordFilterBox : public QWidget { Q_OBJECT EditWidget *mEdit; public: RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); void setFilter (const std::string& filter); void useFilterRequest(const std::string& idOfFilter); void createFilterRequest(std::vector > >& filterSource, Qt::DropAction action); signals: void filterChanged (boost::shared_ptr filter); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/prefs/000077500000000000000000000000001264522266000207425ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/view/prefs/dialogue.cpp000066400000000000000000000063501264522266000232430ustar00rootroot00000000000000 #include "dialogue.hpp" #include #include #include #include #include #include #include "../../model/prefs/state.hpp" #include "page.hpp" void CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main) { mList = new QListWidget (main); mList->setMinimumWidth (50); mList->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding); mList->setSelectionBehavior (QAbstractItemView::SelectItems); main->addWidget (mList); QFontMetrics metrics (QApplication::font()); int maxWidth = 1; for (CSMPrefs::State::Iterator iter = CSMPrefs::get().begin(); iter!=CSMPrefs::get().end(); ++iter) { QString label = QString::fromUtf8 (iter->second.getKey().c_str()); maxWidth = std::max (maxWidth, metrics.width (label)); mList->addItem (label); } mList->setMaximumWidth (maxWidth + 10); connect (mList, SIGNAL (currentItemChanged (QListWidgetItem *, QListWidgetItem *)), this, SLOT (selectionChanged (QListWidgetItem *, QListWidgetItem *))); } void CSVPrefs::Dialogue::buildContentArea (QSplitter *main) { mContent = new QStackedWidget (main); mContent->setSizePolicy (QSizePolicy::Preferred, QSizePolicy::Expanding); main->addWidget (mContent); } CSVPrefs::PageBase *CSVPrefs::Dialogue::makePage (const std::string& key) { // special case page code goes here return new Page (CSMPrefs::get()[key], mContent); } CSVPrefs::Dialogue::Dialogue() { setWindowTitle ("User Settings"); setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); setMinimumSize (600, 400); QSplitter *main = new QSplitter (this); setCentralWidget (main); buildCategorySelector (main); buildContentArea (main); } CSVPrefs::Dialogue::~Dialogue() { if (isVisible()) CSMPrefs::State::get().save(); } void CSVPrefs::Dialogue::closeEvent (QCloseEvent *event) { QMainWindow::closeEvent (event); CSMPrefs::State::get().save(); } void CSVPrefs::Dialogue::show() { if (QWidget *active = QApplication::activeWindow()) { // place at the centre of the window with focus QSize size = active->size(); move (active->geometry().x()+(size.width() - frameGeometry().width())/2, active->geometry().y()+(size.height() - frameGeometry().height())/2); } else { // otherwise place at the centre of the screen QPoint screenCenter = QApplication::desktop()->screenGeometry().center(); move (screenCenter - QPoint(frameGeometry().width()/2, frameGeometry().height()/2)); } QWidget::show(); } void CSVPrefs::Dialogue::selectionChanged (QListWidgetItem *current, QListWidgetItem *previous) { if (current) { std::string key = current->text().toUtf8().data(); for (int i=0; icount(); ++i) { PageBase& page = dynamic_cast (*mContent->widget (i)); if (page.getCategory().getKey()==key) { mContent->setCurrentIndex (i); return; } } PageBase *page = makePage (key); mContent->setCurrentIndex (mContent->addWidget (page)); } } openmw-openmw-0.38.0/apps/opencs/view/prefs/dialogue.hpp000066400000000000000000000015351264522266000232500ustar00rootroot00000000000000#ifndef CSV_PREFS_DIALOGUE_H #define CSV_PREFS_DIALOGUE_H #include class QSplitter; class QListWidget; class QStackedWidget; class QListWidgetItem; namespace CSVPrefs { class PageBase; class Dialogue : public QMainWindow { Q_OBJECT QListWidget *mList; QStackedWidget *mContent; private: void buildCategorySelector (QSplitter *main); void buildContentArea (QSplitter *main); PageBase *makePage (const std::string& key); public: Dialogue(); virtual ~Dialogue(); protected: void closeEvent (QCloseEvent *event); public slots: void show(); private slots: void selectionChanged (QListWidgetItem *current, QListWidgetItem *previous); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/prefs/page.cpp000066400000000000000000000016711264522266000223670ustar00rootroot00000000000000 #include "page.hpp" #include #include "../../model/prefs/setting.hpp" #include "../../model/prefs/category.hpp" CSVPrefs::Page::Page (CSMPrefs::Category& category, QWidget *parent) : PageBase (category, parent) { QWidget *widget = new QWidget (parent); mGrid = new QGridLayout (widget); for (CSMPrefs::Category::Iterator iter = category.begin(); iter!=category.end(); ++iter) addSetting (*iter); setWidget (widget); } void CSVPrefs::Page::addSetting (CSMPrefs::Setting *setting) { std::pair widgets = setting->makeWidgets (this); int next = mGrid->rowCount(); if (widgets.first) { mGrid->addWidget (widgets.first, next, 0); mGrid->addWidget (widgets.second, next, 1); } else if (widgets.second) { mGrid->addWidget (widgets.second, next, 0, 1, 2); } else { mGrid->addWidget (new QWidget (this), next, 0); } } openmw-openmw-0.38.0/apps/opencs/view/prefs/page.hpp000066400000000000000000000006351264522266000223730ustar00rootroot00000000000000#ifndef CSV_PREFS_PAGE_H #define CSV_PREFS_PAGE_H #include "pagebase.hpp" class QGridLayout; namespace CSMPrefs { class Setting; } namespace CSVPrefs { class Page : public PageBase { Q_OBJECT QGridLayout *mGrid; public: Page (CSMPrefs::Category& category, QWidget *parent); void addSetting (CSMPrefs::Setting *setting); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/prefs/pagebase.cpp000066400000000000000000000004231264522266000232140ustar00rootroot00000000000000 #include "pagebase.hpp" #include "../../model/prefs/category.hpp" CSVPrefs::PageBase::PageBase (CSMPrefs::Category& category, QWidget *parent) : QScrollArea (parent), mCategory (category) {} CSMPrefs::Category& CSVPrefs::PageBase::getCategory() { return mCategory; } openmw-openmw-0.38.0/apps/opencs/view/prefs/pagebase.hpp000066400000000000000000000006341264522266000232250ustar00rootroot00000000000000#ifndef CSV_PREFS_PAGEBASE_H #define CSV_PREFS_PAGEBASE_H #include namespace CSMPrefs { class Category; } namespace CSVPrefs { class PageBase : public QScrollArea { Q_OBJECT CSMPrefs::Category& mCategory; public: PageBase (CSMPrefs::Category& category, QWidget *parent); CSMPrefs::Category& getCategory(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/000077500000000000000000000000001264522266000211025ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/view/render/cell.cpp000066400000000000000000000174311264522266000225330ustar00rootroot00000000000000#include "cell.hpp" #include #include #include #include "../../model/world/idtable.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/data.hpp" #include "../../model/world/refcollection.hpp" #include "../../model/world/cellcoordinates.hpp" #include "elements.hpp" #include "terrainstorage.hpp" bool CSVRender::Cell::removeObject (const std::string& id) { std::map::iterator iter = mObjects.find (Misc::StringUtils::lowerCase (id)); if (iter==mObjects.end()) return false; delete iter->second; mObjects.erase (iter); return true; } bool CSVRender::Cell::addObjects (int start, int end) { bool modified = false; const CSMWorld::RefCollection& collection = mData.getReferences(); for (int i=start; i<=end; ++i) { std::string cell = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mCell); CSMWorld::RecordBase::State state = collection.getRecord (i).mState; if (cell==mId && state!=CSMWorld::RecordBase::State_Deleted) { std::string id = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mId); mObjects.insert (std::make_pair (id, new Object (mData, mCellNode, id, false))); modified = true; } } return modified; } CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, bool deleted) : mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted) { std::pair result = CSMWorld::CellCoordinates::fromId (id); if (result.second) mCoordinates = result.first; mCellNode = new osg::Group; rootNode->addChild(mCellNode); if (!mDeleted) { CSMWorld::IdTable& references = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_References)); int rows = references.rowCount(); addObjects (0, rows-1); const CSMWorld::IdCollection& land = mData.getLand(); int landIndex = land.searchId(mId); if (landIndex != -1) { const ESM::Land& esmLand = land.getRecord(mId).get(); if (esmLand.getLandData (ESM::Land::DATA_VHGT)) { mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Element_Terrain<<1)); mTerrain->loadCell(esmLand.mX, esmLand.mY); } } } } CSVRender::Cell::~Cell() { for (std::map::iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) delete iter->second; mCellNode->getParent(0)->removeChild(mCellNode); } bool CSVRender::Cell::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { bool modified = false; for (std::map::iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) if (iter->second->referenceableDataChanged (topLeft, bottomRight)) modified = true; return modified; } bool CSVRender::Cell::referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) { if (parent.isValid()) return false; bool modified = false; for (std::map::iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) if (iter->second->referenceableAboutToBeRemoved (parent, start, end)) modified = true; return modified; } bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (mDeleted) return false; CSMWorld::IdTable& references = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_References)); int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id); int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell); int stateColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification); // list IDs in cell std::map ids; // id, deleted state for (int i=topLeft.row(); i<=bottomRight.row(); ++i) { std::string cell = Misc::StringUtils::lowerCase (references.data ( references.index (i, cellColumn)).toString().toUtf8().constData()); if (cell==mId) { std::string id = Misc::StringUtils::lowerCase (references.data ( references.index (i, idColumn)).toString().toUtf8().constData()); int state = references.data (references.index (i, stateColumn)).toInt(); ids.insert (std::make_pair (id, state==CSMWorld::RecordBase::State_Deleted)); } } // perform update and remove where needed bool modified = false; for (std::map::iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) { if (iter->second->referenceDataChanged (topLeft, bottomRight)) modified = true; std::map::iterator iter2 = ids.find (iter->first); if (iter2!=ids.end()) { if (iter2->second) { removeObject (iter->first); modified = true; } ids.erase (iter2); } } // add new objects for (std::map::iterator iter (ids.begin()); iter!=ids.end(); ++iter) { mObjects.insert (std::make_pair ( iter->first, new Object (mData, mCellNode, iter->first, false))); modified = true; } return modified; } bool CSVRender::Cell::referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) { if (parent.isValid()) return false; if (mDeleted) return false; CSMWorld::IdTable& references = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_References)); int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id); bool modified = false; for (int row = start; row<=end; ++row) if (removeObject (references.data ( references.index (row, idColumn)).toString().toUtf8().constData())) modified = true; return modified; } bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int end) { if (parent.isValid()) return false; if (mDeleted) return false; return addObjects (start, end); } void CSVRender::Cell::setSelection (int elementMask, Selection mode) { if (elementMask & Element_Reference) { for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) { bool selected = false; switch (mode) { case Selection_Clear: selected = false; break; case Selection_All: selected = true; break; case Selection_Invert: selected = !iter->second->getSelected(); break; } iter->second->setSelected (selected); } } } void CSVRender::Cell::setCellArrows (int mask) { for (int i=0; i<4; ++i) { CellArrow::Direction direction = static_cast (1< #include #include #include #include #ifndef Q_MOC_RUN #include #endif #include "object.hpp" #include "cellarrow.hpp" class QModelIndex; namespace osg { class Group; } namespace CSMWorld { class Data; class CellCoordinates; } namespace CSVRender { class Cell { CSMWorld::Data& mData; std::string mId; osg::ref_ptr mCellNode; std::map mObjects; std::auto_ptr mTerrain; CSMWorld::CellCoordinates mCoordinates; std::auto_ptr mCellArrows[4]; bool mDeleted; /// Ignored if cell does not have an object with the given ID. /// /// \return Was the object deleted? bool removeObject (const std::string& id); /// Add objects from reference table that are within this cell. /// /// \return Have any objects been added? bool addObjects (int start, int end); public: enum Selection { Selection_Clear, Selection_All, Selection_Invert }; public: /// \note Deleted covers both cells that are deleted and cells that don't exist in /// the first place. Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, bool deleted = false); ~Cell(); /// \return Did this call result in a modification of the visual representation of /// this cell? bool referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); /// \return Did this call result in a modification of the visual representation of /// this cell? bool referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); /// \return Did this call result in a modification of the visual representation of /// this cell? bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); /// \return Did this call result in a modification of the visual representation of /// this cell? bool referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); /// \return Did this call result in a modification of the visual representation of /// this cell? bool referenceAdded (const QModelIndex& parent, int start, int end); void setSelection (int elementMask, Selection mode); void setCellArrows (int mask); /// Returns 0, 0 in case of an unpaged cell. CSMWorld::CellCoordinates getCoordinates() const; bool isDeleted() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/cellarrow.cpp000066400000000000000000000120211264522266000235740ustar00rootroot00000000000000 #include "cellarrow.hpp" #include #include #include #include #include #include "elements.hpp" CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow) : TagBase (Element_CellArrow), mArrow (arrow) {} CSVRender::CellArrow *CSVRender::CellArrowTag::getCellArrow() const { return mArrow; } QString CSVRender::CellArrowTag::getToolTip (bool hideBasics) const { QString text ("Direction: "); switch (mArrow->getDirection()) { case CellArrow::Direction_North: text += "North"; break; case CellArrow::Direction_West: text += "West"; break; case CellArrow::Direction_South: text += "South"; break; case CellArrow::Direction_East: text += "East"; break; } if (!hideBasics) { text += "

" "Modify which cells are shown" "

  • Primary-Edit: Add cell in given direction
  • " "
  • Secondary-Edit: Add cell and remove old cell
  • " "
  • Shift Primary-Edit: Add cells in given direction
  • " "
  • Shift Secondary-Edit: Add cells and remove old cells
  • " "
"; } return text; } void CSVRender::CellArrow::adjustTransform() { // position const int cellSize = 8192; const int offset = cellSize / 2 + 800; int x = mCoordinates.getX()*cellSize + cellSize/2; int y = mCoordinates.getY()*cellSize + cellSize/2; float xr = 0; float yr = 0; float zr = 0; float angle = osg::DegreesToRadians (90.0f); switch (mDirection) { case Direction_North: y += offset; xr = -angle; zr = angle; break; case Direction_West: x -= offset; yr = -angle; break; case Direction_South: y -= offset; xr = angle; zr = angle; break; case Direction_East: x += offset; yr = angle; break; }; mBaseNode->setPosition (osg::Vec3f (x, y, 0)); // orientation osg::Quat xr2 (xr, osg::Vec3f (1,0,0)); osg::Quat yr2 (yr, osg::Vec3f (0,1,0)); osg::Quat zr2 (zr, osg::Vec3f (0,0,1)); mBaseNode->setAttitude (zr2*yr2*xr2); } void CSVRender::CellArrow::buildShape() { osg::ref_ptr geometry (new osg::Geometry); const int arrowWidth = 4000; const int arrowLength = 1500; const int arrowHeight = 500; osg::Vec3Array *vertices = new osg::Vec3Array; for (int i2=0; i2<2; ++i2) for (int i=0; i<2; ++i) { float height = i ? -arrowHeight/2 : arrowHeight/2; vertices->push_back (osg::Vec3f (height, -arrowWidth/2, 0)); vertices->push_back (osg::Vec3f (height, arrowWidth/2, 0)); vertices->push_back (osg::Vec3f (height, 0, arrowLength)); } geometry->setVertexArray (vertices); osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0); // top primitives->push_back (0); primitives->push_back (1); primitives->push_back (2); // bottom primitives->push_back (5); primitives->push_back (4); primitives->push_back (3); // back primitives->push_back (3+6); primitives->push_back (4+6); primitives->push_back (1+6); primitives->push_back (3+6); primitives->push_back (1+6); primitives->push_back (0+6); // sides primitives->push_back (0+6); primitives->push_back (2+6); primitives->push_back (5+6); primitives->push_back (0+6); primitives->push_back (5+6); primitives->push_back (3+6); primitives->push_back (4+6); primitives->push_back (5+6); primitives->push_back (2+6); primitives->push_back (4+6); primitives->push_back (2+6); primitives->push_back (1+6); geometry->addPrimitiveSet (primitives); osg::Vec4Array *colours = new osg::Vec4Array; for (int i=0; i<6; ++i) colours->push_back (osg::Vec4f (1.0f, 0.0f, 0.0f, 1.0f)); for (int i=0; i<6; ++i) colours->push_back (osg::Vec4f (0.8f, (i==2 || i==5) ? 0.6f : 0.4f, 0.0f, 1.0f)); geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX); geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF); osg::ref_ptr geode (new osg::Geode); geode->addDrawable (geometry); mBaseNode->addChild (geode); } CSVRender::CellArrow::CellArrow (osg::Group *cellNode, Direction direction, const CSMWorld::CellCoordinates& coordinates) : mDirection (direction), mParentNode (cellNode), mCoordinates (coordinates) { mBaseNode = new osg::PositionAttitudeTransform; mBaseNode->setUserData (new CellArrowTag (this)); mParentNode->addChild (mBaseNode); // 0x1 reserved for separating cull and update visitors mBaseNode->setNodeMask (Element_CellArrow<<1); adjustTransform(); buildShape(); } CSVRender::CellArrow::~CellArrow() { mParentNode->removeChild (mBaseNode); } CSMWorld::CellCoordinates CSVRender::CellArrow::getCoordinates() const { return mCoordinates; } CSVRender::CellArrow::Direction CSVRender::CellArrow::getDirection() const { return mDirection; } openmw-openmw-0.38.0/apps/opencs/view/render/cellarrow.hpp000066400000000000000000000027211264522266000236070ustar00rootroot00000000000000#ifndef OPENCS_VIEW_CELLARROW_H #define OPENCS_VIEW_CELLARROW_H #include "tagbase.hpp" #include #include "../../model/world/cellcoordinates.hpp" namespace osg { class PositionAttitudeTransform; class Group; } namespace CSVRender { class CellArrow; class CellArrowTag : public TagBase { CellArrow *mArrow; public: CellArrowTag (CellArrow *arrow); CellArrow *getCellArrow() const; virtual QString getToolTip (bool hideBasics) const; }; class CellArrow { public: enum Direction { Direction_North = 1, Direction_West = 2, Direction_South = 4, Direction_East = 8 }; private: // not implemented CellArrow (const CellArrow&); CellArrow& operator= (const CellArrow&); Direction mDirection; osg::Group* mParentNode; osg::ref_ptr mBaseNode; CSMWorld::CellCoordinates mCoordinates; void adjustTransform(); void buildShape(); public: CellArrow (osg::Group *cellNode, Direction direction, const CSMWorld::CellCoordinates& coordinates); ~CellArrow(); CSMWorld::CellCoordinates getCoordinates() const; Direction getDirection() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/editmode.cpp000066400000000000000000000035541264522266000234070ustar00rootroot00000000000000#include "editmode.hpp" #include "tagbase.hpp" #include "worldspacewidget.hpp" CSVRender::WorldspaceWidget& CSVRender::EditMode::getWorldspaceWidget() { return *mWorldspaceWidget; } CSVRender::EditMode::EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon, unsigned int mask, const QString& tooltip, QWidget *parent) : ModeButton (icon, tooltip, parent), mWorldspaceWidget (worldspaceWidget), mMask (mask) {} unsigned int CSVRender::EditMode::getInteractionMask() const { return mMask; } void CSVRender::EditMode::activate (CSVWidget::SceneToolbar *toolbar) { mWorldspaceWidget->setInteractionMask (mMask); mWorldspaceWidget->clearSelection (~mMask); } void CSVRender::EditMode::setEditLock (bool locked) { } void CSVRender::EditMode::primaryEditPressed (osg::ref_ptr tag) {} void CSVRender::EditMode::secondaryEditPressed (osg::ref_ptr tag) {} void CSVRender::EditMode::primarySelectPressed (osg::ref_ptr tag) {} void CSVRender::EditMode::secondarySelectPressed (osg::ref_ptr tag) {} bool CSVRender::EditMode::primaryEditStartDrag (osg::ref_ptr tag) { return false; } bool CSVRender::EditMode::secondaryEditStartDrag (osg::ref_ptr tag) { return false; } bool CSVRender::EditMode::primarySelectStartDrag (osg::ref_ptr tag) { return false; } bool CSVRender::EditMode::secondarySelectStartDrag (osg::ref_ptr tag) { return false; } void CSVRender::EditMode::drag (int diffX, int diffY, double speedFactor) {} void CSVRender::EditMode::dragCompleted() {} void CSVRender::EditMode::dragAborted() {} void CSVRender::EditMode::dragWheel (int diff, double speedFactor) {} void CSVRender::EditMode::dragEnterEvent (QDragEnterEvent *event) {} void CSVRender::EditMode::dropEvent (QDropEvent* event) {} void CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {} openmw-openmw-0.38.0/apps/opencs/view/render/editmode.hpp000066400000000000000000000060211264522266000234040ustar00rootroot00000000000000#ifndef CSV_RENDER_EDITMODE_H #define CSV_RENDER_EDITMODE_H #include #include "../widget/modebutton.hpp" class QDragEnterEvent; class QDropEvent; class QDragMoveEvent; namespace CSVRender { class WorldspaceWidget; class TagBase; class EditMode : public CSVWidget::ModeButton { Q_OBJECT WorldspaceWidget *mWorldspaceWidget; unsigned int mMask; protected: WorldspaceWidget& getWorldspaceWidget(); public: EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon, unsigned int mask, const QString& tooltip = "", QWidget *parent = 0); unsigned int getInteractionMask() const; virtual void activate (CSVWidget::SceneToolbar *toolbar); /// Default-implementation: Ignored. virtual void setEditLock (bool locked); /// Default-implementation: Ignored. virtual void primaryEditPressed (osg::ref_ptr tag); /// Default-implementation: Ignored. virtual void secondaryEditPressed (osg::ref_ptr tag); /// Default-implementation: Ignored. virtual void primarySelectPressed (osg::ref_ptr tag); /// Default-implementation: Ignored. virtual void secondarySelectPressed (osg::ref_ptr tag); /// Default-implementation: ignore and return false /// /// \return Drag accepted? virtual bool primaryEditStartDrag (osg::ref_ptr tag); /// Default-implementation: ignore and return false /// /// \return Drag accepted? virtual bool secondaryEditStartDrag (osg::ref_ptr tag); /// Default-implementation: ignore and return false /// /// \return Drag accepted? virtual bool primarySelectStartDrag (osg::ref_ptr tag); /// Default-implementation: ignore and return false /// /// \return Drag accepted? virtual bool secondarySelectStartDrag (osg::ref_ptr tag); /// Default-implementation: ignored virtual void drag (int diffX, int diffY, double speedFactor); /// Default-implementation: ignored virtual void dragCompleted(); /// Default-implementation: ignored /// /// \note dragAborted will not be called, if the drag is aborted via changing /// editing mode virtual void dragAborted(); /// Default-implementation: ignored virtual void dragWheel (int diff, double speedFactor); /// Default-implementation: ignored virtual void dragEnterEvent (QDragEnterEvent *event); /// Default-implementation: ignored virtual void dropEvent (QDropEvent* event); /// Default-implementation: ignored virtual void dragMoveEvent (QDragMoveEvent *event); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/elements.hpp000066400000000000000000000007731264522266000234360ustar00rootroot00000000000000#ifndef CSV_RENDER_ELEMENTS_H #define CSV_RENDER_ELEMENTS_H namespace CSVRender { /// Visual elements in a scene enum Elements { // elements that are part of the actual scene Element_Reference = 0x1, Element_Pathgrid = 0x2, Element_Water = 0x4, Element_Fog = 0x8, Element_Terrain = 0x10, // control elements Element_CellMarker = 0x10000, Element_CellArrow = 0x20000, Element_CellBorder = 0x40000 }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/instancemode.cpp000066400000000000000000000030771264522266000242660ustar00rootroot00000000000000 #include "instancemode.hpp" #include "../../model/prefs/state.hpp" #include "elements.hpp" #include "object.hpp" #include "worldspacewidget.hpp" CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":placeholder"), Element_Reference, "Instance editing", parent) { } void CSVRender::InstanceMode::primaryEditPressed (osg::ref_ptr tag) { if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) primarySelectPressed (tag); } void CSVRender::InstanceMode::secondaryEditPressed (osg::ref_ptr tag) { if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) secondarySelectPressed (tag); } void CSVRender::InstanceMode::primarySelectPressed (osg::ref_ptr tag) { getWorldspaceWidget().clearSelection (Element_Reference); if (tag) { if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) { // hit an Object, select it CSVRender::Object* object = objectTag->mObject; object->setSelected (true); return; } } } void CSVRender::InstanceMode::secondarySelectPressed (osg::ref_ptr tag) { if (tag) { if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) { // hit an Object, toggle its selection state CSVRender::Object* object = objectTag->mObject; object->setSelected (!object->getSelected()); return; } } } openmw-openmw-0.38.0/apps/opencs/view/render/instancemode.hpp000066400000000000000000000011371264522266000242660ustar00rootroot00000000000000#ifndef CSV_RENDER_INSTANCEMODE_H #define CSV_RENDER_INSTANCEMODE_H #include "editmode.hpp" namespace CSVRender { class InstanceMode : public EditMode { Q_OBJECT public: InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent = 0); virtual void primaryEditPressed (osg::ref_ptr tag); virtual void secondaryEditPressed (osg::ref_ptr tag); virtual void primarySelectPressed (osg::ref_ptr tag); virtual void secondarySelectPressed (osg::ref_ptr tag); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/lighting.cpp000066400000000000000000000001311264522266000234060ustar00rootroot00000000000000#include "lighting.hpp" #include CSVRender::Lighting::~Lighting() {} openmw-openmw-0.38.0/apps/opencs/view/render/lighting.hpp000066400000000000000000000011541264522266000234210ustar00rootroot00000000000000#ifndef OPENCS_VIEW_LIGHTING_H #define OPENCS_VIEW_LIGHTING_H #include namespace osg { class Vec4f; class LightSource; class Group; } namespace CSVRender { class Lighting { public: Lighting() : mRootNode(0) {} virtual ~Lighting(); virtual void activate (osg::Group* rootNode) = 0; virtual void deactivate() = 0; virtual osg::Vec4f getAmbientColour(osg::Vec4f* defaultAmbient) = 0; protected: osg::ref_ptr mLightSource; osg::Group* mRootNode; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/lightingbright.cpp000066400000000000000000000016151264522266000246160ustar00rootroot00000000000000#include "lightingbright.hpp" #include CSVRender::LightingBright::LightingBright() {} void CSVRender::LightingBright::activate (osg::Group* rootNode) { mRootNode = rootNode; mLightSource = (new osg::LightSource); osg::ref_ptr light (new osg::Light); light->setAmbient(osg::Vec4f(0.f, 0.f, 0.f, 1.f)); light->setPosition(osg::Vec4f(0.f, 0.f, 1.f, 0.f)); light->setDiffuse(osg::Vec4f(1.f, 1.f, 1.f, 1.f)); light->setSpecular(osg::Vec4f(0.f, 0.f, 0.f, 0.f)); light->setConstantAttenuation(1.f); mLightSource->setLight(light); mRootNode->addChild(mLightSource); } void CSVRender::LightingBright::deactivate() { if (mRootNode && mLightSource.get()) mRootNode->removeChild(mLightSource); } osg::Vec4f CSVRender::LightingBright::getAmbientColour(osg::Vec4f* /*defaultAmbient*/) { return osg::Vec4f(1.f, 1.f, 1.f, 1.f); } openmw-openmw-0.38.0/apps/opencs/view/render/lightingbright.hpp000066400000000000000000000007231264522266000246220ustar00rootroot00000000000000#ifndef OPENCS_VIEW_LIGHTING_BRIGHT_H #define OPENCS_VIEW_LIGHTING_BRIGHT_H #include "lighting.hpp" namespace osg { class Light; class Group; } namespace CSVRender { class LightingBright : public Lighting { public: LightingBright(); virtual void activate (osg::Group* rootNode); virtual void deactivate(); virtual osg::Vec4f getAmbientColour(osg::Vec4f* defaultAmbient); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/lightingday.cpp000066400000000000000000000016731264522266000241200ustar00rootroot00000000000000#include "lightingday.hpp" #include CSVRender::LightingDay::LightingDay(){} void CSVRender::LightingDay::activate (osg::Group* rootNode) { mRootNode = rootNode; mLightSource = new osg::LightSource; osg::ref_ptr light (new osg::Light); light->setPosition(osg::Vec4f(0.f, 0.f, 1.f, 0.f)); light->setAmbient(osg::Vec4f(0.f, 0.f, 0.f, 1.f)); light->setDiffuse(osg::Vec4f(1.f, 1.f, 1.f, 1.f)); light->setSpecular(osg::Vec4f(0.f, 0.f, 0.f, 0.f)); light->setConstantAttenuation(1.f); mLightSource->setLight(light); mRootNode->addChild(mLightSource); } void CSVRender::LightingDay::deactivate() { if (mRootNode && mLightSource.get()) mRootNode->removeChild(mLightSource); } osg::Vec4f CSVRender::LightingDay::getAmbientColour(osg::Vec4f *defaultAmbient) { if (defaultAmbient) return *defaultAmbient; else return osg::Vec4f(0.7f, 0.7f, 0.7f, 1.f); } openmw-openmw-0.38.0/apps/opencs/view/render/lightingday.hpp000066400000000000000000000006221264522266000241160ustar00rootroot00000000000000#ifndef OPENCS_VIEW_LIGHTING_DAY_H #define OPENCS_VIEW_LIGHTING_DAY_H #include "lighting.hpp" namespace CSVRender { class LightingDay : public Lighting { public: LightingDay(); virtual void activate (osg::Group* rootNode); virtual void deactivate(); virtual osg::Vec4f getAmbientColour(osg::Vec4f *defaultAmbient); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/lightingnight.cpp000066400000000000000000000017141264522266000244500ustar00rootroot00000000000000#include "lightingnight.hpp" #include CSVRender::LightingNight::LightingNight() {} void CSVRender::LightingNight::activate (osg::Group* rootNode) { mRootNode = rootNode; mLightSource = new osg::LightSource; osg::ref_ptr light (new osg::Light); light->setPosition(osg::Vec4f(0.f, 0.f, 1.f, 0.f)); light->setAmbient(osg::Vec4f(0.f, 0.f, 0.f, 1.f)); light->setDiffuse(osg::Vec4f(0.2f, 0.2f, 0.2f, 1.f)); light->setSpecular(osg::Vec4f(0.f, 0.f, 0.f, 0.f)); light->setConstantAttenuation(1.f); mLightSource->setLight(light); mRootNode->addChild(mLightSource); } void CSVRender::LightingNight::deactivate() { if (mRootNode && mLightSource.get()) mRootNode->removeChild(mLightSource); } osg::Vec4f CSVRender::LightingNight::getAmbientColour(osg::Vec4f *defaultAmbient) { if (defaultAmbient) return *defaultAmbient; else return osg::Vec4f(0.2f, 0.2f, 0.2f, 1.f); } openmw-openmw-0.38.0/apps/opencs/view/render/lightingnight.hpp000066400000000000000000000006311264522266000244520ustar00rootroot00000000000000#ifndef OPENCS_VIEW_LIGHTING_NIGHT_H #define OPENCS_VIEW_LIGHTING_NIGHT_H #include "lighting.hpp" namespace CSVRender { class LightingNight : public Lighting { public: LightingNight(); virtual void activate (osg::Group* rootNode); virtual void deactivate(); virtual osg::Vec4f getAmbientColour(osg::Vec4f *defaultAmbient); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/object.cpp000066400000000000000000000143261264522266000230620ustar00rootroot00000000000000#include "object.hpp" #include #include #include #include #include #include #include #include #include "../../model/world/data.hpp" #include "../../model/world/ref.hpp" #include "../../model/world/refidcollection.hpp" #include #include #include "elements.hpp" namespace { osg::ref_ptr createErrorCube() { osg::ref_ptr shape(new osg::Box(osg::Vec3f(0,0,0), 50.f)); osg::ref_ptr shapedrawable(new osg::ShapeDrawable); shapedrawable->setShape(shape); osg::ref_ptr geode (new osg::Geode); geode->addDrawable(shapedrawable); return geode; } } CSVRender::ObjectTag::ObjectTag (Object* object) : TagBase (Element_Reference), mObject (object) {} QString CSVRender::ObjectTag::getToolTip (bool hideBasics) const { return QString::fromUtf8 (mObject->getReferenceableId().c_str()); } void CSVRender::Object::clear() { } void CSVRender::Object::update() { clear(); std::string model; int error = 0; // 1 referenceable does not exist, 2 referenceable does not specify a mesh const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); int index = referenceables.searchId (mReferenceableId); if (index==-1) error = 1; else { /// \todo check for Deleted state (error 1) model = referenceables.getData (index, referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)). toString().toUtf8().constData(); if (model.empty()) error = 2; } mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); if (error) { mBaseNode->addChild(createErrorCube()); } else { try { std::string path = "meshes\\" + model; mResourceSystem->getSceneManager()->createInstance(path, mBaseNode); } catch (std::exception& e) { // TODO: use error marker mesh std::cerr << e.what() << std::endl; } } } void CSVRender::Object::adjustTransform() { if (mReferenceId.empty()) return; const CSMWorld::CellRef& reference = getReference(); // position mBaseNode->setPosition(mForceBaseToZero ? osg::Vec3() : osg::Vec3f(reference.mPos.pos[0], reference.mPos.pos[1], reference.mPos.pos[2])); // orientation osg::Quat xr (-reference.mPos.rot[0], osg::Vec3f(1,0,0)); osg::Quat yr (-reference.mPos.rot[1], osg::Vec3f(0,1,0)); osg::Quat zr (-reference.mPos.rot[2], osg::Vec3f(0,0,1)); mBaseNode->setAttitude(zr*yr*xr); mBaseNode->setScale(osg::Vec3(reference.mScale, reference.mScale, reference.mScale)); } const CSMWorld::CellRef& CSVRender::Object::getReference() const { if (mReferenceId.empty()) throw std::logic_error ("object does not represent a reference"); return mData.getReferences().getRecord (mReferenceId).get(); } CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero) : mData (data), mBaseNode(0), mSelected(false), mParentNode(parentNode), mResourceSystem(data.getResourceSystem().get()), mForceBaseToZero (forceBaseToZero) { mBaseNode = new osg::PositionAttitudeTransform; mOutline = new osgFX::Scribe; mOutline->addChild(mBaseNode); mBaseNode->setUserData(new ObjectTag(this)); parentNode->addChild(mBaseNode); // 0x1 reserved for separating cull and update visitors mBaseNode->setNodeMask(Element_Reference<<1); if (referenceable) { mReferenceableId = id; } else { mReferenceId = id; mReferenceableId = getReference().mRefID; } adjustTransform(); update(); } CSVRender::Object::~Object() { clear(); mParentNode->removeChild(mBaseNode); } void CSVRender::Object::setSelected(bool selected) { mSelected = selected; mParentNode->removeChild(mOutline); mParentNode->removeChild(mBaseNode); if (selected) mParentNode->addChild(mOutline); else mParentNode->addChild(mBaseNode); } bool CSVRender::Object::getSelected() const { return mSelected; } bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); int index = referenceables.searchId (mReferenceableId); if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row()) { adjustTransform(); update(); return true; } return false; } bool CSVRender::Object::referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) { const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); int index = referenceables.searchId (mReferenceableId); if (index!=-1 && index>=start && index<=end) { // Deletion of referenceable-type objects is handled outside of Object. if (!mReferenceId.empty()) { adjustTransform(); update(); return true; } } return false; } bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (mReferenceId.empty()) return false; const CSMWorld::RefCollection& references = mData.getReferences(); int index = references.searchId (mReferenceId); if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row()) { int columnIndex = references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId); adjustTransform(); if (columnIndex>=topLeft.column() && columnIndex<=bottomRight.row()) { mReferenceableId = references.getData (index, columnIndex).toString().toUtf8().constData(); update(); } return true; } return false; } std::string CSVRender::Object::getReferenceId() const { return mReferenceId; } std::string CSVRender::Object::getReferenceableId() const { return mReferenceableId; } openmw-openmw-0.38.0/apps/opencs/view/render/object.hpp000066400000000000000000000063041264522266000230640ustar00rootroot00000000000000#ifndef OPENCS_VIEW_OBJECT_H #define OPENCS_VIEW_OBJECT_H #include #include #include #include #include "tagbase.hpp" class QModelIndex; namespace osg { class PositionAttitudeTransform; class Group; } namespace osgFX { class Scribe; } namespace Resource { class ResourceSystem; } namespace CSMWorld { class Data; struct CellRef; } namespace CSVRender { class Object; // An object to attach as user data to the osg::Node, allows us to get an Object back from a Node when we are doing a ray query class ObjectTag : public TagBase { public: ObjectTag (Object* object); Object* mObject; virtual QString getToolTip (bool hideBasics) const; }; class Object { const CSMWorld::Data& mData; std::string mReferenceId; std::string mReferenceableId; osg::ref_ptr mBaseNode; osg::ref_ptr mOutline; bool mSelected; osg::Group* mParentNode; Resource::ResourceSystem* mResourceSystem; bool mForceBaseToZero; /// Not implemented Object (const Object&); /// Not implemented Object& operator= (const Object&); /// Remove object from node (includes deleting) void clear(); /// Update model /// @note Make sure adjustTransform() was called first so world space particles get positioned correctly void update(); /// Adjust position, orientation and scale void adjustTransform(); /// Throws an exception if *this was constructed with referenceable const CSMWorld::CellRef& getReference() const; public: Object (CSMWorld::Data& data, osg::Group *cellNode, const std::string& id, bool referenceable, bool forceBaseToZero = false); /// \param forceBaseToZero If this is a reference ignore the coordinates and place /// it at 0, 0, 0 instead. ~Object(); /// Mark the object as selected, selected objects show an outline effect void setSelected(bool selected); bool getSelected() const; /// \return Did this call result in a modification of the visual representation of /// this object? bool referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); /// \return Did this call result in a modification of the visual representation of /// this object? bool referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); /// \return Did this call result in a modification of the visual representation of /// this object? bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); /// Returns an empty string if this is a refereceable-type object. std::string getReferenceId() const; std::string getReferenceableId() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/pagedworldspacewidget.cpp000066400000000000000000000415701264522266000261650ustar00rootroot00000000000000#include "pagedworldspacewidget.hpp" #include #include #include #include #include #include #include "../../model/world/tablemimedata.hpp" #include "../../model/world/idtable.hpp" #include "../widget/scenetooltoggle.hpp" #include "../widget/scenetoolmode.hpp" #include "../widget/scenetooltoggle2.hpp" #include "editmode.hpp" #include "elements.hpp" bool CSVRender::PagedWorldspaceWidget::adjustCells() { bool modified = false; bool wasEmpty = mCells.empty(); const CSMWorld::IdCollection& cells = mDocument.getData().getCells(); { // remove/update std::map::iterator iter (mCells.begin()); while (iter!=mCells.end()) { if (!mSelection.has (iter->first)) { // remove delete iter->second; mCells.erase (iter++); modified = true; } else { // update int index = cells.searchId (iter->first.getId (mWorldspace)); bool deleted = index==-1 || cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted; if (deleted!=iter->second->isDeleted()) { modified = true; std::auto_ptr cell (new Cell (mDocument.getData(), mRootNode, iter->first.getId (mWorldspace), deleted)); delete iter->second; iter->second = cell.release(); } else if (!deleted) { // delete state has not changed -> just update // TODO check if name or region field has changed (cell marker) // FIXME: config setting //std::string name = cells.getRecord(index).get().mName; //std::string region = cells.getRecord(index).get().mRegion; modified = true; } ++iter; } } } // add for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { if (mCells.find (*iter)==mCells.end()) { addCellToScene (*iter); modified = true; } } if (modified) { for (std::map::const_iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) { int mask = 0; for (int i=CellArrow::Direction_North; i<=CellArrow::Direction_East; i *= 2) { CSMWorld::CellCoordinates coordinates (iter->second->getCoordinates()); switch (i) { case CellArrow::Direction_North: coordinates = coordinates.move (0, 1); break; case CellArrow::Direction_West: coordinates = coordinates.move (-1, 0); break; case CellArrow::Direction_South: coordinates = coordinates.move (0, -1); break; case CellArrow::Direction_East: coordinates = coordinates.move (1, 0); break; } if (!mSelection.has (coordinates)) mask |= i; } iter->second->setCellArrows (mask); } } /// \todo do not overwrite manipulator object /// \todo move code to useViewHint function if (modified && wasEmpty) mView->setCameraManipulator(new osgGA::TrackballManipulator); return modified; } void CSVRender::PagedWorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { WorldspaceWidget::addVisibilitySelectorButtons (tool); tool->addButton (Element_Terrain, "Terrain"); tool->addButton (Element_Fog, "Fog", "", true); } void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( CSVWidget::SceneToolMode *tool) { WorldspaceWidget::addEditModeSelectorButtons (tool); /// \todo replace EditMode with suitable subclasses tool->addButton ( new EditMode (this, QIcon (":placeholder"), Element_Reference, "Terrain shape editing"), "terrain-shape"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Element_Reference, "Terrain texture editing"), "terrain-texture"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Element_Reference, "Terrain vertex paint editing"), "terrain-vertex"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Element_Reference, "Terrain movement"), "terrain-move"); } void CSVRender::PagedWorldspaceWidget::handleMouseClick (osg::ref_ptr tag, const std::string& button, bool shift) { if (tag && tag->getElement()==Element_CellArrow) { if (button=="p-edit" || button=="s-edit") { if (CellArrowTag *cellArrowTag = dynamic_cast (tag.get())) { CellArrow *arrow = cellArrowTag->getCellArrow(); CSMWorld::CellCoordinates coordinates = arrow->getCoordinates(); CellArrow::Direction direction = arrow->getDirection(); int x = 0; int y = 0; switch (direction) { case CellArrow::Direction_North: y = 1; break; case CellArrow::Direction_West: x = -1; break; case CellArrow::Direction_South: y = -1; break; case CellArrow::Direction_East: x = 1; break; } bool modified = false; if (shift) { if (button=="p-edit") addCellSelection (x, y); else moveCellSelection (x, y); modified = true; } else { CSMWorld::CellCoordinates newCoordinates = coordinates.move (x, y); if (mCells.find (newCoordinates)==mCells.end()) { addCellToScene (newCoordinates); mSelection.add (newCoordinates); modified = true; } if (button=="s-edit") { if (mCells.find (coordinates)!=mCells.end()) { removeCellFromScene (coordinates); mSelection.remove (coordinates); modified = true; } } } if (modified) adjustCells(); return; } } } WorldspaceWidget::handleMouseClick (tag, button, shift); } void CSVRender::PagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceableDataChanged (topLeft, bottomRight)) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::referenceableAboutToBeRemoved ( const QModelIndex& parent, int start, int end) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceableAboutToBeRemoved (parent, start, end)) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::referenceableAdded (const QModelIndex& parent, int start, int end) { CSMWorld::IdTable& referenceables = dynamic_cast ( *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables)); for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) { QModelIndex topLeft = referenceables.index (start, 0); QModelIndex bottomRight = referenceables.index (end, referenceables.columnCount()); if (iter->second->referenceableDataChanged (topLeft, bottomRight)) flagAsModified(); } } void CSVRender::PagedWorldspaceWidget::referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceDataChanged (topLeft, bottomRight)) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceAboutToBeRemoved (parent, start, end)) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent, int start, int end) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceAdded (parent, start, end)) flagAsModified(); } std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction() { osg::Vec3d eye, center, up; mView->getCamera()->getViewMatrixAsLookAt(eye, center, up); osg::Vec3d position = eye; std::ostringstream stream; stream << "player->position " << position.x() << ", " << position.y() << ", " << position.z() << ", 0"; return stream.str(); } void CSVRender::PagedWorldspaceWidget::addCellToScene ( const CSMWorld::CellCoordinates& coordinates) { const CSMWorld::IdCollection& cells = mDocument.getData().getCells(); int index = cells.searchId (coordinates.getId (mWorldspace)); bool deleted = index==-1 || cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted; Cell *cell = new Cell (mDocument.getData(), mRootNode, coordinates.getId (mWorldspace), deleted); mCells.insert (std::make_pair (coordinates, cell)); } void CSVRender::PagedWorldspaceWidget::removeCellFromScene ( const CSMWorld::CellCoordinates& coordinates) { std::map::iterator iter = mCells.find (coordinates); if (iter!=mCells.end()) { delete iter->second; mCells.erase (iter); } } void CSVRender::PagedWorldspaceWidget::addCellSelection (int x, int y) { CSMWorld::CellSelection newSelection = mSelection; newSelection.move (x, y); for (CSMWorld::CellSelection::Iterator iter (newSelection.begin()); iter!=newSelection.end(); ++iter) { if (mCells.find (*iter)==mCells.end()) { addCellToScene (*iter); mSelection.add (*iter); } } } void CSVRender::PagedWorldspaceWidget::moveCellSelection (int x, int y) { CSMWorld::CellSelection newSelection = mSelection; newSelection.move (x, y); for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { if (!newSelection.has (*iter)) removeCellFromScene (*iter); } for (CSMWorld::CellSelection::Iterator iter (newSelection.begin()); iter!=newSelection.end(); ++iter) { if (!mSelection.has (*iter)) addCellToScene (*iter); } mSelection = newSelection; } CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document) : WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"), mControlElements(NULL), mDisplayCellCoord(true) { QAbstractItemModel *cells = document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells); connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&))); connect (cells, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (cellRemoved (const QModelIndex&, int, int))); connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (cellAdded (const QModelIndex&, int, int))); } CSVRender::PagedWorldspaceWidget::~PagedWorldspaceWidget() { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) { delete iter->second; } } void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) { if (!hint.empty()) { CSMWorld::CellSelection selection; if (hint[0]=='c') { // syntax: c:#x1 y1; #x2 y2 (number of coordinate pairs can be 0 or larger) char ignore; std::istringstream stream (hint.c_str()); if (stream >> ignore) { char ignore1; // : or ; char ignore2; // # int x, y; while (stream >> ignore1 >> ignore2 >> x >> y) selection.add (CSMWorld::CellCoordinates (x, y)); /// \todo adjust camera position } } else if (hint[0]=='r') { /// \todo implement 'r' type hints } setCellSelection (selection); } } void CSVRender::PagedWorldspaceWidget::setCellSelection (const CSMWorld::CellSelection& selection) { mSelection = selection; if (adjustCells()) flagAsModified(); emit cellSelectionChanged (mSelection); } std::pair< int, int > CSVRender::PagedWorldspaceWidget::getCoordinatesFromId (const std::string& record) const { std::istringstream stream (record.c_str()); char ignore; int x, y; stream >> ignore >> x >> y; return std::make_pair(x, y); } bool CSVRender::PagedWorldspaceWidget::handleDrop ( const std::vector< CSMWorld::UniversalId >& data, DropType type) { if (WorldspaceWidget::handleDrop (data, type)) return true; if (type!=Type_CellsExterior) return false; bool selectionChanged = false; for (unsigned i = 0; i < data.size(); ++i) { std::pair coordinates(getCoordinatesFromId(data[i].getId())); if (mSelection.add(CSMWorld::CellCoordinates(coordinates.first, coordinates.second))) { selectionChanged = true; } } if (selectionChanged) { if (adjustCells()) flagAsModified(); emit cellSelectionChanged(mSelection); } return true; } CSVRender::WorldspaceWidget::dropRequirments CSVRender::PagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::DropType type) const { dropRequirments requirements = WorldspaceWidget::getDropRequirements (type); if (requirements!=ignored) return requirements; switch (type) { case Type_CellsExterior: return canHandle; case Type_CellsInterior: return needUnpaged; default: return ignored; } } unsigned int CSVRender::PagedWorldspaceWidget::getVisibilityMask() const { return WorldspaceWidget::getVisibilityMask() | mControlElements->getSelection(); } void CSVRender::PagedWorldspaceWidget::clearSelection (int elementMask) { for (std::map::iterator iter = mCells.begin(); iter!=mCells.end(); ++iter) iter->second->setSelection (elementMask, Cell::Selection_Clear); flagAsModified(); } CSVWidget::SceneToolToggle *CSVRender::PagedWorldspaceWidget::makeControlVisibilitySelector ( CSVWidget::SceneToolbar *parent) { mControlElements = new CSVWidget::SceneToolToggle (parent, "Controls & Guides Visibility", ":placeholder"); mControlElements->addButton (":placeholder", Element_CellMarker, ":placeholder", "Cell marker"); mControlElements->addButton (":placeholder", Element_CellArrow, ":placeholder", "Cell arrows"); mControlElements->addButton (":placeholder", Element_CellBorder, ":placeholder", "Cell border"); mControlElements->setSelection (0xffffffff); connect (mControlElements, SIGNAL (selectionChanged()), this, SLOT (elementSelectionChanged())); return mControlElements; } void CSVRender::PagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { /// \todo check if no selected cell is affected and do not update, if that is the case if (adjustCells()) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::cellRemoved (const QModelIndex& parent, int start, int end) { if (adjustCells()) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int start, int end) { /// \todo check if no selected cell is affected and do not update, if that is the case if (adjustCells()) flagAsModified(); } openmw-openmw-0.38.0/apps/opencs/view/render/pagedworldspacewidget.hpp000066400000000000000000000102441264522266000261640ustar00rootroot00000000000000#ifndef OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H #define OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H #include #include "../../model/world/cellselection.hpp" #include "worldspacewidget.hpp" #include "cell.hpp" namespace CSVWidget { class SceneToolToggle; } namespace CSVRender { class TextOverlay; class OverlayMask; class PagedWorldspaceWidget : public WorldspaceWidget { Q_OBJECT CSMDoc::Document& mDocument; CSMWorld::CellSelection mSelection; std::map mCells; std::string mWorldspace; CSVWidget::SceneToolToggle *mControlElements; bool mDisplayCellCoord; private: std::pair getCoordinatesFromId(const std::string& record) const; /// Bring mCells into sync with mSelection again. /// /// \return Any cells added or removed? bool adjustCells(); virtual void referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); virtual void referenceableAdded (const QModelIndex& index, int start, int end); virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); virtual void referenceAdded (const QModelIndex& index, int start, int end); virtual std::string getStartupInstruction(); /// \note Does not update the view or any cell marker void addCellToScene (const CSMWorld::CellCoordinates& coordinates); /// \note Does not update the view or any cell marker /// /// \note Calling this function for a cell that is not in the selection is a no-op. void removeCellFromScene (const CSMWorld::CellCoordinates& coordinates); /// \note Does not update the view or any cell marker void addCellSelection (int x, int y); /// \note Does not update the view or any cell marker void moveCellSelection (int x, int y); public: PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document); ///< \note Sets the cell area selection to an invalid value to indicate that currently /// no cells are displayed. The cells to be displayed will be specified later through /// hint system. virtual ~PagedWorldspaceWidget(); void useViewHint (const std::string& hint); void setCellSelection(const CSMWorld::CellSelection& selection); /// \return Drop handled? virtual bool handleDrop (const std::vector& data, DropType type); virtual dropRequirments getDropRequirements(DropType type) const; /// \attention The created tool is not added to the toolbar (via addTool). Doing /// that is the responsibility of the calling function. virtual CSVWidget::SceneToolToggle *makeControlVisibilitySelector ( CSVWidget::SceneToolbar *parent); virtual unsigned int getVisibilityMask() const; /// \param elementMask Elements to be affected by the clear operation virtual void clearSelection (int elementMask); protected: virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool); virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool); virtual void handleMouseClick (osg::ref_ptr tag, const std::string& button, bool shift); signals: void cellSelectionChanged (const CSMWorld::CellSelection& selection); private slots: virtual void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); virtual void cellRemoved (const QModelIndex& parent, int start, int end); virtual void cellAdded (const QModelIndex& index, int start, int end); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/previewwidget.cpp000066400000000000000000000107151264522266000244770ustar00rootroot00000000000000#include "previewwidget.hpp" #include #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, const std::string& id, bool referenceable, QWidget *parent) : SceneWidget (data.getResourceSystem(), parent), mData (data), mObject(data, mRootNode, id, referenceable) { mView->setCameraManipulator(new osgGA::TrackballManipulator); QAbstractItemModel *referenceables = mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables); connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (referenceableDataChanged (const QModelIndex&, const QModelIndex&))); connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int))); if (!referenceable) { QAbstractItemModel *references = mData.getTableModel (CSMWorld::UniversalId::Type_References); connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (referenceDataChanged (const QModelIndex&, const QModelIndex&))); connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (referenceAboutToBeRemoved (const QModelIndex&, int, int))); } } void CSVRender::PreviewWidget::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (mObject.referenceableDataChanged (topLeft, bottomRight)) flagAsModified(); if (mObject.getReferenceId().empty()) { CSMWorld::IdTable& referenceables = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables)); QModelIndex index = referenceables.getModelIndex (mObject.getReferenceableId(), referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Modification)); if (referenceables.data (index).toInt()==CSMWorld::RecordBase::State_Deleted) emit closeRequest(); } } void CSVRender::PreviewWidget::referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) { if (mObject.referenceableAboutToBeRemoved (parent, start, end)) flagAsModified(); if (mObject.getReferenceableId().empty()) return; CSMWorld::IdTable& referenceables = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables)); QModelIndex index = referenceables.getModelIndex (mObject.getReferenceableId(), 0); if (index.row()>=start && index.row()<=end) { if (mObject.getReferenceId().empty()) { // this is a preview for a referenceble emit closeRequest(); } } } void CSVRender::PreviewWidget::referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (mObject.referenceDataChanged (topLeft, bottomRight)) flagAsModified(); if (mObject.getReferenceId().empty()) return; CSMWorld::IdTable& references = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_References)); // check for deleted state { QModelIndex index = references.getModelIndex (mObject.getReferenceId(), references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification)); if (references.data (index).toInt()==CSMWorld::RecordBase::State_Deleted) { emit closeRequest(); return; } } int columnIndex = references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId); QModelIndex index = references.getModelIndex (mObject.getReferenceId(), columnIndex); if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) if (index.column()>=topLeft.column() && index.column()<=bottomRight.row()) emit referenceableIdChanged (mObject.getReferenceableId()); } void CSVRender::PreviewWidget::referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) { if (mObject.getReferenceId().empty()) return; CSMWorld::IdTable& references = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_References)); QModelIndex index = references.getModelIndex (mObject.getReferenceId(), 0); if (index.row()>=start && index.row()<=end) emit closeRequest(); } openmw-openmw-0.38.0/apps/opencs/view/render/previewwidget.hpp000066400000000000000000000021261264522266000245010ustar00rootroot00000000000000#ifndef OPENCS_VIEW_PREVIEWWIDGET_H #define OPENCS_VIEW_PREVIEWWIDGET_H #include "scenewidget.hpp" #include "object.hpp" class QModelIndex; namespace VFS { class Manager; } namespace CSMWorld { class Data; } namespace CSVRender { class PreviewWidget : public SceneWidget { Q_OBJECT CSMWorld::Data& mData; CSVRender::Object mObject; public: PreviewWidget (CSMWorld::Data& data, const std::string& id, bool referenceable, QWidget *parent = 0); signals: void closeRequest(); void referenceableIdChanged (const std::string& id); private slots: void referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/scenewidget.cpp000066400000000000000000000202611264522266000241100ustar00rootroot00000000000000#include "scenewidget.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "../widget/scenetoolmode.hpp" #include "lighting.hpp" namespace CSVRender { RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) , mRootNode(0) { osgViewer::CompositeViewer& viewer = CompositeViewer::get(); osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); //ds->setNumMultiSamples(8); osg::ref_ptr traits = new osg::GraphicsContext::Traits; traits->windowName = ""; traits->windowDecoration = true; traits->x = 0; traits->y = 0; traits->width = width(); traits->height = height(); traits->doubleBuffer = true; traits->alpha = ds->getMinimumNumAlphaBits(); traits->stencil = ds->getMinimumNumStencilBits(); traits->sampleBuffers = ds->getMultiSamples(); traits->samples = ds->getNumMultiSamples(); // Doesn't make much sense as we're running on demand updates, and there seems to be a bug with the refresh rate when running multiple QGLWidgets traits->vsync = false; mView = new osgViewer::View; osg::ref_ptr window = new osgQt::GraphicsWindowQt(traits.get()); QLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(window->getGLWidget()); setLayout(layout); // Pass events through this widget first window->getGLWidget()->installEventFilter(this); mView->getCamera()->setGraphicsContext(window); mView->getCamera()->setClearColor( osg::Vec4(0.2, 0.2, 0.6, 1.0) ); mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); mView->getCamera()->setProjectionMatrixAsPerspective(30.0f, static_cast(traits->width)/static_cast(traits->height), 1.0f, 10000.0f ); mRootNode = new osg::Group; mView->getCamera()->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); mView->getCamera()->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON); mView->setSceneData(mRootNode); // Press S to reveal profiling stats mView->addEventHandler(new osgViewer::StatsHandler); mView->getCamera()->setCullMask(~(0x1)); viewer.addView(mView); viewer.setDone(false); viewer.realize(); } RenderWidget::~RenderWidget() { CompositeViewer::get().removeView(mView); } void RenderWidget::flagAsModified() { mView->requestRedraw(); } void RenderWidget::setVisibilityMask(int mask) { // 0x1 reserved for separating cull and update visitors mView->getCamera()->setCullMask(mask<<1); } bool RenderWidget::eventFilter(QObject* obj, QEvent* event) { // handle event in this widget, is there a better way to do this? if (event->type() == QEvent::MouseButtonPress) mousePressEvent(static_cast(event)); if (event->type() == QEvent::MouseButtonRelease) mouseReleaseEvent(static_cast(event)); if (event->type() == QEvent::MouseMove) mouseMoveEvent(static_cast(event)); if (event->type() == QEvent::KeyPress) keyPressEvent(static_cast(event)); if (event->type() == QEvent::KeyRelease) keyReleaseEvent(static_cast(event)); if (event->type() == QEvent::Wheel) wheelEvent(static_cast(event)); // Always pass the event on to GLWidget, i.e. to OSG event queue return QObject::eventFilter(obj, event); } // -------------------------------------------------- CompositeViewer::CompositeViewer() : mSimulationTime(0.0) { #if QT_VERSION >= 0x050000 // Qt5 is currently crashing and reporting "Cannot make QOpenGLContext current in a different thread" when the viewer is run multi-threaded, this is regression from Qt4 osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::SingleThreaded; #else osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::DrawThreadPerContext; #endif setThreadingModel(threadingModel); // disable the default setting of viewer.done() by pressing Escape. setKeyEventSetsDone(0); // Only render when the camera position changed, or content flagged dirty //setRunFrameScheme(osgViewer::ViewerBase::ON_DEMAND); setRunFrameScheme(osgViewer::ViewerBase::CONTINUOUS); connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) ); mTimer.start( 10 ); } CompositeViewer &CompositeViewer::get() { static CompositeViewer sThis; return sThis; } void CompositeViewer::update() { mSimulationTime += mFrameTimer.time_s(); mFrameTimer.setStartTick(); frame(mSimulationTime); } // --------------------------------------------------- SceneWidget::SceneWidget(boost::shared_ptr resourceSystem, QWidget *parent, Qt::WindowFlags f) : RenderWidget(parent, f) , mResourceSystem(resourceSystem) , mLighting(NULL) , mHasDefaultAmbient(false) { // we handle lighting manually mView->setLightingMode(osgViewer::View::NO_LIGHT); setLighting(&mLightingDay); /// \todo make shortcut configurable QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut); connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest())); } SceneWidget::~SceneWidget() { // Since we're holding on to the scene templates past the existance of this graphics context, we'll need to manually release the created objects mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState()); } void SceneWidget::setLighting(Lighting *lighting) { if (mLighting) mLighting->deactivate(); mLighting = lighting; mLighting->activate (mRootNode); osg::Vec4f ambient = mLighting->getAmbientColour(mHasDefaultAmbient ? &mDefaultAmbient : 0); setAmbient(ambient); flagAsModified(); } void SceneWidget::setAmbient(const osg::Vec4f& ambient) { osg::ref_ptr stateset = new osg::StateSet; osg::ref_ptr lightmodel = new osg::LightModel; lightmodel->setAmbientIntensity(ambient); stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON); stateset->setMode(GL_LIGHT0, osg::StateAttribute::ON); stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON); mRootNode->setStateSet(stateset); } void SceneWidget::selectLightingMode (const std::string& mode) { if (mode=="day") setLighting (&mLightingDay); else if (mode=="night") setLighting (&mLightingNight); else if (mode=="bright") setLighting (&mLightingBright); } CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent) { CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Lighting Mode"); /// \todo replace icons tool->addButton (":scenetoolbar/day", "day", "Day" "
  • Cell specific ambient in interiors
  • " "
  • Low ambient in exteriors
  • " "
  • Strong directional light source
  • " "
  • This mode closely resembles day time in-game
"); tool->addButton (":scenetoolbar/night", "night", "Night" "
  • Cell specific ambient in interiors
  • " "
  • Low ambient in exteriors
  • " "
  • Weak directional light source
  • " "
  • This mode closely resembles night time in-game
"); tool->addButton (":scenetoolbar/bright", "bright", "Bright" "
  • Maximum ambient
  • " "
  • Strong directional light source
"); connect (tool, SIGNAL (modeChanged (const std::string&)), this, SLOT (selectLightingMode (const std::string&))); return tool; } void SceneWidget::setDefaultAmbient (const osg::Vec4f& colour) { mDefaultAmbient = colour; mHasDefaultAmbient = true; setAmbient(mLighting->getAmbientColour(&mDefaultAmbient)); } } openmw-openmw-0.38.0/apps/opencs/view/render/scenewidget.hpp000066400000000000000000000052401264522266000241150ustar00rootroot00000000000000#ifndef OPENCS_VIEW_SCENEWIDGET_H #define OPENCS_VIEW_SCENEWIDGET_H #include #include #include #include "lightingday.hpp" #include "lightingnight.hpp" #include "lightingbright.hpp" #include #include namespace Resource { class ResourceSystem; } namespace osg { class Group; } namespace CSVWidget { class SceneToolMode; class SceneToolbar; } namespace CSVRender { class Lighting; class RenderWidget : public QWidget { Q_OBJECT public: RenderWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); virtual ~RenderWidget(); void flagAsModified(); void setVisibilityMask(int mask); bool eventFilter(QObject *, QEvent *); protected: osg::ref_ptr mView; osg::Group* mRootNode; QTimer mTimer; }; // Extension of RenderWidget to support lighting mode selection & toolbar class SceneWidget : public RenderWidget { Q_OBJECT public: SceneWidget(boost::shared_ptr resourceSystem, QWidget* parent = 0, Qt::WindowFlags f = 0); virtual ~SceneWidget(); CSVWidget::SceneToolMode *makeLightingSelector (CSVWidget::SceneToolbar *parent); ///< \attention The created tool is not added to the toolbar (via addTool). Doing that /// is the responsibility of the calling function. void setDefaultAmbient (const osg::Vec4f& colour); ///< \note The actual ambient colour may differ based on lighting settings. protected: void setLighting (Lighting *lighting); ///< \attention The ownership of \a lighting is not transferred to *this. void setAmbient(const osg::Vec4f& ambient); boost::shared_ptr mResourceSystem; Lighting* mLighting; osg::Vec4f mDefaultAmbient; bool mHasDefaultAmbient; LightingDay mLightingDay; LightingNight mLightingNight; LightingBright mLightingBright; private slots: void selectLightingMode (const std::string& mode); signals: void focusToolbarRequest(); }; // There are rendering glitches when using multiple Viewer instances, work around using CompositeViewer with multiple views class CompositeViewer : public QObject, public osgViewer::CompositeViewer { Q_OBJECT public: CompositeViewer(); static CompositeViewer& get(); QTimer mTimer; private: osg::Timer mFrameTimer; double mSimulationTime; public slots: void update(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/tagbase.cpp000066400000000000000000000004101264522266000232070ustar00rootroot00000000000000 #include "tagbase.hpp" CSVRender::TagBase::TagBase (Elements element) : mElement (element) {} CSVRender::Elements CSVRender::TagBase::getElement() const { return mElement; } QString CSVRender::TagBase::getToolTip (bool hideBasics) const { return ""; } openmw-openmw-0.38.0/apps/opencs/view/render/tagbase.hpp000066400000000000000000000006421264522266000232230ustar00rootroot00000000000000#ifndef OPENCS_VIEW_TAGBASE_H #define OPENCS_VIEW_TAGBASE_H #include #include #include "elements.hpp" namespace CSVRender { class TagBase : public osg::Referenced { Elements mElement; public: TagBase (Elements element); Elements getElement() const; virtual QString getToolTip (bool hideBasics) const; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/terrainstorage.cpp000066400000000000000000000031611264522266000246400ustar00rootroot00000000000000#include "terrainstorage.hpp" namespace CSVRender { TerrainStorage::TerrainStorage(const CSMWorld::Data &data) : ESMTerrain::Storage(data.getResourceSystem()->getVFS()) , mData(data) { } const ESM::Land* TerrainStorage::getLand(int cellX, int cellY) { std::ostringstream stream; stream << "#" << cellX << " " << cellY; // The cell isn't guaranteed to have Land. This is because the terrain implementation // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell int index = mData.getLand().searchId(stream.str()); if (index == -1) return NULL; const ESM::Land& land = mData.getLand().getRecord(index).get(); int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; land.loadData (mask); return &land; } const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) { int numRecords = mData.getLandTextures().getSize(); for (int i=0; imIndex == index && ltex->mPluginIndex == plugin) return ltex; } return NULL; } void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) { // not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells throw std::runtime_error("getBounds not implemented"); } } openmw-openmw-0.38.0/apps/opencs/view/render/terrainstorage.hpp000066400000000000000000000013131264522266000246420ustar00rootroot00000000000000#ifndef OPENCS_RENDER_TERRAINSTORAGE_H #define OPENCS_RENDER_TERRAINSTORAGE_H #include #include "../../model/world/data.hpp" namespace CSVRender { /** * @brief A bridge between the terrain component and OpenCS's terrain data storage. */ class TerrainStorage : public ESMTerrain::Storage { public: TerrainStorage(const CSMWorld::Data& data); private: const CSMWorld::Data& mData; virtual const ESM::Land* getLand (int cellX, int cellY); virtual const ESM::LandTexture* getLandTexture(int index, short plugin); virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/unpagedworldspacewidget.cpp000066400000000000000000000141731264522266000265270ustar00rootroot00000000000000#include "unpagedworldspacewidget.hpp" #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/tablemimedata.hpp" #include "../widget/scenetooltoggle.hpp" #include "../widget/scenetooltoggle2.hpp" #include "elements.hpp" void CSVRender::UnpagedWorldspaceWidget::update() { const CSMWorld::Record& record = dynamic_cast&> (mCellsModel->getRecord (mCellId)); osg::Vec4f colour = SceneUtil::colourFromRGB(record.get().mAmbi.mAmbient); setDefaultAmbient (colour); /// \todo deal with mSunlight and mFog/mForDensity flagAsModified(); } CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, QWidget* parent) : WorldspaceWidget (document, parent), mCellId (cellId) { mCellsModel = &dynamic_cast ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); mReferenceablesModel = &dynamic_cast ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables)); connect (mCellsModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&))); connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int))); update(); mCell.reset (new Cell (document.getData(), mRootNode, mCellId)); mView->setCameraManipulator(new osgGA::TrackballManipulator); } void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { int index = mCellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, index); if (cellIndex.row()>=topLeft.row() && cellIndex.row()<=bottomRight.row()) { if (mCellsModel->data (cellIndex).toInt()==CSMWorld::RecordBase::State_Deleted) { emit closeRequest(); } else { /// \todo possible optimisation: check columns and update only if relevant columns have /// changed update(); } } } void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end) { QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, 0); if (cellIndex.row()>=start && cellIndex.row()<=end) emit closeRequest(); } bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector& data, DropType type) { if (WorldspaceWidget::handleDrop (data, type)) return true; if (type!=Type_CellsInterior) return false; mCellId = data.begin()->getId(); mCell.reset (new Cell (getDocument().getData(), mRootNode, mCellId)); update(); emit cellChanged(*data.begin()); return true; } void CSVRender::UnpagedWorldspaceWidget::clearSelection (int elementMask) { mCell->setSelection (elementMask, Cell::Selection_Clear); flagAsModified(); } void CSVRender::UnpagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (mCell.get()) if (mCell.get()->referenceableDataChanged (topLeft, bottomRight)) flagAsModified(); } void CSVRender::UnpagedWorldspaceWidget::referenceableAboutToBeRemoved ( const QModelIndex& parent, int start, int end) { if (mCell.get()) if (mCell.get()->referenceableAboutToBeRemoved (parent, start, end)) flagAsModified(); } void CSVRender::UnpagedWorldspaceWidget::referenceableAdded (const QModelIndex& parent, int start, int end) { if (mCell.get()) { QModelIndex topLeft = mReferenceablesModel->index (start, 0); QModelIndex bottomRight = mReferenceablesModel->index (end, mReferenceablesModel->columnCount()); if (mCell.get()->referenceableDataChanged (topLeft, bottomRight)) flagAsModified(); } } void CSVRender::UnpagedWorldspaceWidget::referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (mCell.get()) if (mCell.get()->referenceDataChanged (topLeft, bottomRight)) flagAsModified(); } void CSVRender::UnpagedWorldspaceWidget::referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) { if (mCell.get()) if (mCell.get()->referenceAboutToBeRemoved (parent, start, end)) flagAsModified(); } void CSVRender::UnpagedWorldspaceWidget::referenceAdded (const QModelIndex& parent, int start, int end) { if (mCell.get()) if (mCell.get()->referenceAdded (parent, start, end)) flagAsModified(); } void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { WorldspaceWidget::addVisibilitySelectorButtons (tool); tool->addButton (Element_Terrain, "Terrain", "", true); tool->addButton (Element_Fog, "Fog"); } std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction() { osg::Vec3d eye, center, up; mView->getCamera()->getViewMatrixAsLookAt(eye, center, up); osg::Vec3d position = eye; std::ostringstream stream; stream << "player->positionCell " << position.x() << ", " << position.y() << ", " << position.z() << ", 0, \"" << mCellId << "\""; return stream.str(); } CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::DropType type) const { dropRequirments requirements = WorldspaceWidget::getDropRequirements (type); if (requirements!=ignored) return requirements; switch(type) { case Type_CellsInterior: return canHandle; case Type_CellsExterior: return needPaged; default: return ignored; } } openmw-openmw-0.38.0/apps/opencs/view/render/unpagedworldspacewidget.hpp000066400000000000000000000043261264522266000265330ustar00rootroot00000000000000#ifndef OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H #define OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H #include #include #include "worldspacewidget.hpp" #include "cell.hpp" class QModelIndex; namespace CSMDoc { class Document; } namespace CSMWorld { class IdTable; } namespace CSVRender { class UnpagedWorldspaceWidget : public WorldspaceWidget { Q_OBJECT std::string mCellId; CSMWorld::IdTable *mCellsModel; CSMWorld::IdTable *mReferenceablesModel; std::auto_ptr mCell; void update(); public: UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, QWidget *parent); virtual dropRequirments getDropRequirements(DropType type) const; /// \return Drop handled? virtual bool handleDrop (const std::vector& data, DropType type); /// \param elementMask Elements to be affected by the clear operation virtual void clearSelection (int elementMask); private: virtual void referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); virtual void referenceableAdded (const QModelIndex& index, int start, int end); virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); virtual void referenceAdded (const QModelIndex& index, int start, int end); virtual std::string getStartupInstruction(); protected: virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool); private slots: void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); signals: void cellChanged(const CSMWorld::UniversalId& id); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/render/worldspacewidget.cpp000066400000000000000000000571571264522266000251740ustar00rootroot00000000000000#include "worldspacewidget.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../model/world/universalid.hpp" #include "../../model/world/idtable.hpp" #include "../../model/prefs/state.hpp" #include "../widget/scenetoolmode.hpp" #include "../widget/scenetooltoggle2.hpp" #include "../widget/scenetoolrun.hpp" #include "object.hpp" #include "elements.hpp" #include "editmode.hpp" #include "instancemode.hpp" CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent) : SceneWidget (document.getData().getResourceSystem(), parent), mSceneElements(0), mRun(0), mDocument(document), mInteractionMask (0), mEditMode (0), mLocked (false), mDragging (false), mToolTipPos (-1, -1) { setAcceptDrops(true); QAbstractItemModel *referenceables = document.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables); connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (referenceableDataChanged (const QModelIndex&, const QModelIndex&))); connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int))); connect (referenceables, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (referenceableAdded (const QModelIndex&, int, int))); QAbstractItemModel *references = document.getData().getTableModel (CSMWorld::UniversalId::Type_References); connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (referenceDataChanged (const QModelIndex&, const QModelIndex&))); connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (referenceAboutToBeRemoved (const QModelIndex&, int, int))); connect (references, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (referenceAdded (const QModelIndex&, int, int))); QAbstractItemModel *debugProfiles = document.getData().getTableModel (CSMWorld::UniversalId::Type_DebugProfiles); connect (debugProfiles, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (debugProfileDataChanged (const QModelIndex&, const QModelIndex&))); connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (debugProfileAboutToBeRemoved (const QModelIndex&, int, int))); connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), this, SLOT (settingChanged (const CSMPrefs::Setting *))); CSMPrefs::get()["3D Scene Input"].update(); CSMPrefs::get()["Tooltips"].update(); mToolTipDelayTimer.setSingleShot (true); connect (&mToolTipDelayTimer, SIGNAL (timeout()), this, SLOT (showToolTip())); } CSVRender::WorldspaceWidget::~WorldspaceWidget () { } void CSVRender::WorldspaceWidget::settingChanged (const CSMPrefs::Setting *setting) { if (storeMappingSetting (setting)) return; if (*setting=="3D Scene Input/drag-factor") mDragFactor = setting->toDouble(); else if (*setting=="3D Scene Input/drag-wheel-factor") mDragWheelFactor = setting->toDouble(); else if (*setting=="3D Scene Input/drag-shift-factor") mDragShiftFactor = setting->toDouble(); else if (*setting=="Tooltips/scene-delay") mToolTipDelay = setting->toInt(); else if (*setting=="Tooltips/scene") mShowToolTips = setting->isTrue(); } void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode) { if (mode=="1st") mView->setCameraManipulator(new osgGA::FirstPersonManipulator); else if (mode=="free") mView->setCameraManipulator(new osgGA::FirstPersonManipulator); else if (mode=="orbit") mView->setCameraManipulator(new osgGA::OrbitManipulator); } void CSVRender::WorldspaceWidget::useViewHint (const std::string& hint) {} void CSVRender::WorldspaceWidget::selectDefaultNavigationMode() { mView->setCameraManipulator(new osgGA::FirstPersonManipulator); } CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( CSVWidget::SceneToolbar *parent) { CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Camera Mode"); /// \todo replace icons /// \todo consider user-defined button-mapping tool->addButton (":scenetoolbar/1st-person", "1st", "First Person" "
  • Mouse-Look while holding the left button
  • " "
  • WASD movement keys
  • " "
  • Mouse wheel moves the camera forawrd/backward
  • " "
  • Stafing (also vertically) by holding the left mouse button and control
  • " "
  • Camera is held upright
  • " "
  • Hold shift to speed up movement
  • " "
"); tool->addButton (":scenetoolbar/free-camera", "free", "Free Camera" "
  • Mouse-Look while holding the left button
  • " "
  • Stafing (also vertically) via WASD or by holding the left mouse button and control
  • " "
  • Mouse wheel moves the camera forawrd/backward
  • " "
  • Roll camera with Q and E keys
  • " "
  • Hold shift to speed up movement
  • " "
"); tool->addButton (":scenetoolbar/orbiting-camera", "orbit", "Orbiting Camera" "
  • Always facing the centre point
  • " "
  • Rotate around the centre point via WASD or by moving the mouse while holding the left button
  • " "
  • Mouse wheel moves camera away or towards centre point but can not pass through it
  • " "
  • Roll camera with Q and E keys
  • " "
  • Stafing (also vertically) by holding the left mouse button and control (includes relocation of the centre point)
  • " "
  • Hold shift to speed up movement
  • " "
"); connect (tool, SIGNAL (modeChanged (const std::string&)), this, SLOT (selectNavigationMode (const std::string&))); return tool; } CSVWidget::SceneToolToggle2 *CSVRender::WorldspaceWidget::makeSceneVisibilitySelector (CSVWidget::SceneToolbar *parent) { mSceneElements = new CSVWidget::SceneToolToggle2 (parent, "Scene Element Visibility", ":scenetoolbar/scene-view-c", ":scenetoolbar/scene-view-"); addVisibilitySelectorButtons (mSceneElements); mSceneElements->setSelection (0xffffffff); connect (mSceneElements, SIGNAL (selectionChanged()), this, SLOT (elementSelectionChanged())); return mSceneElements; } CSVWidget::SceneToolRun *CSVRender::WorldspaceWidget::makeRunTool ( CSVWidget::SceneToolbar *parent) { CSMWorld::IdTable& debugProfiles = dynamic_cast ( *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_DebugProfiles)); std::vector profiles; int idColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Id); int stateColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Modification); int defaultColumn = debugProfiles.findColumnIndex ( CSMWorld::Columns::ColumnId_DefaultProfile); int size = debugProfiles.rowCount(); for (int i=0; i& data) { DropType output = Type_Other; for (std::vector::const_iterator iter (data.begin()); iter!=data.end(); ++iter) { DropType type = Type_Other; if (iter->getType()==CSMWorld::UniversalId::Type_Cell || iter->getType()==CSMWorld::UniversalId::Type_Cell_Missing) { type = iter->getId().substr (0, 1)=="#" ? Type_CellsExterior : Type_CellsInterior; } else if (iter->getType()==CSMWorld::UniversalId::Type_DebugProfile) type = Type_DebugProfile; if (iter==data.begin()) output = type; else if (output!=type) // mixed types -> ignore return Type_Other; } return output; } CSVRender::WorldspaceWidget::dropRequirments CSVRender::WorldspaceWidget::getDropRequirements (DropType type) const { if (type==Type_DebugProfile) return canHandle; return ignored; } bool CSVRender::WorldspaceWidget::handleDrop (const std::vector& data, DropType type) { if (type==Type_DebugProfile) { if (mRun) { for (std::vector::const_iterator iter (data.begin()); iter!=data.end(); ++iter) mRun->addProfile (iter->getId()); } return true; } return false; } unsigned int CSVRender::WorldspaceWidget::getVisibilityMask() const { return mSceneElements->getSelection(); } void CSVRender::WorldspaceWidget::setInteractionMask (unsigned int mask) { mInteractionMask = mask | Element_CellMarker | Element_CellArrow; } unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const { return mInteractionMask & getVisibilityMask(); } void CSVRender::WorldspaceWidget::setEditLock (bool locked) { dynamic_cast (*mEditMode->getCurrent()).setEditLock (locked); } void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { tool->addButton (Element_Reference, "Instances"); tool->addButton (Element_Water, "Water"); tool->addButton (Element_Pathgrid, "Pathgrid"); } void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool) { /// \todo replace EditMode with suitable subclasses tool->addButton (new InstanceMode (this, tool), "object"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"), "pathgrid"); } CSMDoc::Document& CSVRender::WorldspaceWidget::getDocument() { return mDocument; } void CSVRender::WorldspaceWidget::dragEnterEvent (QDragEnterEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped return; if (mime->fromDocument (mDocument)) { if (mime->holdsType (CSMWorld::UniversalId::Type_Cell) || mime->holdsType (CSMWorld::UniversalId::Type_Cell_Missing) || mime->holdsType (CSMWorld::UniversalId::Type_DebugProfile)) { // These drops are handled through the subview object. event->accept(); } else dynamic_cast (*mEditMode->getCurrent()).dragEnterEvent (event); } } void CSVRender::WorldspaceWidget::dragMoveEvent(QDragMoveEvent *event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped return; if (mime->fromDocument (mDocument)) { if (mime->holdsType (CSMWorld::UniversalId::Type_Cell) || mime->holdsType (CSMWorld::UniversalId::Type_Cell_Missing) || mime->holdsType (CSMWorld::UniversalId::Type_DebugProfile)) { // These drops are handled through the subview object. event->accept(); } else dynamic_cast (*mEditMode->getCurrent()).dragMoveEvent (event); } } bool CSVRender::WorldspaceWidget::storeMappingSetting (const CSMPrefs::Setting *setting) { if (setting->getParent()->getKey()!="3D Scene Input") return false; static const char * const sMappingSettings[] = { "p-navi", "s-navi", "p-edit", "s-edit", "p-select", "s-select", 0 }; for (int i=0; sMappingSettings[i]; ++i) if (setting->getKey()==sMappingSettings[i]) { QString value = QString::fromUtf8 (setting->toString().c_str()); Qt::MouseButton button = Qt::NoButton; if (value.endsWith ("Left Mouse-Button")) button = Qt::LeftButton; else if (value.endsWith ("Right Mouse-Button")) button = Qt::RightButton; else if (value.endsWith ("Middle Mouse-Button")) button = Qt::MiddleButton; else return false; bool ctrl = value.startsWith ("Ctrl-"); mButtonMapping[std::make_pair (button, ctrl)] = sMappingSettings[i]; return true; } return false; } osg::ref_ptr CSVRender::WorldspaceWidget::mousePick (const QPoint& localPos) { // (0,0) is considered the lower left corner of an OpenGL window int x = localPos.x(); int y = height() - localPos.y(); osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x, y)); intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); osgUtil::IntersectionVisitor visitor(intersector); visitor.setTraversalMask(getInteractionMask() << 1); mView->getCamera()->accept(visitor); for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); it != intersector->getIntersections().end(); ++it) { osgUtil::LineSegmentIntersector::Intersection intersection = *it; // reject back-facing polygons osg::Vec3f normal = intersection.getWorldIntersectNormal(); normal = osg::Matrix::transform3x3(normal, mView->getCamera()->getViewMatrix()); if (normal.z() < 0) continue; for (std::vector::iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) { osg::Node* node = *it; if (osg::ref_ptr tag = dynamic_cast(node->getUserData())) return tag; } // ignoring terrain for now // must be terrain, report coordinates // std::cout << "Terrain hit at " << intersection.getWorldIntersectPoint().x() << " " << intersection.getWorldIntersectPoint().y() << std::endl; // return; } return osg::ref_ptr(); } std::string CSVRender::WorldspaceWidget::mapButton (QMouseEvent *event) { std::pair phyiscal ( event->button(), event->modifiers() & Qt::ControlModifier); std::map, std::string>::const_iterator iter = mButtonMapping.find (phyiscal); if (iter!=mButtonMapping.end()) return iter->second; return ""; } void CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped return; if (mime->fromDocument (mDocument)) { if (mime->holdsType (CSMWorld::UniversalId::Type_Cell) || mime->holdsType (CSMWorld::UniversalId::Type_Cell_Missing) || mime->holdsType (CSMWorld::UniversalId::Type_DebugProfile)) { emit dataDropped(mime->getData()); } else dynamic_cast (*mEditMode->getCurrent()).dropEvent (event); } } void CSVRender::WorldspaceWidget::runRequest (const std::string& profile) { mDocument.startRunning (profile, getStartupInstruction()); } void CSVRender::WorldspaceWidget::debugProfileDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (!mRun) return; CSMWorld::IdTable& debugProfiles = dynamic_cast ( *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_DebugProfiles)); int idColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Id); int stateColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Modification); for (int i=topLeft.row(); i<=bottomRight.row(); ++i) { int state = debugProfiles.data (debugProfiles.index (i, stateColumn)).toInt(); // As of version 0.33 this case can not happen because debug profiles exist only in // project or session scope, which means they will never be in deleted state. But we // are adding the code for the sake of completeness and to avoid surprises if debug // profile ever get extended to content scope. if (state==CSMWorld::RecordBase::State_Deleted) mRun->removeProfile (debugProfiles.data ( debugProfiles.index (i, idColumn)).toString().toUtf8().constData()); } } void CSVRender::WorldspaceWidget::debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end) { if (parent.isValid()) return; if (!mRun) return; CSMWorld::IdTable& debugProfiles = dynamic_cast ( *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_DebugProfiles)); int idColumn = debugProfiles.findColumnIndex (CSMWorld::Columns::ColumnId_Id); for (int i=start; i<=end; ++i) { mRun->removeProfile (debugProfiles.data ( debugProfiles.index (i, idColumn)).toString().toUtf8().constData()); } } void CSVRender::WorldspaceWidget::editModeChanged (const std::string& id) { dynamic_cast (*mEditMode->getCurrent()).setEditLock (mLocked); mDragging = false; } void CSVRender::WorldspaceWidget::showToolTip() { if (mShowToolTips) { QPoint pos = QCursor::pos(); if (osg::ref_ptr tag = mousePick (mapFromGlobal (pos))) { bool hideBasics = CSMPrefs::get()["Tooltips"]["scene-hide-basic"].isTrue(); QToolTip::showText (pos, tag->getToolTip (hideBasics), this); } } } void CSVRender::WorldspaceWidget::elementSelectionChanged() { setVisibilityMask (getVisibilityMask()); flagAsModified(); updateOverlay(); } void CSVRender::WorldspaceWidget::updateOverlay() { } void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) { if (!mDragging) { if (mDragMode.empty()) { if (event->globalPos()!=mToolTipPos) { mToolTipPos = event->globalPos(); if (mShowToolTips) mToolTipDelayTimer.start (mToolTipDelay); } } else if (mDragMode=="p-navi" || mDragMode=="s-navi") { } else if (mDragMode=="p-edit" || mDragMode=="s-edit" || mDragMode=="p-select" || mDragMode=="s-select") { osg::ref_ptr tag = mousePick (event->pos()); EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); if (mDragMode=="p-edit") mDragging = editMode.primaryEditStartDrag (tag); else if (mDragMode=="s-edit") mDragging = editMode.secondaryEditStartDrag (tag); else if (mDragMode=="p-select") mDragging = editMode.primarySelectStartDrag (tag); else if (mDragMode=="s-select") mDragging = editMode.secondarySelectStartDrag (tag); if (mDragging) { #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) mDragX = event->localPos().x(); mDragY = height() - event->localPos().y(); #else mDragX = event->posF().x(); mDragY = height() - event->posF().y(); #endif } } } else { int diffX = event->x() - mDragX; int diffY = (height() - event->y()) - mDragY; mDragX = event->x(); mDragY = height() - event->y(); double factor = mDragFactor; if (event->modifiers() & Qt::ShiftModifier) factor *= mDragShiftFactor; EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); editMode.drag (diffX, diffY, factor); } } void CSVRender::WorldspaceWidget::mousePressEvent (QMouseEvent *event) { std::string button = mapButton (event); if (!mDragging) mDragMode = button; } void CSVRender::WorldspaceWidget::mouseReleaseEvent (QMouseEvent *event) { std::string button = mapButton (event); if (mDragging) { if (mDragMode=="p-navi" || mDragMode=="s-navi") { } else if (mDragMode=="p-edit" || mDragMode=="s-edit" || mDragMode=="p-select" || mDragMode=="s-select") { EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); editMode.dragCompleted(); mDragging = false; } } else { if (button=="p-navi" || button=="s-navi") { } else if (button=="p-edit" || button=="s-edit" || button=="p-select" || button=="s-select") { osg::ref_ptr tag = mousePick (event->pos()); handleMouseClick (tag, button, event->modifiers() & Qt::ShiftModifier); } } mDragMode.clear(); } void CSVRender::WorldspaceWidget::mouseDoubleClickEvent (QMouseEvent *event) { if(event->button() == Qt::RightButton) { //mMouse->mouseDoubleClickEvent(event); } } void CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event) { if (mDragging) { double factor = mDragWheelFactor; if (event->modifiers() & Qt::ShiftModifier) factor *= mDragShiftFactor; EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); editMode.dragWheel (event->delta(), factor); } } void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event) { if(event->key() == Qt::Key_Escape) { if (mDragging) { EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); editMode.dragAborted(); mDragging = false; } } else RenderWidget::keyPressEvent(event); } void CSVRender::WorldspaceWidget::handleMouseClick (osg::ref_ptr tag, const std::string& button, bool shift) { EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); if (button=="p-edit") editMode.primaryEditPressed (tag); else if (button=="s-edit") editMode.secondaryEditPressed (tag); else if (button=="p-select") editMode.primarySelectPressed (tag); else if (button=="s-select") editMode.secondarySelectPressed (tag); } openmw-openmw-0.38.0/apps/opencs/view/render/worldspacewidget.hpp000066400000000000000000000152101264522266000251610ustar00rootroot00000000000000#ifndef OPENCS_VIEW_WORLDSPACEWIDGET_H #define OPENCS_VIEW_WORLDSPACEWIDGET_H #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/tablemimedata.hpp" #include "scenewidget.hpp" #include "elements.hpp" namespace CSMPrefs { class Setting; } namespace CSMWorld { class UniversalId; } namespace CSVWidget { class SceneToolMode; class SceneToolToggle2; class SceneToolbar; class SceneToolRun; } namespace CSVRender { class TagBase; class CellArrow; class WorldspaceWidget : public SceneWidget { Q_OBJECT CSVWidget::SceneToolToggle2 *mSceneElements; CSVWidget::SceneToolRun *mRun; CSMDoc::Document& mDocument; unsigned int mInteractionMask; std::map, std::string> mButtonMapping; CSVWidget::SceneToolMode *mEditMode; bool mLocked; std::string mDragMode; bool mDragging; int mDragX; int mDragY; double mDragFactor; double mDragWheelFactor; double mDragShiftFactor; QTimer mToolTipDelayTimer; QPoint mToolTipPos; bool mShowToolTips; int mToolTipDelay; public: enum DropType { Type_CellsInterior, Type_CellsExterior, Type_Other, Type_DebugProfile }; enum dropRequirments { canHandle, needPaged, needUnpaged, ignored //either mixed cells, or not cells }; WorldspaceWidget (CSMDoc::Document& document, QWidget *parent = 0); ~WorldspaceWidget (); CSVWidget::SceneToolMode *makeNavigationSelector (CSVWidget::SceneToolbar *parent); ///< \attention The created tool is not added to the toolbar (via addTool). Doing that /// is the responsibility of the calling function. /// \attention The created tool is not added to the toolbar (via addTool). Doing /// that is the responsibility of the calling function. CSVWidget::SceneToolToggle2 *makeSceneVisibilitySelector ( CSVWidget::SceneToolbar *parent); /// \attention The created tool is not added to the toolbar (via addTool). Doing /// that is the responsibility of the calling function. CSVWidget::SceneToolRun *makeRunTool (CSVWidget::SceneToolbar *parent); /// \attention The created tool is not added to the toolbar (via addTool). Doing /// that is the responsibility of the calling function. CSVWidget::SceneToolMode *makeEditModeSelector (CSVWidget::SceneToolbar *parent); void selectDefaultNavigationMode(); static DropType getDropType(const std::vector& data); virtual dropRequirments getDropRequirements(DropType type) const; virtual void useViewHint (const std::string& hint); ///< Default-implementation: ignored. /// \return Drop handled? virtual bool handleDrop (const std::vector& data, DropType type); virtual unsigned int getVisibilityMask() const; /// \note This function will implicitly add elements that are independent of the /// selected edit mode. virtual void setInteractionMask (unsigned int mask); /// \note This function will only return those elements that are both visible and /// marked for interaction. unsigned int getInteractionMask() const; virtual void setEditLock (bool locked); CSMDoc::Document& getDocument(); /// \param elementMask Elements to be affected by the clear operation virtual void clearSelection (int elementMask) = 0; protected: virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool); virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool); virtual void updateOverlay(); virtual void mouseMoveEvent (QMouseEvent *event); virtual void mousePressEvent (QMouseEvent *event); virtual void mouseReleaseEvent (QMouseEvent *event); virtual void mouseDoubleClickEvent (QMouseEvent *event); virtual void wheelEvent (QWheelEvent *event); virtual void keyPressEvent (QKeyEvent *event); virtual void handleMouseClick (osg::ref_ptr tag, const std::string& button, bool shift); private: void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent* event); void dragMoveEvent(QDragMoveEvent *event); /// \return Is \a key a button mapping setting? (ignored otherwise) bool storeMappingSetting (const CSMPrefs::Setting *setting); osg::ref_ptr mousePick (const QPoint& localPos); std::string mapButton (QMouseEvent *event); virtual std::string getStartupInstruction() = 0; private slots: void settingChanged (const CSMPrefs::Setting *setting); void selectNavigationMode (const std::string& mode); virtual void referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0; virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0; virtual void referenceableAdded (const QModelIndex& index, int start, int end) = 0; virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0; virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0; virtual void referenceAdded (const QModelIndex& index, int start, int end) = 0; virtual void runRequest (const std::string& profile); void debugProfileDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end); void editModeChanged (const std::string& id); void showToolTip(); protected slots: void elementSelectionChanged(); signals: void closeRequest(); void dataDropped(const std::vector& data); friend class MouseState; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/tools/000077500000000000000000000000001264522266000207635ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/view/tools/merge.cpp000066400000000000000000000073571264522266000226020ustar00rootroot00000000000000 #include "merge.hpp" #include #include #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/doc/documentmanager.hpp" #include "../doc/filewidget.hpp" #include "../doc/adjusterwidget.hpp" void CSVTools::Merge::keyPressEvent (QKeyEvent *event) { if (event->key()==Qt::Key_Escape) { event->accept(); cancel(); } else QWidget::keyPressEvent (event); } CSVTools::Merge::Merge (CSMDoc::DocumentManager& documentManager, QWidget *parent) : QWidget (parent), mDocument (0), mDocumentManager (documentManager) { setWindowTitle ("Merge Content Files into a new Game File"); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout (mainLayout); QSplitter *splitter = new QSplitter (Qt::Horizontal, this); mainLayout->addWidget (splitter, 1); // left panel (files to be merged) QWidget *left = new QWidget (this); left->setContentsMargins (0, 0, 0, 0); splitter->addWidget (left); QVBoxLayout *leftLayout = new QVBoxLayout; left->setLayout (leftLayout); leftLayout->addWidget (new QLabel ("Files to be merged", this)); mFiles = new QListWidget (this); leftLayout->addWidget (mFiles, 1); // right panel (new game file) QWidget *right = new QWidget (this); right->setContentsMargins (0, 0, 0, 0); splitter->addWidget (right); QVBoxLayout *rightLayout = new QVBoxLayout; rightLayout->setAlignment (Qt::AlignTop); right->setLayout (rightLayout); rightLayout->addWidget (new QLabel ("New game file", this)); mNewFile = new CSVDoc::FileWidget (this); mNewFile->setType (false); mNewFile->extensionLabelIsVisible (true); rightLayout->addWidget (mNewFile); mAdjuster = new CSVDoc::AdjusterWidget (this); rightLayout->addWidget (mAdjuster); connect (mNewFile, SIGNAL (nameChanged (const QString&, bool)), mAdjuster, SLOT (setName (const QString&, bool))); connect (mAdjuster, SIGNAL (stateChanged (bool)), this, SLOT (stateChanged (bool))); // buttons QDialogButtonBox *buttons = new QDialogButtonBox (QDialogButtonBox::Cancel, Qt::Horizontal, this); connect (buttons->button (QDialogButtonBox::Cancel), SIGNAL (clicked()), this, SLOT (cancel())); mOkay = new QPushButton ("Merge", this); connect (mOkay, SIGNAL (clicked()), this, SLOT (accept())); mOkay->setDefault (true); buttons->addButton (mOkay, QDialogButtonBox::AcceptRole); mainLayout->addWidget (buttons); } void CSVTools::Merge::configure (CSMDoc::Document *document) { mDocument = document; mNewFile->setName (""); // content files while (mFiles->count()) delete mFiles->takeItem (0); std::vector files = document->getContentFiles(); for (std::vector::const_iterator iter (files.begin()); iter!=files.end(); ++iter) mFiles->addItem (QString::fromUtf8 (iter->filename().string().c_str())); } void CSVTools::Merge::setLocalData (const boost::filesystem::path& localData) { mAdjuster->setLocalData (localData); } CSMDoc::Document *CSVTools::Merge::getDocument() const { return mDocument; } void CSVTools::Merge::cancel() { mDocument = 0; hide(); } void CSVTools::Merge::accept() { if ((mDocument->getState() & CSMDoc::State_Merging)==0) { std::vector< boost::filesystem::path > files (1, mAdjuster->getPath()); std::auto_ptr target ( mDocumentManager.makeDocument (files, files[0], true)); mDocument->runMerge (target); hide(); } } void CSVTools::Merge::stateChanged (bool valid) { mOkay->setEnabled (valid); } openmw-openmw-0.38.0/apps/opencs/view/tools/merge.hpp000066400000000000000000000022431264522266000225740ustar00rootroot00000000000000#ifndef CSV_TOOLS_REPORTTABLE_H #define CSV_TOOLS_REPORTTABLE_H #include #include class QPushButton; class QListWidget; namespace CSMDoc { class Document; class DocumentManager; } namespace CSVDoc { class FileWidget; class AdjusterWidget; } namespace CSVTools { class Merge : public QWidget { Q_OBJECT CSMDoc::Document *mDocument; QPushButton *mOkay; QListWidget *mFiles; CSVDoc::FileWidget *mNewFile; CSVDoc::AdjusterWidget *mAdjuster; CSMDoc::DocumentManager& mDocumentManager; void keyPressEvent (QKeyEvent *event); public: Merge (CSMDoc::DocumentManager& documentManager, QWidget *parent = 0); /// Configure dialogue for a new merge void configure (CSMDoc::Document *document); void setLocalData (const boost::filesystem::path& localData); CSMDoc::Document *getDocument() const; public slots: void cancel(); private slots: void accept(); void stateChanged (bool valid); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/tools/reportsubview.cpp000066400000000000000000000023741264522266000244150ustar00rootroot00000000000000#include "reportsubview.hpp" #include "reporttable.hpp" CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : CSVDoc::SubView (id), mDocument (document), mRefreshState (0) { if (id.getType()==CSMWorld::UniversalId::Type_VerificationResults) mRefreshState = CSMDoc::State_Verifying; setWidget (mTable = new ReportTable (document, id, false, mRefreshState, this)); connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)), SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&))); if (mRefreshState==CSMDoc::State_Verifying) { connect (mTable, SIGNAL (refreshRequest()), this, SLOT (refreshRequest())); connect (&document, SIGNAL (stateChanged (int, CSMDoc::Document *)), mTable, SLOT (stateChanged (int, CSMDoc::Document *))); } } void CSVTools::ReportSubView::setEditLock (bool locked) { // ignored. We don't change document state anyway. } void CSVTools::ReportSubView::refreshRequest() { if (!(mDocument.getState() & mRefreshState)) { if (mRefreshState==CSMDoc::State_Verifying) { mTable->clear(); mDocument.verify (getUniversalId()); } } } openmw-openmw-0.38.0/apps/opencs/view/tools/reportsubview.hpp000066400000000000000000000012041264522266000244110ustar00rootroot00000000000000#ifndef CSV_TOOLS_REPORTSUBVIEW_H #define CSV_TOOLS_REPORTSUBVIEW_H #include "../doc/subview.hpp" class QTableView; class QModelIndex; namespace CSMDoc { class Document; } namespace CSVTools { class ReportTable; class ReportSubView : public CSVDoc::SubView { Q_OBJECT ReportTable *mTable; CSMDoc::Document& mDocument; int mRefreshState; public: ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); private slots: void refreshRequest(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/tools/reporttable.cpp000066400000000000000000000230131264522266000240110ustar00rootroot00000000000000#include "reporttable.hpp" #include #include #include #include #include #include #include #include #include #include #include "../../model/tools/reportmodel.hpp" #include "../../model/prefs/state.hpp" #include "../../view/world/idtypedelegate.hpp" namespace CSVTools { class RichTextDelegate : public QStyledItemDelegate { public: RichTextDelegate (QObject *parent = 0); virtual void paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; }; } CSVTools::RichTextDelegate::RichTextDelegate (QObject *parent) : QStyledItemDelegate (parent) {} void CSVTools::RichTextDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QTextDocument document; QVariant value = index.data (Qt::DisplayRole); if (value.isValid() && !value.isNull()) { document.setHtml (value.toString()); painter->translate (option.rect.topLeft()); document.drawContents (painter); painter->translate (-option.rect.topLeft()); } } void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event) { QModelIndexList selectedRows = selectionModel()->selectedRows(); // create context menu QMenu menu (this); if (!selectedRows.empty()) { menu.addAction (mShowAction); menu.addAction (mRemoveAction); bool found = false; for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) { QString hint = mProxyModel->data (mProxyModel->index (iter->row(), 2)).toString(); if (!hint.isEmpty() && hint[0]=='R') { found = true; break; } } if (found) menu.addAction (mReplaceAction); } if (mRefreshAction) menu.addAction (mRefreshAction); menu.exec (event->globalPos()); } void CSVTools::ReportTable::mouseMoveEvent (QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) startDragFromTable (*this); } void CSVTools::ReportTable::mouseDoubleClickEvent (QMouseEvent *event) { Qt::KeyboardModifiers modifiers = event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier); QModelIndex index = currentIndex(); selectionModel()->select (index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Rows); std::map::iterator iter = mDoubleClickActions.find (modifiers); if (iter==mDoubleClickActions.end()) { event->accept(); return; } switch (iter->second) { case Action_None: event->accept(); break; case Action_Edit: event->accept(); showSelection(); break; case Action_Remove: event->accept(); removeSelection(); break; case Action_EditAndRemove: event->accept(); showSelection(); removeSelection(); break; } } CSVTools::ReportTable::ReportTable (CSMDoc::Document& document, const CSMWorld::UniversalId& id, bool richTextDescription, int refreshState, QWidget *parent) : CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id)), mRefreshAction (0), mRefreshState (refreshState) { #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); #else horizontalHeader()->setResizeMode (QHeaderView::Interactive); #endif horizontalHeader()->setStretchLastSection (true); verticalHeader()->hide(); setSortingEnabled (true); setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); mProxyModel = new QSortFilterProxyModel (this); mProxyModel->setSourceModel (mModel); setModel (mProxyModel); setColumnHidden (2, true); mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0, mDocument, this); setItemDelegateForColumn (0, mIdTypeDelegate); if (richTextDescription) setItemDelegateForColumn (mModel->columnCount()-1, new RichTextDelegate (this)); mShowAction = new QAction (tr ("Show"), this); connect (mShowAction, SIGNAL (triggered()), this, SLOT (showSelection())); addAction (mShowAction); mRemoveAction = new QAction (tr ("Remove from list"), this); connect (mRemoveAction, SIGNAL (triggered()), this, SLOT (removeSelection())); addAction (mRemoveAction); mReplaceAction = new QAction (tr ("Replace"), this); connect (mReplaceAction, SIGNAL (triggered()), this, SIGNAL (replaceRequest())); addAction (mReplaceAction); if (mRefreshState) { mRefreshAction = new QAction (tr ("Refresh"), this); mRefreshAction->setEnabled (!(mDocument.getState() & mRefreshState)); connect (mRefreshAction, SIGNAL (triggered()), this, SIGNAL (refreshRequest())); addAction (mRefreshAction); } mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_Edit)); mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_Remove)); mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_EditAndRemove)); connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), this, SLOT (settingChanged (const CSMPrefs::Setting *))); CSMPrefs::get()["Reports"].update(); } std::vector CSVTools::ReportTable::getDraggedRecords() const { std::vector ids; QModelIndexList selectedRows = selectionModel()->selectedRows(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) { ids.push_back (mModel->getUniversalId (mProxyModel->mapToSource (*iter).row())); } return ids; } std::vector CSVTools::ReportTable::getReplaceIndices (bool selection) const { std::vector indices; if (selection) { QModelIndexList selectedRows = selectionModel()->selectedRows(); std::vector rows; for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) { rows.push_back (mProxyModel->mapToSource (*iter).row()); } std::sort (rows.begin(), rows.end()); for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) { QString hint = mModel->data (mModel->index (*iter, 2)).toString(); if (!hint.isEmpty() && hint[0]=='R') indices.push_back (*iter); } } else { for (int i=0; irowCount(); ++i) { QString hint = mModel->data (mModel->index (i, 2)).toString(); if (!hint.isEmpty() && hint[0]=='R') indices.push_back (i); } } return indices; } void CSVTools::ReportTable::flagAsReplaced (int index) { mModel->flagAsReplaced (index); } void CSVTools::ReportTable::settingChanged (const CSMPrefs::Setting *setting) { if (setting->getParent()->getKey()=="Reports") { QString base ("double"); QString key = setting->getKey().c_str(); if (key.startsWith (base)) { QString modifierString = key.mid (base.size()); Qt::KeyboardModifiers modifiers = 0; if (modifierString=="-s") modifiers = Qt::ShiftModifier; else if (modifierString=="-c") modifiers = Qt::ControlModifier; else if (modifierString=="-sc") modifiers = Qt::ShiftModifier | Qt::ControlModifier; DoubleClickAction action = Action_None; std::string value = setting->toString(); if (value=="Edit") action = Action_Edit; else if (value=="Remove") action = Action_Remove; else if (value=="Edit And Remove") action = Action_EditAndRemove; mDoubleClickActions[modifiers] = action; return; } } else if (*setting=="Records/type-format") mIdTypeDelegate->settingChanged (setting); } void CSVTools::ReportTable::showSelection() { QModelIndexList selectedRows = selectionModel()->selectedRows(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) { int row = mProxyModel->mapToSource (*iter).row(); emit editRequest (mModel->getUniversalId (row), mModel->getHint (row)); } } void CSVTools::ReportTable::removeSelection() { QModelIndexList selectedRows = selectionModel()->selectedRows(); std::vector rows; for (QModelIndexList::iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) { rows.push_back (mProxyModel->mapToSource (*iter).row()); } std::sort (rows.begin(), rows.end()); for (std::vector::const_reverse_iterator iter (rows.rbegin()); iter!=rows.rend(); ++iter) mProxyModel->removeRows (*iter, 1); selectionModel()->clear(); } void CSVTools::ReportTable::clear() { mModel->clear(); } void CSVTools::ReportTable::stateChanged (int state, CSMDoc::Document *document) { if (mRefreshAction) mRefreshAction->setEnabled (!(state & mRefreshState)); } openmw-openmw-0.38.0/apps/opencs/view/tools/reporttable.hpp000066400000000000000000000051741264522266000240260ustar00rootroot00000000000000#ifndef CSV_TOOLS_REPORTTABLE_H #define CSV_TOOLS_REPORTTABLE_H #include #include "../world/dragrecordtable.hpp" class QAction; class QSortFilterProxyModel; namespace CSMTools { class ReportModel; } namespace CSMPrefs { class Setting; } namespace CSVWorld { class CommandDelegate; } namespace CSVTools { class ReportTable : public CSVWorld::DragRecordTable { Q_OBJECT enum DoubleClickAction { Action_None, Action_Edit, Action_Remove, Action_EditAndRemove }; QSortFilterProxyModel *mProxyModel; CSMTools::ReportModel *mModel; CSVWorld::CommandDelegate *mIdTypeDelegate; QAction *mShowAction; QAction *mRemoveAction; QAction *mReplaceAction; QAction *mRefreshAction; std::map mDoubleClickActions; int mRefreshState; private: void contextMenuEvent (QContextMenuEvent *event); void mouseMoveEvent (QMouseEvent *event); virtual void mouseDoubleClickEvent (QMouseEvent *event); public: /// \param richTextDescription Use rich text in the description column. /// \param refreshState Document state to check for refresh function. If value is /// 0 no refresh function exists. If the document current has the specified state /// the refresh function is disabled. ReportTable (CSMDoc::Document& document, const CSMWorld::UniversalId& id, bool richTextDescription, int refreshState = 0, QWidget *parent = 0); virtual std::vector getDraggedRecords() const; void clear(); /// Return indices of rows that are suitable for replacement. /// /// \param selection Only list selected rows. /// /// \return rows in the original model std::vector getReplaceIndices (bool selection) const; /// \param index row in the original model void flagAsReplaced (int index); private slots: void settingChanged (const CSMPrefs::Setting *setting); void showSelection(); void removeSelection(); public slots: void stateChanged (int state, CSMDoc::Document *document); signals: void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void replaceRequest(); void refreshRequest(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/tools/searchbox.cpp000066400000000000000000000115661264522266000234560ustar00rootroot00000000000000#include "searchbox.hpp" #include #include #include #include #include "../../model/world/columns.hpp" #include "../../model/tools/search.hpp" void CSVTools::SearchBox::updateSearchButton() { if (!mSearchEnabled) mSearch.setEnabled (false); else { switch (mMode.currentIndex()) { case 0: case 1: case 2: case 3: mSearch.setEnabled (!mText.text().isEmpty()); break; case 4: mSearch.setEnabled (true); break; } } } CSVTools::SearchBox::SearchBox (QWidget *parent) : QWidget (parent), mSearch ("Search"), mSearchEnabled (false), mReplace ("Replace All") { mLayout = new QGridLayout (this); // search panel std::vector states = CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification); states.resize (states.size()-1); // ignore erased state for (std::vector::const_iterator iter (states.begin()); iter!=states.end(); ++iter) mRecordState.addItem (QString::fromUtf8 (iter->c_str())); mMode.addItem ("Text"); mMode.addItem ("Text (RegEx)"); mMode.addItem ("ID"); mMode.addItem ("ID (RegEx)"); mMode.addItem ("Record State"); mLayout->addWidget (&mMode, 0, 0); mLayout->addWidget (&mSearch, 0, 3); mInput.insertWidget (0, &mText); mInput.insertWidget (1, &mRecordState); mLayout->addWidget (&mInput, 0, 1); connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int))); connect (&mText, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); connect (&mSearch, SIGNAL (clicked (bool)), this, SLOT (startSearch (bool))); connect (&mText, SIGNAL (returnPressed()), this, SLOT (startSearch())); // replace panel mReplaceInput.insertWidget (0, &mReplaceText); mReplaceInput.insertWidget (1, &mReplacePlaceholder); mLayout->addWidget (&mReplaceInput, 1, 1); mLayout->addWidget (&mReplace, 1, 3); // layout adjustments mLayout->setColumnMinimumWidth (2, 50); mLayout->setColumnStretch (1, 1); mLayout->setContentsMargins (0, 0, 0, 0); connect (&mReplace, (SIGNAL (clicked (bool))), this, SLOT (replaceAll (bool))); // update modeSelected (0); updateSearchButton(); } void CSVTools::SearchBox::setSearchMode (bool enabled) { mSearchEnabled = enabled; updateSearchButton(); } CSMTools::Search CSVTools::SearchBox::getSearch() const { CSMTools::Search::Type type = static_cast (mMode.currentIndex()); switch (type) { case CSMTools::Search::Type_Text: case CSMTools::Search::Type_Id: return CSMTools::Search (type, std::string (mText.text().toUtf8().data())); case CSMTools::Search::Type_TextRegEx: case CSMTools::Search::Type_IdRegEx: return CSMTools::Search (type, QRegExp (mText.text().toUtf8().data(), Qt::CaseInsensitive)); case CSMTools::Search::Type_RecordState: return CSMTools::Search (type, mRecordState.currentIndex()); case CSMTools::Search::Type_None: break; } throw std::logic_error ("invalid search mode index"); } std::string CSVTools::SearchBox::getReplaceText() const { CSMTools::Search::Type type = static_cast (mMode.currentIndex()); switch (type) { case CSMTools::Search::Type_Text: case CSMTools::Search::Type_TextRegEx: case CSMTools::Search::Type_Id: case CSMTools::Search::Type_IdRegEx: return mReplaceText.text().toUtf8().data(); default: throw std::logic_error ("Invalid search mode for replace"); } } void CSVTools::SearchBox::setEditLock (bool locked) { mReplace.setEnabled (!locked); } void CSVTools::SearchBox::focus() { mInput.currentWidget()->setFocus(); } void CSVTools::SearchBox::modeSelected (int index) { switch (index) { case CSMTools::Search::Type_Text: case CSMTools::Search::Type_TextRegEx: case CSMTools::Search::Type_Id: case CSMTools::Search::Type_IdRegEx: mInput.setCurrentIndex (0); mReplaceInput.setCurrentIndex (0); break; case CSMTools::Search::Type_RecordState: mInput.setCurrentIndex (1); mReplaceInput.setCurrentIndex (1); break; } mInput.currentWidget()->setFocus(); updateSearchButton(); } void CSVTools::SearchBox::textChanged (const QString& text) { updateSearchButton(); } void CSVTools::SearchBox::startSearch (bool checked) { if (mSearch.isEnabled()) emit startSearch (getSearch()); } void CSVTools::SearchBox::replaceAll (bool checked) { emit replaceAll(); } openmw-openmw-0.38.0/apps/opencs/view/tools/searchbox.hpp000066400000000000000000000026141264522266000234550ustar00rootroot00000000000000#ifndef CSV_TOOLS_SEARCHBOX_H #define CSV_TOOLS_SEARCHBOX_H #include #include #include #include #include #include class QGridLayout; namespace CSMTools { class Search; } namespace CSVTools { class SearchBox : public QWidget { Q_OBJECT QStackedWidget mInput; QLineEdit mText; QComboBox mRecordState; QPushButton mSearch; QGridLayout *mLayout; QComboBox mMode; bool mSearchEnabled; QStackedWidget mReplaceInput; QLineEdit mReplaceText; QLabel mReplacePlaceholder; QPushButton mReplace; private: void updateSearchButton(); public: SearchBox (QWidget *parent = 0); void setSearchMode (bool enabled); CSMTools::Search getSearch() const; std::string getReplaceText() const; void setEditLock (bool locked); void focus(); private slots: void modeSelected (int index); void textChanged (const QString& text); void startSearch (bool checked = true); void replaceAll (bool checked); signals: void startSearch (const CSMTools::Search& search); void replaceAll(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/tools/searchsubview.cpp000066400000000000000000000073031264522266000243440ustar00rootroot00000000000000#include "searchsubview.hpp" #include #include "../../model/doc/document.hpp" #include "../../model/tools/search.hpp" #include "../../model/tools/reportmodel.hpp" #include "../../model/world/idtablebase.hpp" #include "../../model/prefs/state.hpp" #include "reporttable.hpp" #include "searchbox.hpp" void CSVTools::SearchSubView::replace (bool selection) { if (mLocked) return; std::vector indices = mTable->getReplaceIndices (selection); std::string replace = mSearchBox.getReplaceText(); const CSMTools::ReportModel& model = dynamic_cast (*mTable->model()); bool autoDelete = CSMPrefs::get()["Search & Replace"]["auto-delete"].isTrue(); CSMTools::Search search (mSearch); CSMWorld::IdTableBase *currentTable = 0; // We are running through the indices in reverse order to avoid messing up multiple results // in a single string. for (std::vector::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter) { CSMWorld::UniversalId id = model.getUniversalId (*iter); CSMWorld::UniversalId::Type type = CSMWorld::UniversalId::getParentType (id.getType()); CSMWorld::IdTableBase *table = &dynamic_cast ( *mDocument.getData().getTableModel (type)); if (table!=currentTable) { search.configure (table); currentTable = table; } std::string hint = model.getHint (*iter); if (search.verify (mDocument, table, id, hint)) { search.replace (mDocument, table, id, hint, replace); mTable->flagAsReplaced (*iter); if (autoDelete) mTable->model()->removeRows (*iter, 1); } } } void CSVTools::SearchSubView::showEvent (QShowEvent *event) { CSVDoc::SubView::showEvent (event); mSearchBox.focus(); } CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : CSVDoc::SubView (id), mDocument (document), mLocked (false) { QVBoxLayout *layout = new QVBoxLayout; layout->addWidget (&mSearchBox); layout->addWidget (mTable = new ReportTable (document, id, true), 2); QWidget *widget = new QWidget; widget->setLayout (layout); setWidget (widget); stateChanged (document.getState(), &document); connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)), SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&))); connect (mTable, SIGNAL (replaceRequest()), this, SLOT (replaceRequest())); connect (&document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (stateChanged (int, CSMDoc::Document *))); connect (&mSearchBox, SIGNAL (startSearch (const CSMTools::Search&)), this, SLOT (startSearch (const CSMTools::Search&))); connect (&mSearchBox, SIGNAL (replaceAll()), this, SLOT (replaceAllRequest())); } void CSVTools::SearchSubView::setEditLock (bool locked) { mLocked = locked; mSearchBox.setEditLock (locked); } void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *document) { mSearchBox.setSearchMode (!(state & CSMDoc::State_Searching)); } void CSVTools::SearchSubView::startSearch (const CSMTools::Search& search) { CSMPrefs::Category& settings = CSMPrefs::get()["Search & Replace"]; mSearch = search; mSearch.setPadding (settings["char-before"].toInt(), settings["char-after"].toInt()); mTable->clear(); mDocument.runSearch (getUniversalId(), mSearch); } void CSVTools::SearchSubView::replaceRequest() { replace (true); } void CSVTools::SearchSubView::replaceAllRequest() { replace (false); } openmw-openmw-0.38.0/apps/opencs/view/tools/searchsubview.hpp000066400000000000000000000020741264522266000243510ustar00rootroot00000000000000#ifndef CSV_TOOLS_SEARCHSUBVIEW_H #define CSV_TOOLS_SEARCHSUBVIEW_H #include "../../model/tools/search.hpp" #include "../doc/subview.hpp" #include "searchbox.hpp" class QTableView; class QModelIndex; namespace CSMDoc { class Document; } namespace CSVTools { class ReportTable; class SearchSubView : public CSVDoc::SubView { Q_OBJECT ReportTable *mTable; SearchBox mSearchBox; CSMDoc::Document& mDocument; CSMTools::Search mSearch; bool mLocked; private: void replace (bool selection); protected: void showEvent (QShowEvent *event); public: SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); private slots: void stateChanged (int state, CSMDoc::Document *document); void startSearch (const CSMTools::Search& search); void replaceRequest(); void replaceAllRequest(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/tools/subviews.cpp000066400000000000000000000010331264522266000233330ustar00rootroot00000000000000#include "subviews.hpp" #include "../doc/subviewfactoryimp.hpp" #include "reportsubview.hpp" #include "searchsubview.hpp" void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { manager.add (CSMWorld::UniversalId::Type_VerificationResults, new CSVDoc::SubViewFactory); manager.add (CSMWorld::UniversalId::Type_LoadErrorLog, new CSVDoc::SubViewFactory); manager.add (CSMWorld::UniversalId::Type_Search, new CSVDoc::SubViewFactory); } openmw-openmw-0.38.0/apps/opencs/view/tools/subviews.hpp000066400000000000000000000003301264522266000233370ustar00rootroot00000000000000#ifndef CSV_TOOLS_SUBVIEWS_H #define CSV_TOOLS_SUBVIEWS_H namespace CSVDoc { class SubViewFactoryManager; } namespace CSVTools { void addSubViewFactories (CSVDoc::SubViewFactoryManager& manager); } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/000077500000000000000000000000001264522266000211065ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/view/widget/coloreditor.cpp000066400000000000000000000060751264522266000241470ustar00rootroot00000000000000#include "coloreditor.hpp" #include #include #include #include #include #include #include #include "colorpickerpopup.hpp" CSVWidget::ColorEditor::ColorEditor(const QColor &color, QWidget *parent, bool popupOnStart) : QPushButton(parent), mColor(color), mColorPicker(new ColorPickerPopup(this)), mPopupOnStart(popupOnStart) { setCheckable(true); connect(this, SIGNAL(clicked()), this, SLOT(showPicker())); connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid())); connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &))); } void CSVWidget::ColorEditor::paintEvent(QPaintEvent *event) { QPushButton::paintEvent(event); QRect buttonRect = rect(); QRect coloredRect(buttonRect.x() + qRound(buttonRect.width() / 4.0), buttonRect.y() + qRound(buttonRect.height() / 4.0), buttonRect.width() / 2, buttonRect.height() / 2); QPainter painter(this); painter.fillRect(coloredRect, mColor); painter.setPen(Qt::black); painter.drawRect(coloredRect); } void CSVWidget::ColorEditor::showEvent(QShowEvent *event) { QPushButton::showEvent(event); if (isVisible() && mPopupOnStart) { setChecked(true); showPicker(); mPopupOnStart = false; } } QColor CSVWidget::ColorEditor::color() const { return mColor; } void CSVWidget::ColorEditor::setColor(const QColor &color) { mColor = color; update(); } void CSVWidget::ColorEditor::showPicker() { if (isChecked()) { mColorPicker->showPicker(calculatePopupPosition(), mColor); } else { mColorPicker->hide(); } } void CSVWidget::ColorEditor::pickerHid() { setChecked(false); emit pickingFinished(); } void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color) { mColor = color; update(); } QPoint CSVWidget::ColorEditor::calculatePopupPosition() { QRect editorGeometry = geometry(); QRect popupGeometry = mColorPicker->geometry(); QRect screenGeometry = QApplication::desktop()->screenGeometry(); // Center the popup horizontally relative to the editor int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2; // Popup position need to be specified in global coords QPoint popupPosition = mapToGlobal(QPoint(localPopupX, editorGeometry.height())); // Make sure that the popup isn't out of the screen if (popupPosition.x() < screenGeometry.left()) { popupPosition.setX(screenGeometry.left() + 1); } else if (popupPosition.x() + popupGeometry.width() > screenGeometry.right()) { popupPosition.setX(screenGeometry.right() - popupGeometry.width() - 1); } if (popupPosition.y() + popupGeometry.height() > screenGeometry.bottom()) { // Place the popup above the editor popupPosition.setY(popupPosition.y() - popupGeometry.height() - editorGeometry.height() - 1); } return popupPosition; } openmw-openmw-0.38.0/apps/opencs/view/widget/coloreditor.hpp000066400000000000000000000016641264522266000241530ustar00rootroot00000000000000#ifndef CSV_WIDGET_COLOREDITOR_HPP #define CSV_WIDGET_COLOREDITOR_HPP #include class QColor; class QPoint; class QSize; namespace CSVWidget { class ColorPickerPopup; class ColorEditor : public QPushButton { Q_OBJECT QColor mColor; ColorPickerPopup *mColorPicker; bool mPopupOnStart; QPoint calculatePopupPosition(); public: ColorEditor(const QColor &color, QWidget *parent = 0, bool popupOnStart = false); QColor color() const; void setColor(const QColor &color); protected: virtual void paintEvent(QPaintEvent *event); virtual void showEvent(QShowEvent *event); private slots: void showPicker(); void pickerHid(); void pickerColorChanged(const QColor &color); signals: void pickingFinished(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/colorpickerpopup.cpp000066400000000000000000000050351264522266000252150ustar00rootroot00000000000000#include "colorpickerpopup.hpp" #include #include #include #include #include #include #include CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent) : QFrame(parent) { setWindowFlags(Qt::Popup); setFrameStyle(QFrame::Box | QFrame::Plain); hide(); mColorPicker = new QColorDialog(this); mColorPicker->setWindowFlags(Qt::Widget); mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog); mColorPicker->installEventFilter(this); mColorPicker->open(); connect(mColorPicker, SIGNAL(currentColorChanged(const QColor &)), this, SIGNAL(colorChanged(const QColor &))); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(mColorPicker); layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); layout->setContentsMargins(0, 0, 0, 0); setLayout(layout); setFixedSize(mColorPicker->size()); } void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColor &initialColor) { QRect geometry = this->geometry(); geometry.moveTo(position); setGeometry(geometry); mColorPicker->setCurrentColor(initialColor); show(); } void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) { QPushButton *button = qobject_cast(parentWidget()); if (button != NULL) { QStyleOptionButton option; option.init(button); QRect buttonRect = option.rect; buttonRect.moveTo(button->mapToGlobal(buttonRect.topLeft())); // If the mouse is pressed above the pop-up parent, // the pop-up will be hidden and the pressed signal won't be repeated for the parent if (buttonRect.contains(event->globalPos()) || buttonRect.contains(event->pos())) { setAttribute(Qt::WA_NoMouseReplay); } } QFrame::mousePressEvent(event); } void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event) { QFrame::hideEvent(event); emit hid(); } bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event) { if (object == mColorPicker && event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); // Prevent QColorDialog from closing when Escape is pressed. // Instead, hide the popup. if (keyEvent->key() == Qt::Key_Escape) { hide(); return true; } } return QFrame::eventFilter(object, event); } openmw-openmw-0.38.0/apps/opencs/view/widget/colorpickerpopup.hpp000066400000000000000000000012511264522266000252160ustar00rootroot00000000000000#ifndef CSVWIDGET_COLORPICKERPOPUP_HPP #define CSVWIDGET_COLORPICKERPOPUP_HPP #include class QColorDialog; namespace CSVWidget { class ColorPickerPopup : public QFrame { Q_OBJECT QColorDialog *mColorPicker; public: explicit ColorPickerPopup(QWidget *parent); void showPicker(const QPoint &position, const QColor &initialColor); protected: virtual void mousePressEvent(QMouseEvent *event); virtual void hideEvent(QHideEvent *event); virtual bool eventFilter(QObject *object, QEvent *event); signals: void hid(); void colorChanged(const QColor &color); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/completerpopup.cpp000066400000000000000000000014311264522266000246670ustar00rootroot00000000000000#include "completerpopup.hpp" CSVWidget::CompleterPopup::CompleterPopup(QWidget *parent) : QListView(parent) { setEditTriggers(QAbstractItemView::NoEditTriggers); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setSelectionBehavior(QAbstractItemView::SelectRows); setSelectionMode(QAbstractItemView::SingleSelection); } int CSVWidget::CompleterPopup::sizeHintForRow(int row) const { if (model() == NULL) { return -1; } if (row < 0 || row >= model()->rowCount()) { return -1; } ensurePolished(); QModelIndex index = model()->index(row, modelColumn()); QStyleOptionViewItem option = viewOptions(); QAbstractItemDelegate *delegate = itemDelegate(index); return delegate->sizeHint(option, index).height(); } openmw-openmw-0.38.0/apps/opencs/view/widget/completerpopup.hpp000066400000000000000000000004651264522266000247020ustar00rootroot00000000000000#ifndef CSV_WIDGET_COMPLETERPOPUP_HPP #define CSV_WIDGET_COMPLETERPOPUP_HPP #include namespace CSVWidget { class CompleterPopup : public QListView { public: CompleterPopup(QWidget *parent = 0); virtual int sizeHintForRow(int row) const; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/droplineedit.cpp000066400000000000000000000021431264522266000242740ustar00rootroot00000000000000#include "droplineedit.hpp" #include #include "../../model/world/tablemimedata.hpp" #include "../../model/world/universalid.hpp" #include "../world/dragdroputils.hpp" CSVWidget::DropLineEdit::DropLineEdit(CSMWorld::ColumnBase::Display type, QWidget *parent) : QLineEdit(parent), mDropType(type) { setAcceptDrops(true); } void CSVWidget::DropLineEdit::dragEnterEvent(QDragEnterEvent *event) { if (CSVWorld::DragDropUtils::canAcceptData(*event, mDropType)) { event->acceptProposedAction(); } } void CSVWidget::DropLineEdit::dragMoveEvent(QDragMoveEvent *event) { if (CSVWorld::DragDropUtils::canAcceptData(*event, mDropType)) { event->accept(); } } void CSVWidget::DropLineEdit::dropEvent(QDropEvent *event) { if (CSVWorld::DragDropUtils::canAcceptData(*event, mDropType)) { CSMWorld::UniversalId id = CSVWorld::DragDropUtils::getAcceptedData(*event, mDropType); setText(QString::fromUtf8(id.getId().c_str())); emit tableMimeDataDropped(id, CSVWorld::DragDropUtils::getTableMimeData(*event)->getDocumentPtr()); } } openmw-openmw-0.38.0/apps/opencs/view/widget/droplineedit.hpp000066400000000000000000000015611264522266000243040ustar00rootroot00000000000000#ifndef CSV_WIDGET_DROPLINEEDIT_HPP #define CSV_WIDGET_DROPLINEEDIT_HPP #include #include "../../model/world/columnbase.hpp" namespace CSMDoc { class Document; } namespace CSMWorld { class TableMimeData; class UniversalId; } namespace CSVWidget { class DropLineEdit : public QLineEdit { Q_OBJECT CSMWorld::ColumnBase::Display mDropType; ///< The accepted Display type for this LineEdit. public: DropLineEdit(CSMWorld::ColumnBase::Display type, QWidget *parent = 0); protected: void dragEnterEvent(QDragEnterEvent *event); void dragMoveEvent(QDragMoveEvent *event); void dropEvent(QDropEvent *event); signals: void tableMimeDataDropped(const CSMWorld::UniversalId &id, const CSMDoc::Document *document); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/modebutton.cpp000066400000000000000000000004611264522266000237730ustar00rootroot00000000000000#include "modebutton.hpp" CSVWidget::ModeButton::ModeButton (const QIcon& icon, const QString& tooltip, QWidget *parent) : PushButton (icon, Type_Mode, tooltip, parent) {} void CSVWidget::ModeButton::activate (SceneToolbar *toolbar) {} void CSVWidget::ModeButton::deactivate (SceneToolbar *toolbar) {} openmw-openmw-0.38.0/apps/opencs/view/widget/modebutton.hpp000066400000000000000000000012131264522266000237740ustar00rootroot00000000000000#ifndef CSV_WIDGET_MODEBUTTON_H #define CSV_WIDGET_MODEBUTTON_H #include "pushbutton.hpp" namespace CSVWidget { class SceneToolbar; /// \brief Specialist PushButton of Type_Mode for use in SceneToolMode class ModeButton : public PushButton { Q_OBJECT public: ModeButton (const QIcon& icon, const QString& tooltip = "", QWidget *parent = 0); /// Default-Implementation: do nothing virtual void activate (SceneToolbar *toolbar); /// Default-Implementation: do nothing virtual void deactivate (SceneToolbar *toolbar); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/pushbutton.cpp000066400000000000000000000050531264522266000240300ustar00rootroot00000000000000#include "pushbutton.hpp" #include #include void CSVWidget::PushButton::setExtendedToolTip() { QString tooltip = mToolTip; if (tooltip.isEmpty()) tooltip = "(Tool tip not implemented yet)"; switch (mType) { case Type_TopMode: tooltip += "

(left click to change mode)"; break; case Type_TopAction: break; case Type_Mode: tooltip += "

(left click to activate," "
shift-left click to activate and keep panel open)"; break; case Type_Toggle: tooltip += "

(left click to "; tooltip += isChecked() ? "disable" : "enable"; tooltip += "

shift-left click to "; tooltip += isChecked() ? "disable" : "enable"; tooltip += " and keep panel open)"; break; } setToolTip (tooltip); } void CSVWidget::PushButton::keyPressEvent (QKeyEvent *event) { if (event->key()!=Qt::Key_Shift) mKeepOpen = false; QPushButton::keyPressEvent (event); } void CSVWidget::PushButton::keyReleaseEvent (QKeyEvent *event) { if (event->key()==Qt::Key_Space) mKeepOpen = event->modifiers() & Qt::ShiftModifier; QPushButton::keyReleaseEvent (event); } void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event) { mKeepOpen = event->button()==Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier); QPushButton::mouseReleaseEvent (event); } CSVWidget::PushButton::PushButton (const QIcon& icon, Type type, const QString& tooltip, QWidget *parent) : QPushButton (icon, "", parent), mKeepOpen (false), mType (type), mToolTip (tooltip) { if (type==Type_Mode || type==Type_Toggle) { setCheckable (true); connect (this, SIGNAL (toggled (bool)), this, SLOT (checkedStateChanged (bool))); } setCheckable (type==Type_Mode || type==Type_Toggle); setExtendedToolTip(); } CSVWidget::PushButton::PushButton (Type type, const QString& tooltip, QWidget *parent) : QPushButton (parent), mKeepOpen (false), mType (type), mToolTip (tooltip) { setCheckable (type==Type_Mode || type==Type_Toggle); setExtendedToolTip(); } bool CSVWidget::PushButton::hasKeepOpen() const { return mKeepOpen; } QString CSVWidget::PushButton::getBaseToolTip() const { return mToolTip; } CSVWidget::PushButton::Type CSVWidget::PushButton::getType() const { return mType; } void CSVWidget::PushButton::checkedStateChanged (bool checked) { setExtendedToolTip(); } openmw-openmw-0.38.0/apps/opencs/view/widget/pushbutton.hpp000066400000000000000000000027421264522266000240370ustar00rootroot00000000000000#ifndef CSV_WIDGET_PUSHBUTTON_H #define CSV_WIDGET_PUSHBUTTON_H #include namespace CSVWidget { class PushButton : public QPushButton { Q_OBJECT public: enum Type { Type_TopMode, // top level button for mode selector panel Type_TopAction, // top level button that triggers an action Type_Mode, // mode button Type_Toggle }; private: bool mKeepOpen; Type mType; QString mToolTip; private: void setExtendedToolTip(); protected: virtual void keyPressEvent (QKeyEvent *event); virtual void keyReleaseEvent (QKeyEvent *event); virtual void mouseReleaseEvent (QMouseEvent *event); public: /// \param push Do not maintain a toggle state PushButton (const QIcon& icon, Type type, const QString& tooltip = "", QWidget *parent = 0); /// \param push Do not maintain a toggle state PushButton (Type type, const QString& tooltip = "", QWidget *parent = 0); bool hasKeepOpen() const; /// Return tooltip used at construction (without any button-specific modifications) QString getBaseToolTip() const; Type getType() const; private slots: void checkedStateChanged (bool checked); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/scenetool.cpp000066400000000000000000000016411264522266000236070ustar00rootroot00000000000000#include "scenetool.hpp" #include #include "scenetoolbar.hpp" CSVWidget::SceneTool::SceneTool (SceneToolbar *parent, Type type) : PushButton (type, "", parent) { setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); setIconSize (QSize (parent->getIconSize(), parent->getIconSize())); setFixedSize (parent->getButtonSize(), parent->getButtonSize()); connect (this, SIGNAL (clicked()), this, SLOT (openRequest())); } void CSVWidget::SceneTool::activate() {} void CSVWidget::SceneTool::mouseReleaseEvent (QMouseEvent *event) { if (getType()==Type_TopAction && event->button()==Qt::RightButton) showPanel (parentWidget()->mapToGlobal (pos())); else PushButton::mouseReleaseEvent (event); } void CSVWidget::SceneTool::openRequest() { if (getType()==Type_TopAction) activate(); else showPanel (parentWidget()->mapToGlobal (pos())); } openmw-openmw-0.38.0/apps/opencs/view/widget/scenetool.hpp000066400000000000000000000013161264522266000236130ustar00rootroot00000000000000#ifndef CSV_WIDGET_SCENETOOL_H #define CSV_WIDGET_SCENETOOL_H #include "pushbutton.hpp" namespace CSVWidget { class SceneToolbar; ///< \brief Tool base class class SceneTool : public PushButton { Q_OBJECT public: SceneTool (SceneToolbar *parent, Type type = Type_TopMode); virtual void showPanel (const QPoint& position) = 0; /// This function will only called for buttons of type Type_TopAction. The default /// implementation is empty. virtual void activate(); protected: void mouseReleaseEvent (QMouseEvent *event); private slots: void openRequest(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/scenetoolbar.cpp000066400000000000000000000026271264522266000243010ustar00rootroot00000000000000#include "scenetoolbar.hpp" #include #include #include "scenetool.hpp" void CSVWidget::SceneToolbar::focusInEvent (QFocusEvent *event) { QWidget::focusInEvent (event); if (mLayout->count()) dynamic_cast (*mLayout->itemAt (0)).widget()->setFocus(); } CSVWidget::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent) : QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6) { setFixedWidth (mButtonSize); mLayout = new QVBoxLayout (this); mLayout->setAlignment (Qt::AlignTop); mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); setLayout (mLayout); /// \todo make shortcut configurable QShortcut *focusScene = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut); connect (focusScene, SIGNAL (activated()), this, SIGNAL (focusSceneRequest())); } void CSVWidget::SceneToolbar::addTool (SceneTool *tool, SceneTool *insertPoint) { if (!insertPoint) mLayout->addWidget (tool, 0, Qt::AlignTop); else { int index = mLayout->indexOf (insertPoint); mLayout->insertWidget (index+1, tool, 0, Qt::AlignTop); } } void CSVWidget::SceneToolbar::removeTool (SceneTool *tool) { mLayout->removeWidget (tool); } int CSVWidget::SceneToolbar::getButtonSize() const { return mButtonSize; } int CSVWidget::SceneToolbar::getIconSize() const { return mIconSize; } openmw-openmw-0.38.0/apps/opencs/view/widget/scenetoolbar.hpp000066400000000000000000000015661264522266000243070ustar00rootroot00000000000000#ifndef CSV_WIDGET_SCENETOOLBAR_H #define CSV_WIDGET_SCENETOOLBAR_H #include class QVBoxLayout; namespace CSVWidget { class SceneTool; class SceneToolbar : public QWidget { Q_OBJECT QVBoxLayout *mLayout; int mButtonSize; int mIconSize; protected: virtual void focusInEvent (QFocusEvent *event); public: SceneToolbar (int buttonSize, QWidget *parent = 0); /// If insertPoint==0, insert \a tool at the end of the scrollbar. Otherwise /// insert tool after \a insertPoint. void addTool (SceneTool *tool, SceneTool *insertPoint = 0); void removeTool (SceneTool *tool); int getButtonSize() const; int getIconSize() const; signals: void focusSceneRequest(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/scenetoolmode.cpp000066400000000000000000000054411264522266000244560ustar00rootroot00000000000000#include "scenetoolmode.hpp" #include #include #include #include "scenetoolbar.hpp" #include "modebutton.hpp" void CSVWidget::SceneToolMode::adjustToolTip (const ModeButton *activeMode) { QString toolTip = mToolTip; toolTip += "

Currently selected: " + activeMode->getBaseToolTip(); toolTip += "

(left click to change mode)"; setToolTip (toolTip); } CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent, const QString& toolTip) : SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()), mToolTip (toolTip), mFirst (0), mCurrent (0), mToolbar (parent) { mPanel = new QFrame (this, Qt::Popup); mLayout = new QHBoxLayout (mPanel); mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); mPanel->setLayout (mLayout); } void CSVWidget::SceneToolMode::showPanel (const QPoint& position) { mPanel->move (position); mPanel->show(); if (mFirst) mFirst->setFocus (Qt::OtherFocusReason); } void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id, const QString& tooltip) { ModeButton *button = new ModeButton (QIcon (QPixmap (icon.c_str())), tooltip, mPanel); addButton (button, id); } void CSVWidget::SceneToolMode::addButton (ModeButton *button, const std::string& id) { button->setParent (mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); mLayout->addWidget (button); mButtons.insert (std::make_pair (button, id)); connect (button, SIGNAL (clicked()), this, SLOT (selected())); if (mButtons.size()==1) { mFirst = mCurrent = button; setIcon (button->icon()); button->setChecked (true); adjustToolTip (button); mCurrent->activate (mToolbar); } } CSVWidget::ModeButton *CSVWidget::SceneToolMode::getCurrent() { return mCurrent; } void CSVWidget::SceneToolMode::selected() { std::map::const_iterator iter = mButtons.find (dynamic_cast (sender())); if (iter!=mButtons.end()) { if (!iter->first->hasKeepOpen()) mPanel->hide(); for (std::map::const_iterator iter2 = mButtons.begin(); iter2!=mButtons.end(); ++iter2) iter2->first->setChecked (iter2==iter); setIcon (iter->first->icon()); adjustToolTip (iter->first); if (mCurrent!=iter->first) { if (mCurrent) mCurrent->deactivate (mToolbar); mCurrent = iter->first; mCurrent->activate (mToolbar); } emit modeChanged (iter->second); } } openmw-openmw-0.38.0/apps/opencs/view/widget/scenetoolmode.hpp000066400000000000000000000025521264522266000244630ustar00rootroot00000000000000#ifndef CSV_WIDGET_SCENETOOL_MODE_H #define CSV_WIDGET_SCENETOOL_MODE_H #include "scenetool.hpp" #include class QHBoxLayout; namespace CSVWidget { class SceneToolbar; class ModeButton; ///< \brief Mode selector tool class SceneToolMode : public SceneTool { Q_OBJECT QWidget *mPanel; QHBoxLayout *mLayout; std::map mButtons; // widget, id int mButtonSize; int mIconSize; QString mToolTip; PushButton *mFirst; ModeButton *mCurrent; SceneToolbar *mToolbar; void adjustToolTip (const ModeButton *activeMode); public: SceneToolMode (SceneToolbar *parent, const QString& toolTip); virtual void showPanel (const QPoint& position); void addButton (const std::string& icon, const std::string& id, const QString& tooltip = ""); /// The ownership of \a button is transferred to *this. void addButton (ModeButton *button, const std::string& id); /// Will return a 0-pointer only if the mode does not have any buttons yet. ModeButton *getCurrent(); signals: void modeChanged (const std::string& id); private slots: void selected(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/scenetoolrun.cpp000066400000000000000000000076621264522266000243450ustar00rootroot00000000000000#include "scenetoolrun.hpp" #include #include #include #include #include #include void CSVWidget::SceneToolRun::adjustToolTips() { QString toolTip = mToolTip; if (mSelected==mProfiles.end()) toolTip += "

No debug profile selected (function disabled)"; else { toolTip += "

Debug profile: " + QString::fromUtf8 (mSelected->c_str()); toolTip += "

(right click to switch to a different profile)"; } setToolTip (toolTip); } void CSVWidget::SceneToolRun::updateIcon() { setDisabled (mSelected==mProfiles.end()); } void CSVWidget::SceneToolRun::updatePanel() { mTable->setRowCount (mProfiles.size()); int i = 0; for (std::set::const_iterator iter (mProfiles.begin()); iter!=mProfiles.end(); ++iter, ++i) { mTable->setItem (i, 0, new QTableWidgetItem (QString::fromUtf8 (iter->c_str()))); mTable->setItem (i, 1, new QTableWidgetItem ( QApplication::style()->standardIcon (QStyle::SP_TitleBarCloseButton), "")); } } CSVWidget::SceneToolRun::SceneToolRun (SceneToolbar *parent, const QString& toolTip, const QString& icon, const std::vector& profiles) : SceneTool (parent, Type_TopAction), mProfiles (profiles.begin(), profiles.end()), mSelected (mProfiles.begin()), mToolTip (toolTip) { setIcon (QIcon (icon)); updateIcon(); adjustToolTips(); mPanel = new QFrame (this, Qt::Popup); QHBoxLayout *layout = new QHBoxLayout (mPanel); layout->setContentsMargins (QMargins (0, 0, 0, 0)); mTable = new QTableWidget (0, 2, this); mTable->setShowGrid (false); mTable->verticalHeader()->hide(); mTable->horizontalHeader()->hide(); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents); #else mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); mTable->horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents); #endif mTable->setSelectionMode (QAbstractItemView::NoSelection); layout->addWidget (mTable); connect (mTable, SIGNAL (clicked (const QModelIndex&)), this, SLOT (clicked (const QModelIndex&))); } void CSVWidget::SceneToolRun::showPanel (const QPoint& position) { updatePanel(); mPanel->move (position); mPanel->show(); } void CSVWidget::SceneToolRun::activate() { if (mSelected!=mProfiles.end()) emit runRequest (*mSelected); } void CSVWidget::SceneToolRun::removeProfile (const std::string& profile) { std::set::iterator iter = mProfiles.find (profile); if (iter!=mProfiles.end()) { if (iter==mSelected) { if (iter!=mProfiles.begin()) --mSelected; else ++mSelected; } mProfiles.erase (iter); if (mSelected==mProfiles.end()) updateIcon(); adjustToolTips(); } } void CSVWidget::SceneToolRun::addProfile (const std::string& profile) { std::set::iterator iter = mProfiles.find (profile); if (iter==mProfiles.end()) { mProfiles.insert (profile); if (mSelected==mProfiles.end()) { mSelected = mProfiles.begin(); updateIcon(); } adjustToolTips(); } } void CSVWidget::SceneToolRun::clicked (const QModelIndex& index) { if (index.column()==0) { // select profile mSelected = mProfiles.begin(); std::advance (mSelected, index.row()); mPanel->hide(); adjustToolTips(); } else if (index.column()==1) { // remove profile from list std::set::iterator iter = mProfiles.begin(); std::advance (iter, index.row()); removeProfile (*iter); updatePanel(); } } openmw-openmw-0.38.0/apps/opencs/view/widget/scenetoolrun.hpp000066400000000000000000000027561264522266000243510ustar00rootroot00000000000000#ifndef CSV_WIDGET_SCENETOOLRUN_H #define CSV_WIDGET_SCENETOOLRUN_H #include #include #include "scenetool.hpp" class QFrame; class QTableWidget; class QModelIndex; namespace CSVWidget { class SceneToolRun : public SceneTool { Q_OBJECT std::set mProfiles; std::set::iterator mSelected; QString mToolTip; QFrame *mPanel; QTableWidget *mTable; private: void adjustToolTips(); void updateIcon(); void updatePanel(); public: SceneToolRun (SceneToolbar *parent, const QString& toolTip, const QString& icon, const std::vector& profiles); virtual void showPanel (const QPoint& position); virtual void activate(); /// \attention This function does not remove the profile from the profile selection /// panel. void removeProfile (const std::string& profile); /// \attention This function doe not add the profile to the profile selection /// panel. This only happens when the panel is re-opened. /// /// \note Adding profiles that are already listed is a no-op. void addProfile (const std::string& profile); private slots: void clicked (const QModelIndex& index); signals: void runRequest (const std::string& profile); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/scenetooltoggle.cpp000066400000000000000000000123361264522266000250140ustar00rootroot00000000000000#include "scenetooltoggle.hpp" #include #include #include #include #include #include "scenetoolbar.hpp" #include "pushbutton.hpp" void CSVWidget::SceneToolToggle::adjustToolTip() { QString toolTip = mToolTip; toolTip += "

Currently enabled: "; bool first = true; for (std::map::const_iterator iter (mButtons.begin()); iter!=mButtons.end(); ++iter) if (iter->first->isChecked()) { if (!first) toolTip += ", "; else first = false; toolTip += iter->second.mName; } if (first) toolTip += "none"; toolTip += "

(left click to alter selection)"; setToolTip (toolTip); } void CSVWidget::SceneToolToggle::adjustIcon() { unsigned int selection = getSelection(); if (!selection) setIcon (QIcon (QString::fromUtf8 (mEmptyIcon.c_str()))); else { QPixmap pixmap (48, 48); pixmap.fill (QColor (0, 0, 0, 0)); { QPainter painter (&pixmap); for (std::map::const_iterator iter (mButtons.begin()); iter!=mButtons.end(); ++iter) if (iter->first->isChecked()) { painter.drawImage (getIconBox (iter->second.mIndex), QImage (QString::fromUtf8 (iter->second.mSmallIcon.c_str()))); } } setIcon (pixmap); } } QRect CSVWidget::SceneToolToggle::getIconBox (int index) const { // layout for a 3x3 grid int xMax = 3; int yMax = 3; // icon size int xBorder = 1; int yBorder = 1; int iconXSize = (mIconSize-xBorder*(xMax+1))/xMax; int iconYSize = (mIconSize-yBorder*(yMax+1))/yMax; int y = index / xMax; int x = index % xMax; int total = mButtons.size(); int actualYIcons = total/xMax; if (total % xMax) ++actualYIcons; if (actualYIcons!=yMax) { // space out icons vertically, if there aren't enough to populate all rows int diff = yMax - actualYIcons; yBorder += (diff*(yBorder+iconXSize)) / (actualYIcons+1); } if (y==actualYIcons-1) { // generating the last row of icons int actualXIcons = total % xMax; if (actualXIcons) { // space out icons horizontally, if there aren't enough to fill the last row int diff = xMax - actualXIcons; xBorder += (diff*(xBorder+iconXSize)) / (actualXIcons+1); } } return QRect ((iconXSize+xBorder)*x+xBorder, (iconYSize+yBorder)*y+yBorder, iconXSize, iconYSize); } CSVWidget::SceneToolToggle::SceneToolToggle (SceneToolbar *parent, const QString& toolTip, const std::string& emptyIcon) : SceneTool (parent), mEmptyIcon (emptyIcon), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()), mToolTip (toolTip), mFirst (0) { mPanel = new QFrame (this, Qt::Popup); mLayout = new QHBoxLayout (mPanel); mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); mPanel->setLayout (mLayout); } void CSVWidget::SceneToolToggle::showPanel (const QPoint& position) { mPanel->move (position); mPanel->show(); if (mFirst) mFirst->setFocus (Qt::OtherFocusReason); } void CSVWidget::SceneToolToggle::addButton (const std::string& icon, unsigned int id, const std::string& smallIcon, const QString& name, const QString& tooltip) { if (mButtons.size()>=9) throw std::runtime_error ("Exceeded number of buttons in toggle type tool"); PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), PushButton::Type_Toggle, tooltip.isEmpty() ? name: tooltip, mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); mLayout->addWidget (button); ButtonDesc desc; desc.mId = id; desc.mSmallIcon = smallIcon; desc.mName = name; desc.mIndex = mButtons.size(); mButtons.insert (std::make_pair (button, desc)); connect (button, SIGNAL (clicked()), this, SLOT (selected())); if (mButtons.size()==1) mFirst = button; } unsigned int CSVWidget::SceneToolToggle::getSelection() const { unsigned int selection = 0; for (std::map::const_iterator iter (mButtons.begin()); iter!=mButtons.end(); ++iter) if (iter->first->isChecked()) selection |= iter->second.mId; return selection; } void CSVWidget::SceneToolToggle::setSelection (unsigned int selection) { for (std::map::iterator iter (mButtons.begin()); iter!=mButtons.end(); ++iter) iter->first->setChecked (selection & iter->second.mId); adjustToolTip(); adjustIcon(); } void CSVWidget::SceneToolToggle::selected() { std::map::const_iterator iter = mButtons.find (dynamic_cast (sender())); if (iter!=mButtons.end()) { if (!iter->first->hasKeepOpen()) mPanel->hide(); adjustToolTip(); adjustIcon(); emit selectionChanged(); } } openmw-openmw-0.38.0/apps/opencs/view/widget/scenetooltoggle.hpp000066400000000000000000000037641264522266000250260ustar00rootroot00000000000000#ifndef CSV_WIDGET_SCENETOOL_TOGGLE_H #define CSV_WIDGET_SCENETOOL_TOGGLE_H #include "scenetool.hpp" #include class QHBoxLayout; class QRect; namespace CSVWidget { class SceneToolbar; class PushButton; ///< \brief Multi-Toggle tool class SceneToolToggle : public SceneTool { Q_OBJECT struct ButtonDesc { unsigned int mId; std::string mSmallIcon; QString mName; int mIndex; }; std::string mEmptyIcon; QWidget *mPanel; QHBoxLayout *mLayout; std::map mButtons; // widget, id int mButtonSize; int mIconSize; QString mToolTip; PushButton *mFirst; void adjustToolTip(); void adjustIcon(); QRect getIconBox (int index) const; public: SceneToolToggle (SceneToolbar *parent, const QString& toolTip, const std::string& emptyIcon); virtual void showPanel (const QPoint& position); /// \attention After the last button has been added, setSelection must be called at /// least once to finalise the layout. /// /// \note The layout algorithm can not handle more than 9 buttons. To prevent this An /// attempt to add more will result in an exception being thrown. /// The small icons will be sized at (x-4)/3 (where x is the main icon size). void addButton (const std::string& icon, unsigned int id, const std::string& smallIcon, const QString& name, const QString& tooltip = ""); unsigned int getSelection() const; /// \param or'ed button IDs. IDs that do not exist will be ignored. void setSelection (unsigned int selection); signals: void selectionChanged(); private slots: void selected(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/widget/scenetooltoggle2.cpp000066400000000000000000000071011264522266000250700ustar00rootroot00000000000000#include "scenetooltoggle2.hpp" #include #include #include #include #include #include #include "scenetoolbar.hpp" #include "pushbutton.hpp" void CSVWidget::SceneToolToggle2::adjustToolTip() { QString toolTip = mToolTip; toolTip += "

Currently enabled: "; bool first = true; for (std::map::const_iterator iter (mButtons.begin()); iter!=mButtons.end(); ++iter) if (iter->first->isChecked()) { if (!first) toolTip += ", "; else first = false; toolTip += iter->second.mName; } if (first) toolTip += "none"; toolTip += "

(left click to alter selection)"; setToolTip (toolTip); } void CSVWidget::SceneToolToggle2::adjustIcon() { std::ostringstream stream; stream << mCompositeIcon << getSelection(); setIcon (QIcon (QString::fromUtf8 (stream.str().c_str()))); } CSVWidget::SceneToolToggle2::SceneToolToggle2 (SceneToolbar *parent, const QString& toolTip, const std::string& compositeIcon, const std::string& singleIcon) : SceneTool (parent), mCompositeIcon (compositeIcon), mSingleIcon (singleIcon), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()), mToolTip (toolTip), mFirst (0) { mPanel = new QFrame (this, Qt::Popup); mLayout = new QHBoxLayout (mPanel); mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); mPanel->setLayout (mLayout); } void CSVWidget::SceneToolToggle2::showPanel (const QPoint& position) { mPanel->move (position); mPanel->show(); if (mFirst) mFirst->setFocus (Qt::OtherFocusReason); } void CSVWidget::SceneToolToggle2::addButton (unsigned int id, const QString& name, const QString& tooltip, bool disabled) { std::ostringstream stream; stream << mSingleIcon << id; PushButton *button = new PushButton (QIcon (QPixmap (stream.str().c_str())), PushButton::Type_Toggle, tooltip.isEmpty() ? name: tooltip, mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); if (disabled) button->setDisabled (true); mLayout->addWidget (button); ButtonDesc desc; desc.mId = id; desc.mName = name; desc.mIndex = mButtons.size(); mButtons.insert (std::make_pair (button, desc)); connect (button, SIGNAL (clicked()), this, SLOT (selected())); if (mButtons.size()==1 && !disabled) mFirst = button; } unsigned int CSVWidget::SceneToolToggle2::getSelection() const { unsigned int selection = 0; for (std::map::const_iterator iter (mButtons.begin()); iter!=mButtons.end(); ++iter) if (iter->first->isChecked()) selection |= iter->second.mId; return selection; } void CSVWidget::SceneToolToggle2::setSelection (unsigned int selection) { for (std::map::iterator iter (mButtons.begin()); iter!=mButtons.end(); ++iter) iter->first->setChecked (selection & iter->second.mId); adjustToolTip(); adjustIcon(); } void CSVWidget::SceneToolToggle2::selected() { std::map::const_iterator iter = mButtons.find (dynamic_cast (sender())); if (iter!=mButtons.end()) { if (!iter->first->hasKeepOpen()) mPanel->hide(); adjustToolTip(); adjustIcon(); emit selectionChanged(); } } openmw-openmw-0.38.0/apps/opencs/view/widget/scenetooltoggle2.hpp000066400000000000000000000040171264522266000251000ustar00rootroot00000000000000#ifndef CSV_WIDGET_SCENETOOL_TOGGLE2_H #define CSV_WIDGET_SCENETOOL_TOGGLE2_H #include "scenetool.hpp" #include class QHBoxLayout; class QRect; namespace CSVWidget { class SceneToolbar; class PushButton; ///< \brief Multi-Toggle tool /// /// Top level button is using predefined icons instead building a composite icon. class SceneToolToggle2 : public SceneTool { Q_OBJECT struct ButtonDesc { unsigned int mId; QString mName; int mIndex; }; std::string mCompositeIcon; std::string mSingleIcon; QWidget *mPanel; QHBoxLayout *mLayout; std::map mButtons; // widget, id int mButtonSize; int mIconSize; QString mToolTip; PushButton *mFirst; void adjustToolTip(); void adjustIcon(); public: /// The top level icon is compositeIcon + sum of bitpatterns for active buttons (in /// decimal) /// /// The icon for individual toggle buttons is signleIcon + bitmask for button (in /// decimal) SceneToolToggle2 (SceneToolbar *parent, const QString& toolTip, const std::string& compositeIcon, const std::string& singleIcon); virtual void showPanel (const QPoint& position); /// \attention After the last button has been added, setSelection must be called at /// least once to finalise the layout. void addButton (unsigned int id, const QString& name, const QString& tooltip = "", bool disabled = false); unsigned int getSelection() const; /// \param or'ed button IDs. IDs that do not exist will be ignored. void setSelection (unsigned int selection); signals: void selectionChanged(); private slots: void selected(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/000077500000000000000000000000001264522266000207525ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/opencs/view/world/cellcreator.cpp000066400000000000000000000064121264522266000237600ustar00rootroot00000000000000#include "cellcreator.hpp" #include #include #include #include #include #include "../../model/world/commands.hpp" #include "../../model/world/idtree.hpp" std::string CSVWorld::CellCreator::getId() const { if (mType->currentIndex()==0) return GenericCreator::getId(); std::ostringstream stream; stream << "#" << mX->value() << " " << mY->value(); return stream.str(); } void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const { CSMWorld::IdTree *model = dynamic_cast(getData().getTableModel(getCollectionId())); Q_ASSERT(model != NULL); int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell); int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior); command.addNestedValue(parentIndex, index, mType->currentIndex() == 0); } CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) : GenericCreator (data, undoStack, id) { mY = new QSpinBox (this); mY->setVisible (false); mY->setMinimum (std::numeric_limits::min()); mY->setMaximum (std::numeric_limits::max()); connect (mY, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); insertAtBeginning (mY, true); mYLabel = new QLabel ("Y", this); mYLabel->setVisible (false); insertAtBeginning (mYLabel, false); mX = new QSpinBox (this); mX->setVisible (false); mX->setMinimum (std::numeric_limits::min()); mX->setMaximum (std::numeric_limits::max()); connect (mX, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); insertAtBeginning (mX, true); mXLabel = new QLabel ("X", this); mXLabel->setVisible (false); insertAtBeginning (mXLabel, false); mType = new QComboBox (this); mType->addItem ("Interior Cell"); mType->addItem ("Exterior Cell"); connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int))); insertAtBeginning (mType, false); } void CSVWorld::CellCreator::reset() { mX->setValue (0); mY->setValue (0); mType->setCurrentIndex (0); setType(0); GenericCreator::reset(); } void CSVWorld::CellCreator::setType (int index) { setManualEditing (index==0); mXLabel->setVisible (index==1); mX->setVisible (index==1); mYLabel->setVisible (index==1); mY->setVisible (index==1); update(); } void CSVWorld::CellCreator::valueChanged (int index) { update(); } void CSVWorld::CellCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) { CSVWorld::GenericCreator::cloneMode(originId, type); if (*(originId.begin()) == '#') //if originid points to the exterior cell { setType(1); //enable x and y controls mType->setCurrentIndex(1); } else { setType(0); mType->setCurrentIndex(0); } } std::string CSVWorld::CellCreator::getErrors() const { std::string errors; if (mType->currentIndex() == 0) { errors = GenericCreator::getErrors(); } else if (getData().hasId(getId())) { errors = "The Exterior Cell is already exist"; } return errors; } openmw-openmw-0.38.0/apps/opencs/view/world/cellcreator.hpp000066400000000000000000000023311264522266000237610ustar00rootroot00000000000000#ifndef CSV_WORLD_CELLCREATOR_H #define CSV_WORLD_CELLCREATOR_H class QLabel; class QSpinBox; class QComboBox; #include "genericcreator.hpp" namespace CSVWorld { class CellCreator : public GenericCreator { Q_OBJECT QComboBox *mType; QLabel *mXLabel; QSpinBox *mX; QLabel *mYLabel; QSpinBox *mY; protected: virtual std::string getId() const; /// Allow subclasses to add additional data to \a command. virtual void configureCreateCommand(CSMWorld::CreateCommand& command) const; public: CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); virtual void reset(); virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. private slots: void setType (int index); void valueChanged (int index); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/colordelegate.cpp000066400000000000000000000025401264522266000242700ustar00rootroot00000000000000#include "colordelegate.hpp" #include #include #include "../widget/coloreditor.hpp" CSVWorld::ColorDelegate::ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) : CommandDelegate(dispatcher, document, parent) {} void CSVWorld::ColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0), option.rect.y() + qRound(option.rect.height() / 4.0), option.rect.width() / 2, option.rect.height() / 2); painter->save(); painter->fillRect(coloredRect, index.data().value()); painter->setPen(Qt::black); painter->drawRect(coloredRect); painter->restore(); } CSVWorld::CommandDelegate *CSVWorld::ColorDelegateFactory::makeDelegate(CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document &document, QObject *parent) const { return new ColorDelegate(dispatcher, document, parent); } openmw-openmw-0.38.0/apps/opencs/view/world/colordelegate.hpp000066400000000000000000000020321264522266000242710ustar00rootroot00000000000000#ifndef CSV_WORLD_COLORDELEGATE_HPP #define CSV_WORLD_COLORDELEGATE_HPP #include "util.hpp" class QRect; namespace CSVWidget { class ColorEditButton; } namespace CSVWorld { class ColorDelegate : public CommandDelegate { public: ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; class ColorDelegateFactory : public CommandDelegateFactory { public: virtual CommandDelegate *makeDelegate(CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document &document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/creator.cpp000066400000000000000000000007741264522266000231250ustar00rootroot00000000000000#include "creator.hpp" #include CSVWorld::Creator::~Creator() {} void CSVWorld::Creator::setScope (unsigned int scope) { if (scope!=CSMWorld::Scope_Content) throw std::logic_error ("Invalid scope in creator"); } CSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {} CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const { return 0; } openmw-openmw-0.38.0/apps/opencs/view/world/creator.hpp000066400000000000000000000060061264522266000231240ustar00rootroot00000000000000#ifndef CSV_WORLD_CREATOR_H #define CSV_WORLD_CREATOR_H #include #include #include "../../model/doc/document.hpp" #include "../../model/world/scope.hpp" #include "../../model/world/universalid.hpp" namespace CSMDoc { class Document; } namespace CSVWorld { /// \brief Record creator UI base class class Creator : public QWidget { Q_OBJECT public: virtual ~Creator(); virtual void reset() = 0; virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) = 0; virtual void setEditLock (bool locked) = 0; virtual void toggleWidgets(bool active = true) = 0; /// Default implementation: Throw an exception if scope!=Scope_Content. virtual void setScope (unsigned int scope); /// Focus main input widget virtual void focus() = 0; signals: void done(); void requestFocus (const std::string& id); ///< Request owner of this creator to focus the just created \a id. The owner may /// ignore this request. }; /// \brief Base class for Creator factory class CreatorFactoryBase { public: virtual ~CreatorFactoryBase(); virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const = 0; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function can return a 0-pointer, which means no UI for creating/deleting /// records should be provided. }; /// \brief Creator factory that does not produces any creator class NullCreatorFactory : public CreatorFactoryBase { public: virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function always returns 0. }; template class CreatorFactory : public CreatorFactoryBase { public: virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function can return a 0-pointer, which means no UI for creating/deleting /// records should be provided. }; template Creator *CreatorFactory::makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const { std::auto_ptr creator (new CreatorT (document.getData(), document.getUndoStack(), id)); creator->setScope (scope); return creator.release(); } } #endif openmw-openmw-0.38.0/apps/opencs/view/world/datadisplaydelegate.cpp000066400000000000000000000117611264522266000254560ustar00rootroot00000000000000#include "datadisplaydelegate.hpp" #include "../../model/prefs/state.hpp" #include #include CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, const std::string &pageName, const std::string &settingName, QObject *parent) : EnumDelegate (values, dispatcher, document, parent), mDisplayMode (Mode_TextOnly), mIcons (icons), mIconSize (QSize(16, 16)), mHorizontalMargin(QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1), mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName) { buildPixmaps(); if (!pageName.empty()) updateDisplayMode (CSMPrefs::get()[pageName][settingName].toString()); } void CSVWorld::DataDisplayDelegate::buildPixmaps () { if (!mPixmaps.empty()) mPixmaps.clear(); IconList::iterator it = mIcons.begin(); while (it != mIcons.end()) { mPixmaps.push_back (std::make_pair (it->first, it->second.pixmap (mIconSize) ) ); ++it; } } void CSVWorld::DataDisplayDelegate::setIconSize(const QSize& size) { mIconSize = size; buildPixmaps(); } void CSVWorld::DataDisplayDelegate::setTextLeftOffset(int offset) { mTextLeftOffset = offset; } QSize CSVWorld::DataDisplayDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QSize size = EnumDelegate::sizeHint(option, index); int valueIndex = getValueIndex(index); if (valueIndex != -1) { if (mDisplayMode == Mode_IconOnly) { size.setWidth(mIconSize.width() + 2 * mHorizontalMargin); } else if (mDisplayMode == Mode_IconAndText) { size.setWidth(size.width() + mIconSize.width() + mTextLeftOffset); } if (mDisplayMode != Mode_TextOnly) { size.setHeight(qMax(size.height(), mIconSize.height())); } } return size; } void CSVWorld::DataDisplayDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { painter->save(); //default to enum delegate's paint method for text-only conditions if (mDisplayMode == Mode_TextOnly) EnumDelegate::paint(painter, option, index); else { int valueIndex = getValueIndex(index); if (valueIndex != -1) { paintIcon(painter, option, valueIndex); } } painter->restore(); } void CSVWorld::DataDisplayDelegate::paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int index) const { QRect iconRect = option.rect; QRect textRect = iconRect; iconRect.setLeft(iconRect.left() + mHorizontalMargin); iconRect.setRight(option.rect.right() - mHorizontalMargin); if (mDisplayMode == Mode_IconAndText) { iconRect.setWidth(mIconSize.width()); textRect.setLeft(iconRect.right() + mTextLeftOffset); textRect.setRight(option.rect.right() - mHorizontalMargin); QString text = option.fontMetrics.elidedText(mValues.at(index).second, option.textElideMode, textRect.width()); QApplication::style()->drawItemText(painter, textRect, Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text); } QApplication::style()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, mPixmaps.at(index).second); } void CSVWorld::DataDisplayDelegate::updateDisplayMode (const std::string &mode) { if (mode == "Icon and Text") mDisplayMode = Mode_IconAndText; else if (mode == "Icon Only") mDisplayMode = Mode_IconOnly; else if (mode == "Text Only") mDisplayMode = Mode_TextOnly; } CSVWorld::DataDisplayDelegate::~DataDisplayDelegate() { } void CSVWorld::DataDisplayDelegate::settingChanged (const CSMPrefs::Setting *setting) { if (*setting==mSettingKey) updateDisplayMode (setting->toString()); } void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, const QString& enumName, const QString& iconFilename) { mIcons.push_back (std::make_pair(enumValue, QIcon(iconFilename))); EnumDelegateFactory::add(enumValue, enumName); } CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate ( CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { return new DataDisplayDelegate (mValues, mIcons, dispatcher, document, "", "", parent); } openmw-openmw-0.38.0/apps/opencs/view/world/datadisplaydelegate.hpp000077500000000000000000000051201264522266000254560ustar00rootroot00000000000000#ifndef DATADISPLAYDELEGATE_HPP #define DATADISPLAYDELEGATE_HPP #include #include "enumdelegate.hpp" namespace CSMPrefs { class Setting; } namespace CSVWorld { class DataDisplayDelegate : public EnumDelegate { public: typedef std::vector < std::pair < int, QIcon > > IconList; typedef std::vector > ValueList; protected: enum DisplayMode { Mode_TextOnly, Mode_IconOnly, Mode_IconAndText }; DisplayMode mDisplayMode; IconList mIcons; private: std::vector > mPixmaps; QSize mIconSize; int mHorizontalMargin; int mTextLeftOffset; std::string mSettingKey; public: DataDisplayDelegate (const ValueList & values, const IconList & icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, const std::string& pageName, const std::string& settingName, QObject *parent); ~DataDisplayDelegate(); virtual void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; /// pass a QSize defining height / width of icon. Default is QSize (16,16). void setIconSize (const QSize& icon); /// offset the horizontal position of the text from the right edge of the icon. Default is 8 pixels. void setTextLeftOffset (int offset); private: /// update the display mode based on a passed string void updateDisplayMode (const std::string &); /// custom paint function for painting the icon. Mode_IconAndText and Mode_Icon only. void paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int i) const; /// rebuild the list of pixmaps from the provided icons (called when icon size is changed) void buildPixmaps(); virtual void settingChanged (const CSMPrefs::Setting *setting); }; class DataDisplayDelegateFactory : public EnumDelegateFactory { protected: DataDisplayDelegate::IconList mIcons; public: virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. protected: void add (int enumValue, const QString& enumName, const QString& iconFilename); }; } #endif // DATADISPLAYDELEGATE_HPP openmw-openmw-0.38.0/apps/opencs/view/world/dialoguecreator.cpp000066400000000000000000000025661264522266000246400ustar00rootroot00000000000000#include "dialoguecreator.hpp" #include #include "../../model/doc/document.hpp" #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/idtable.hpp" void CSVWorld::DialogueCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const { int index = dynamic_cast (*getData().getTableModel (getCollectionId())). findColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); command.addValue (index, mType); } CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, int type) : GenericCreator (data, undoStack, id, true), mType (type) {} CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const { return new DialogueCreator (document.getData(), document.getUndoStack(), id, ESM::Dialogue::Topic); } CSVWorld::Creator *CSVWorld::JournalCreatorFactory::makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const { return new DialogueCreator (document.getData(), document.getUndoStack(), id, ESM::Dialogue::Journal); } openmw-openmw-0.38.0/apps/opencs/view/world/dialoguecreator.hpp000066400000000000000000000020361264522266000246350ustar00rootroot00000000000000#ifndef CSV_WORLD_DIALOGUECREATOR_H #define CSV_WORLD_DIALOGUECREATOR_H #include "genericcreator.hpp" namespace CSVWorld { class DialogueCreator : public GenericCreator { int mType; protected: virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; public: DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, int type); }; class TopicCreatorFactory : public CreatorFactoryBase { public: virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. }; class JournalCreatorFactory : public CreatorFactoryBase { public: virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/dialoguespinbox.cpp000066400000000000000000000023311264522266000246510ustar00rootroot00000000000000#include "dialoguespinbox.hpp" #include CSVWorld::DialogueSpinBox::DialogueSpinBox(QWidget *parent) : QSpinBox(parent) { setFocusPolicy(Qt::StrongFocus); } void CSVWorld::DialogueSpinBox::focusInEvent(QFocusEvent *event) { setFocusPolicy(Qt::WheelFocus); QSpinBox::focusInEvent(event); } void CSVWorld::DialogueSpinBox::focusOutEvent(QFocusEvent *event) { setFocusPolicy(Qt::StrongFocus); QSpinBox::focusOutEvent(event); } void CSVWorld::DialogueSpinBox::wheelEvent(QWheelEvent *event) { if (!hasFocus()) event->ignore(); else QSpinBox::wheelEvent(event); } CSVWorld::DialogueDoubleSpinBox::DialogueDoubleSpinBox(QWidget *parent) : QDoubleSpinBox(parent) { setFocusPolicy(Qt::StrongFocus); } void CSVWorld::DialogueDoubleSpinBox::focusInEvent(QFocusEvent *event) { setFocusPolicy(Qt::WheelFocus); QDoubleSpinBox::focusInEvent(event); } void CSVWorld::DialogueDoubleSpinBox::focusOutEvent(QFocusEvent *event) { setFocusPolicy(Qt::StrongFocus); QDoubleSpinBox::focusOutEvent(event); } void CSVWorld::DialogueDoubleSpinBox::wheelEvent(QWheelEvent *event) { if (!hasFocus()) event->ignore(); else QDoubleSpinBox::wheelEvent(event); } openmw-openmw-0.38.0/apps/opencs/view/world/dialoguespinbox.hpp000066400000000000000000000015611264522266000246620ustar00rootroot00000000000000#ifndef CSV_WORLD_DIALOGUESPINBOX_H #define CSV_WORLD_DIALOGUESPINBOX_H #include #include namespace CSVWorld { class DialogueSpinBox : public QSpinBox { Q_OBJECT public: DialogueSpinBox (QWidget *parent = 0); protected: virtual void focusInEvent(QFocusEvent *event); virtual void focusOutEvent(QFocusEvent *event); virtual void wheelEvent(QWheelEvent *event); }; class DialogueDoubleSpinBox : public QDoubleSpinBox { Q_OBJECT public: DialogueDoubleSpinBox (QWidget *parent = 0); protected: virtual void focusInEvent(QFocusEvent *event); virtual void focusOutEvent(QFocusEvent *event); virtual void wheelEvent(QWheelEvent *event); }; } #endif // CSV_WORLD_DIALOGUESPINBOX_H openmw-openmw-0.38.0/apps/opencs/view/world/dialoguesubview.cpp000066400000000000000000001021131264522266000246520ustar00rootroot00000000000000#include "dialoguesubview.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/columnbase.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/idtree.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/record.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/idtree.hpp" #include "../../model/world/commands.hpp" #include "../../model/doc/document.hpp" #include "../../model/prefs/state.hpp" #include "../widget/coloreditor.hpp" #include "../widget/droplineedit.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" #include "tablebottombox.hpp" #include "nestedtable.hpp" #include "recordbuttonbar.hpp" /* ==============================NotEditableSubDelegate========================================== */ CSVWorld::NotEditableSubDelegate::NotEditableSubDelegate(const CSMWorld::IdTable* table, QObject * parent) : QAbstractItemDelegate(parent), mTable(table) {} void CSVWorld::NotEditableSubDelegate::setEditorData (QWidget* editor, const QModelIndex& index) const { QLabel* label = qobject_cast(editor); if(!label) return; QVariant v = index.data(Qt::EditRole); if (!v.isValid()) { v = index.data(Qt::DisplayRole); if (!v.isValid()) { return; } } CSMWorld::Columns::ColumnId columnId = static_cast ( mTable->getColumnId (index.column())); if (QVariant::String == v.type()) { label->setText(v.toString()); } else if (CSMWorld::Columns::hasEnums (columnId)) { int data = v.toInt(); std::vector enumNames (CSMWorld::Columns::getEnums (columnId)); label->setText(QString::fromUtf8(enumNames.at(data).c_str())); } else { label->setText (v.toString()); } } void CSVWorld::NotEditableSubDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { //not editable widgets will not save model data } void CSVWorld::NotEditableSubDelegate::paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { //does nothing } QSize CSVWorld::NotEditableSubDelegate::sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const { return QSize(); } QWidget* CSVWorld::NotEditableSubDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { QLabel *label = new QLabel(parent); label->setTextInteractionFlags (Qt::TextSelectableByMouse); return label; } /* ==============================DialogueDelegateDispatcherProxy========================================== */ CSVWorld::DialogueDelegateDispatcherProxy::refWrapper::refWrapper(const QModelIndex& index) : mIndex(index) {} CSVWorld::DialogueDelegateDispatcherProxy::DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display) : mEditor(editor), mDisplay(display), mIndexWrapper(NULL) { } void CSVWorld::DialogueDelegateDispatcherProxy::editorDataCommited() { if (mIndexWrapper.get()) { emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); } } void CSVWorld::DialogueDelegateDispatcherProxy::setIndex(const QModelIndex& index) { mIndexWrapper.reset(new refWrapper(index)); } QWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const { return mEditor; } /* ==============================DialogueDelegateDispatcher========================================== */ CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, QAbstractItemModel *model) : mParent(parent), mTable(model ? model : table), mCommandDispatcher (commandDispatcher), mDocument (document), mNotEditableDelegate(table, parent) { } CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display) { CommandDelegate *delegate = NULL; std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt == mDelegates.end()) { delegate = CommandDelegateFactoryCollection::get().makeDelegate ( display, &mCommandDispatcher, mDocument, mParent); mDelegates.insert(std::make_pair(display, delegate)); } else { delegate = delegateIt->second; } return delegate; } void CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display) { setModelData(editor, mTable, index, display); } void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const QModelIndex& index) const { CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None; if (index.parent().isValid()) { display = static_cast (static_cast(mTable)->nestedHeaderData (index.parent().column(), index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); } else { display = static_cast (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); } QLabel* label = qobject_cast(editor); if(label) { mNotEditableDelegate.setEditorData(label, index); return; } std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { delegateIt->second->setEditorData(editor, index, true); } for (unsigned i = 0; i < mProxys.size(); ++i) { if (mProxys[i]->getEditor() == editor) { mProxys[i]->setIndex(index); } } } void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { setModelData(editor, model, index, CSMWorld::ColumnBase::Display_None); } void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { delegateIt->second->setModelData(editor, model, index); } } void CSVWorld::DialogueDelegateDispatcher::paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { //Does nothing } QSize CSVWorld::DialogueDelegateDispatcher::sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const { return QSize(); //silencing warning, otherwise does nothing } QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index) { QVariant variant = index.data(); if (!variant.isValid()) { variant = index.data(Qt::DisplayRole); if (!variant.isValid()) { return NULL; } } QWidget* editor = NULL; if (! (mTable->flags (index) & Qt::ItemIsEditable)) { return mNotEditableDelegate.createEditor(qobject_cast(mParent), QStyleOptionViewItem(), index); } std::map::iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { editor = delegateIt->second->createEditor(qobject_cast(mParent), QStyleOptionViewItem(), index, display); DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); // NOTE: For each entry in CSVWorld::CommandDelegate::createEditor() a corresponding entry // is required here if (qobject_cast(editor)) { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); connect(editor, SIGNAL(tableMimeDataDropped(const CSMWorld::UniversalId&, const CSMDoc::Document*)), proxy, SLOT(editorDataCommited())); } else if (qobject_cast(editor)) { connect(editor, SIGNAL(stateChanged(int)), proxy, SLOT(editorDataCommited())); } else if (qobject_cast(editor)) { connect(editor, SIGNAL(textChanged()), proxy, SLOT(editorDataCommited())); } else if (qobject_cast(editor)) { connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited())); } else if (qobject_cast(editor) || qobject_cast(editor)) { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); } else if (qobject_cast(editor)) { connect(editor, SIGNAL(pickingFinished()), proxy, SLOT(editorDataCommited())); } else // throw an exception because this is a coding error throw std::logic_error ("Dialogue editor type missing"); connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display))); mProxys.push_back(proxy); //deleted in the destructor } return editor; } CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher() { for (unsigned i = 0; i < mProxys.size(); ++i) { delete mProxys[i]; //unique_ptr could be handy } } CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Display display) : QObject(widget), mWidget(widget), mIdType(CSMWorld::TableMimeData::convertEnums(display)) { Q_ASSERT(mWidget != NULL); Q_ASSERT(CSMWorld::ColumnBase::isId(display)); Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None); mWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(mWidget, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showContextMenu(const QPoint &))); mEditIdAction = new QAction(this); connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editIdRequest())); QLineEdit *lineEdit = qobject_cast(mWidget); if (lineEdit != NULL) { mContextMenu = lineEdit->createStandardContextMenu(); } else { mContextMenu = new QMenu(mWidget); } } void CSVWorld::IdContextMenu::excludeId(const std::string &id) { mExcludedIds.insert(id); } QString CSVWorld::IdContextMenu::getWidgetValue() const { QLineEdit *lineEdit = qobject_cast(mWidget); QLabel *label = qobject_cast(mWidget); QString value = ""; if (lineEdit != NULL) { value = lineEdit->text(); } else if (label != NULL) { value = label->text(); } return value; } void CSVWorld::IdContextMenu::addEditIdActionToMenu(const QString &text) { mEditIdAction->setText(text); if (mContextMenu->actions().isEmpty()) { mContextMenu->addAction(mEditIdAction); } else if (mContextMenu->actions().first() != mEditIdAction) { QAction *action = mContextMenu->actions().first(); mContextMenu->insertAction(action, mEditIdAction); mContextMenu->insertSeparator(action); } } void CSVWorld::IdContextMenu::removeEditIdActionFromMenu() { if (mContextMenu->actions().isEmpty()) { return; } if (mContextMenu->actions().first() == mEditIdAction) { mContextMenu->removeAction(mEditIdAction); if (!mContextMenu->actions().isEmpty() && mContextMenu->actions().first()->isSeparator()) { mContextMenu->removeAction(mContextMenu->actions().first()); } } } void CSVWorld::IdContextMenu::showContextMenu(const QPoint &pos) { QString value = getWidgetValue(); bool isExcludedId = mExcludedIds.find(value.toUtf8().constData()) != mExcludedIds.end(); if (!value.isEmpty() && !isExcludedId) { addEditIdActionToMenu("Edit '" + value + "'"); } else { removeEditIdActionFromMenu(); } if (!mContextMenu->actions().isEmpty()) { mContextMenu->exec(mWidget->mapToGlobal(pos)); } } void CSVWorld::IdContextMenu::editIdRequest() { CSMWorld::UniversalId editId(mIdType, getWidgetValue().toUtf8().constData()); emit editIdRequest(editId, ""); } /* =============================================================EditWidget===================================================== */ void CSVWorld::EditWidget::createEditorContextMenu(QWidget *editor, CSMWorld::ColumnBase::Display display, int currentRow) const { Q_ASSERT(editor != NULL); if (CSMWorld::ColumnBase::isId(display) && CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None) { int idColumn = mTable->findColumnIndex(CSMWorld::Columns::ColumnId_Id); QString id = mTable->data(mTable->index(currentRow, idColumn)).toString(); IdContextMenu *menu = new IdContextMenu(editor, display); // Current ID is already opened, so no need to create Edit 'ID' action for it menu->excludeId(id.toUtf8().constData()); connect(menu, SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)), this, SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &))); } } CSVWorld::EditWidget::~EditWidget() { for (unsigned i = 0; i < mNestedModels.size(); ++i) delete mNestedModels[i]; if (mDispatcher) delete mDispatcher; if (mNestedTableDispatcher) delete mNestedTableDispatcher; } CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, bool createAndDelete) : QScrollArea(parent), mWidgetMapper(NULL), mNestedTableMapper(NULL), mDispatcher(NULL), mNestedTableDispatcher(NULL), mMainWidget(NULL), mTable(table), mCommandDispatcher (commandDispatcher), mDocument (document) { remake (row); } void CSVWorld::EditWidget::remake(int row) { if (mMainWidget) { QWidget *del = this->takeWidget(); del->deleteLater(); } mMainWidget = new QWidget (this); for (unsigned i = 0; i < mNestedModels.size(); ++i) delete mNestedModels[i]; mNestedModels.clear(); if (mDispatcher) delete mDispatcher; mDispatcher = new DialogueDelegateDispatcher(0/*this*/, mTable, mCommandDispatcher, mDocument); if (mNestedTableDispatcher) delete mNestedTableDispatcher; //not sure if widget mapper can handle deleting the widgets that were mapped if (mWidgetMapper) delete mWidgetMapper; mWidgetMapper = new QDataWidgetMapper (this); mWidgetMapper->setModel(mTable); mWidgetMapper->setItemDelegate(mDispatcher); if (mNestedTableMapper) delete mNestedTableMapper; QFrame* line = new QFrame(mMainWidget); line->setObjectName(QString::fromUtf8("line")); line->setGeometry(QRect(320, 150, 118, 3)); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); QFrame* line2 = new QFrame(mMainWidget); line2->setObjectName(QString::fromUtf8("line")); line2->setGeometry(QRect(320, 150, 118, 3)); line2->setFrameShape(QFrame::HLine); line2->setFrameShadow(QFrame::Sunken); QVBoxLayout *mainLayout = new QVBoxLayout(mMainWidget); QGridLayout *lockedLayout = new QGridLayout(); QGridLayout *unlockedLayout = new QGridLayout(); QVBoxLayout *tablesLayout = new QVBoxLayout(); mainLayout->addLayout(lockedLayout, QSizePolicy::Fixed); mainLayout->addWidget(line, 1); mainLayout->addLayout(unlockedLayout, QSizePolicy::Preferred); mainLayout->addWidget(line2, 1); mainLayout->addLayout(tablesLayout, QSizePolicy::Preferred); mainLayout->addStretch(1); int unlocked = 0; int locked = 0; const int columns = mTable->columnCount(); for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); if (flags & CSMWorld::ColumnBase::Flag_Dialogue) { CSMWorld::ColumnBase::Display display = static_cast (mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); if (mTable->hasChildren(mTable->index(row, i)) && !(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { mNestedModels.push_back(new CSMWorld::NestedTableProxyModel ( mTable->index(row, i), display, dynamic_cast(mTable))); int idColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_Id); int typeColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType); CSMWorld::UniversalId id = CSMWorld::UniversalId( static_cast (mTable->data (mTable->index (row, typeColumn)).toInt()), mTable->data (mTable->index (row, idColumn)).toString().toUtf8().constData()); bool editable = true; bool fixedRows = false; QVariant v = mTable->index(row, i).data(); if (v.canConvert()) { assert (QString(v.typeName()) == "CSMWorld::ColumnBase::TableEditModes"); if (v.value() == CSMWorld::ColumnBase::TableEdit_None) editable = false; else if (v.value() == CSMWorld::ColumnBase::TableEdit_FixedRows) fixedRows = true; } NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this, editable, fixedRows); table->resizeColumnsToContents(); if (!editable) { table->setEditTriggers(QAbstractItemView::NoEditTriggers); table->setEnabled(false); } int rows = mTable->rowCount(mTable->index(row, i)); int rowHeight = (rows == 0) ? table->horizontalHeader()->height() : table->rowHeight(0); int tableMaxHeight = (5 * rowHeight) + table->horizontalHeader()->height() + 2 * table->frameWidth(); table->setMinimumHeight(tableMaxHeight); QLabel* label = new QLabel (mTable->headerData (i, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); if(!editable) label->setEnabled(false); tablesLayout->addWidget(label); tablesLayout->addWidget(table); connect(table, SIGNAL(editRequest(const CSMWorld::UniversalId &, const std::string &)), this, SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &))); } else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { mDispatcher->makeDelegate (display); QWidget* editor = mDispatcher->makeEditor (display, (mTable->index (row, i))); if (editor) { mWidgetMapper->addMapping (editor, i); QLabel* label = new QLabel (mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); editor->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); if (! (mTable->flags (mTable->index (row, i)) & Qt::ItemIsEditable)) { lockedLayout->addWidget (label, locked, 0); lockedLayout->addWidget (editor, locked, 1); ++locked; } else { unlockedLayout->addWidget (label, unlocked, 0); unlockedLayout->addWidget (editor, unlocked, 1); ++unlocked; } if(mTable->index(row, i).data().type() == QVariant::UserType) { editor->setEnabled(false); label->setEnabled(false); } createEditorContextMenu(editor, display, row); } } else { CSMWorld::IdTree *tree = static_cast(mTable); mNestedTableMapper = new QDataWidgetMapper (this); mNestedTableMapper->setModel(tree); // FIXME: lack MIME support? mNestedTableDispatcher = new DialogueDelegateDispatcher (0/*this*/, mTable, mCommandDispatcher, mDocument, tree); mNestedTableMapper->setRootIndex (tree->index(row, i)); mNestedTableMapper->setItemDelegate(mNestedTableDispatcher); int columnCount = tree->columnCount(tree->index(row, i)); for (int col = 0; col < columnCount; ++col) { int displayRole = tree->nestedHeaderData (i, col, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt(); CSMWorld::ColumnBase::Display display = static_cast (displayRole); mNestedTableDispatcher->makeDelegate (display); // FIXME: assumed all columns are editable QWidget* editor = mNestedTableDispatcher->makeEditor (display, tree->index (0, col, tree->index(row, i))); if (editor) { mNestedTableMapper->addMapping (editor, col); // Need to use Qt::DisplayRole in order to get the correct string // from CSMWorld::Columns QLabel* label = new QLabel (tree->nestedHeaderData (i, col, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); editor->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); unlockedLayout->addWidget (label, unlocked, 0); unlockedLayout->addWidget (editor, unlocked, 1); ++unlocked; if(tree->index(0, col, tree->index(row, i)).data().type() == QVariant::UserType) { editor->setEnabled(false); label->setEnabled(false); } createEditorContextMenu(editor, display, row); } } mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i))); } } } mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0)); if (unlocked == 0) mainLayout->removeWidget(line); this->setWidget(mMainWidget); this->setWidgetResizable(true); } QVBoxLayout& CSVWorld::SimpleDialogueSubView::getMainLayout() { return *mMainLayout; } CSMWorld::IdTable& CSVWorld::SimpleDialogueSubView::getTable() { return *mTable; } CSMWorld::CommandDispatcher& CSVWorld::SimpleDialogueSubView::getCommandDispatcher() { return mCommandDispatcher; } CSVWorld::EditWidget& CSVWorld::SimpleDialogueSubView::getEditWidget() { return *mEditWidget; } bool CSVWorld::SimpleDialogueSubView::isLocked() const { return mLocked; } CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mEditWidget(0), mMainLayout(NULL), mTable(dynamic_cast(document.getData().getTableModel(id))), mLocked(false), mDocument(document), mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())) { connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); connect(mTable, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex&, int, int))); updateCurrentId(); QWidget *mainWidget = new QWidget(this); mMainLayout = new QVBoxLayout(mainWidget); setWidget (mainWidget); int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id); mEditWidget = new EditWidget(mainWidget, mTable->getModelIndex(getUniversalId().getId(), idColumn).row(), mTable, mCommandDispatcher, document, false); mMainLayout->addWidget(mEditWidget); mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); dataChanged(mTable->getModelIndex (getUniversalId().getId(), idColumn)); connect(mEditWidget, SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)), this, SIGNAL(focusId(const CSMWorld::UniversalId &, const std::string &))); } void CSVWorld::SimpleDialogueSubView::setEditLock (bool locked) { if (!mEditWidget) // hack to indicate that getUniversalId().getId() is no longer valid return; int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id); mLocked = locked; QModelIndex currentIndex(mTable->getModelIndex(getUniversalId().getId(), idColumn)); if (currentIndex.isValid()) { CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (currentIndex.row(), 1)).toInt()); mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || locked); mCommandDispatcher.setEditLock (locked); } } void CSVWorld::SimpleDialogueSubView::dataChanged (const QModelIndex & index) { int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id); QModelIndex currentIndex(mTable->getModelIndex(getUniversalId().getId(), idColumn)); if (currentIndex.isValid() && (index.parent().isValid() ? index.parent().row() : index.row()) == currentIndex.row()) { CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (currentIndex.row(), 1)).toInt()); mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked); // Check if the changed data should force refresh (rebuild) the dialogue subview int flags = 0; if (index.parent().isValid()) // TODO: check that index is topLeft { flags = static_cast(mTable)->nestedHeaderData (index.parent().column(), index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); } else { flags = mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); } if (flags & CSMWorld::ColumnBase::Flag_Dialogue_Refresh) { int y = mEditWidget->verticalScrollBar()->value(); mEditWidget->remake (index.parent().isValid() ? index.parent().row() : index.row()); mEditWidget->verticalScrollBar()->setValue(y); } } } void CSVWorld::SimpleDialogueSubView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id); QModelIndex currentIndex(mTable->getModelIndex(getUniversalId().getId(), idColumn)); if (!currentIndex.isValid()) { return; } if (currentIndex.parent() == parent && currentIndex.row() >= start && currentIndex.row() <= end) { if(mEditWidget) { delete mEditWidget; mEditWidget = 0; } emit closeRequest(this); } } void CSVWorld::SimpleDialogueSubView::updateCurrentId() { std::vector selection; selection.push_back (getUniversalId().getId()); mCommandDispatcher.setSelection(selection); } void CSVWorld::DialogueSubView::addButtonBar() { if (mButtons) return; mButtons = new RecordButtonBar (getUniversalId(), getTable(), mBottom, &getCommandDispatcher(), this); getMainLayout().insertWidget (1, mButtons); // connections connect (mButtons, SIGNAL (showPreview()), this, SLOT (showPreview())); connect (mButtons, SIGNAL (viewRecord()), this, SLOT (viewRecord())); connect (mButtons, SIGNAL (switchToRow (int)), this, SLOT (switchToRow (int))); connect (this, SIGNAL (universalIdChanged (const CSMWorld::UniversalId&)), mButtons, SLOT (universalIdChanged (const CSMWorld::UniversalId&))); } CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting) : SimpleDialogueSubView (id, document), mButtons (0) { // bottom box mBottom = new TableBottomBox (creatorFactory, document, id, this); connect (mBottom, SIGNAL (requestFocus (const std::string&)), this, SLOT (requestFocus (const std::string&))); // layout getMainLayout().addWidget (mBottom); connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), this, SLOT (settingChanged (const CSMPrefs::Setting *))); CSMPrefs::get()["ID Dialogues"].update(); } void CSVWorld::DialogueSubView::setEditLock (bool locked) { SimpleDialogueSubView::setEditLock (locked); if (mButtons) mButtons->setEditLock (locked); } void CSVWorld::DialogueSubView::settingChanged (const CSMPrefs::Setting *setting) { if (*setting=="ID Dialogues/toolbar") { if (setting->isTrue()) { addButtonBar(); } else if (mButtons) { getMainLayout().removeWidget (mButtons); delete mButtons; mButtons = 0; } } } void CSVWorld::DialogueSubView::showPreview () { int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id); QModelIndex currentIndex (getTable().getModelIndex (getUniversalId().getId(), idColumn)); if (currentIndex.isValid() && getTable().getFeatures() & CSMWorld::IdTable::Feature_Preview && currentIndex.row() < getTable().rowCount()) { emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, getUniversalId().getId()), ""); } } void CSVWorld::DialogueSubView::viewRecord () { int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id); QModelIndex currentIndex (getTable().getModelIndex (getUniversalId().getId(), idColumn)); if (currentIndex.isValid() && currentIndex.row() < getTable().rowCount()) { std::pair params = getTable().view (currentIndex.row()); if (params.first.getType()!=CSMWorld::UniversalId::Type_None) emit focusId (params.first, params.second); } } void CSVWorld::DialogueSubView::switchToRow (int row) { int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id); std::string id = getTable().data (getTable().index (row, idColumn)).toString().toUtf8().constData(); int typeColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_RecordType); CSMWorld::UniversalId::Type type = static_cast ( getTable().data (getTable().index (row, typeColumn)).toInt()); setUniversalId (CSMWorld::UniversalId (type, id)); updateCurrentId(); getEditWidget().remake (row); int stateColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Modification); CSMWorld::RecordBase::State state = static_cast ( getTable().data (getTable().index (row, stateColumn)).toInt()); getEditWidget().setDisabled (isLocked() || state==CSMWorld::RecordBase::State_Deleted); } void CSVWorld::DialogueSubView::requestFocus (const std::string& id) { int idColumn = getTable().findColumnIndex (CSMWorld::Columns::ColumnId_Id); QModelIndex index = getTable().getModelIndex (id, idColumn); if (index.isValid()) switchToRow (index.row()); } openmw-openmw-0.38.0/apps/opencs/view/world/dialoguesubview.hpp000066400000000000000000000211121264522266000246560ustar00rootroot00000000000000#ifndef CSV_WORLD_DIALOGUESUBVIEW_H #define CSV_WORLD_DIALOGUESUBVIEW_H #include #include #include #include #include #include "../doc/subview.hpp" #include "../../model/world/columnbase.hpp" #include "../../model/world/commanddispatcher.hpp" #include "../../model/world/universalid.hpp" class QDataWidgetMapper; class QSize; class QEvent; class QLabel; class QVBoxLayout; class QMenu; namespace CSMWorld { class IdTable; class NestedTableProxyModel; } namespace CSMPrefs { class Setting; } namespace CSMDoc { class Document; } namespace CSVWorld { class CommandDelegate; class CreatorFactoryBase; class TableBottomBox; class NotEditableSubDelegate : public QAbstractItemDelegate { const CSMWorld::IdTable* mTable; public: NotEditableSubDelegate(const CSMWorld::IdTable* table, QObject * parent = 0); virtual void setEditorData (QWidget* editor, const QModelIndex& index) const; virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; virtual void paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; ///< does nothing virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const; ///< does nothing virtual QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; }; //this can't be nested into the DialogueDelegateDispatcher, because it needs to emit signals class DialogueDelegateDispatcherProxy : public QObject { Q_OBJECT class refWrapper { public: refWrapper(const QModelIndex& index); const QModelIndex& mIndex; }; QWidget* mEditor; CSMWorld::ColumnBase::Display mDisplay; std::auto_ptr mIndexWrapper; public: DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display); QWidget* getEditor() const; public slots: void editorDataCommited(); void setIndex(const QModelIndex& index); signals: void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display); }; class DialogueDelegateDispatcher : public QAbstractItemDelegate { Q_OBJECT std::map mDelegates; QObject* mParent; QAbstractItemModel* mTable; CSMWorld::CommandDispatcher& mCommandDispatcher; CSMDoc::Document& mDocument; NotEditableSubDelegate mNotEditableDelegate; std::vector mProxys; //once we move to the C++11 we should use unique_ptr public: DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, QAbstractItemModel* model = 0); ~DialogueDelegateDispatcher(); CSVWorld::CommandDelegate* makeDelegate(CSMWorld::ColumnBase::Display display); QWidget* makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index); ///< will return null if delegate is not present, parent of the widget is //same as for dispatcher itself virtual void setEditorData (QWidget* editor, const QModelIndex& index) const; virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const; virtual void paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; ///< does nothing virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const; ///< does nothing private slots: void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display); }; /// A context menu with "Edit 'ID'" action for editors in the dialogue subview class IdContextMenu : public QObject { Q_OBJECT QWidget *mWidget; CSMWorld::UniversalId::Type mIdType; std::set mExcludedIds; ///< A list of IDs that should not have the Edit 'ID' action. QMenu *mContextMenu; QAction *mEditIdAction; QString getWidgetValue() const; void addEditIdActionToMenu(const QString &text); void removeEditIdActionFromMenu(); public: IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Display display); void excludeId(const std::string &id); private slots: void showContextMenu(const QPoint &pos); void editIdRequest(); signals: void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint); }; class EditWidget : public QScrollArea { Q_OBJECT QDataWidgetMapper *mWidgetMapper; QDataWidgetMapper *mNestedTableMapper; DialogueDelegateDispatcher *mDispatcher; DialogueDelegateDispatcher *mNestedTableDispatcher; QWidget* mMainWidget; CSMWorld::IdTable* mTable; CSMWorld::CommandDispatcher& mCommandDispatcher; CSMDoc::Document& mDocument; std::vector mNestedModels; //Plain, raw C pointers, deleted in the dtor void createEditorContextMenu(QWidget *editor, CSMWorld::ColumnBase::Display display, int currentRow) const; public: EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, bool createAndDelete = false); virtual ~EditWidget(); void remake(int row); signals: void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint); }; class SimpleDialogueSubView : public CSVDoc::SubView { Q_OBJECT EditWidget* mEditWidget; QVBoxLayout* mMainLayout; CSMWorld::IdTable* mTable; bool mLocked; const CSMDoc::Document& mDocument; CSMWorld::CommandDispatcher mCommandDispatcher; protected: QVBoxLayout& getMainLayout(); CSMWorld::IdTable& getTable(); CSMWorld::CommandDispatcher& getCommandDispatcher(); EditWidget& getEditWidget(); void updateCurrentId(); bool isLocked() const; public: SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); private slots: void dataChanged(const QModelIndex & index); ///\brief we need to care for deleting currently edited record void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); }; class RecordButtonBar; class DialogueSubView : public SimpleDialogueSubView { Q_OBJECT TableBottomBox* mBottom; RecordButtonBar *mButtons; private: void addButtonBar(); public: DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting = false); virtual void setEditLock (bool locked); private slots: void settingChanged (const CSMPrefs::Setting *setting); void showPreview(); void viewRecord(); void switchToRow (int row); void requestFocus (const std::string& id); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/dragdroputils.cpp000066400000000000000000000015261264522266000243450ustar00rootroot00000000000000#include "dragdroputils.hpp" #include #include "../../model/world/tablemimedata.hpp" const CSMWorld::TableMimeData *CSVWorld::DragDropUtils::getTableMimeData(const QDropEvent &event) { return dynamic_cast(event.mimeData()); } bool CSVWorld::DragDropUtils::canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type) { const CSMWorld::TableMimeData *data = getTableMimeData(event); return data != NULL && data->holdsType(type); } CSMWorld::UniversalId CSVWorld::DragDropUtils::getAcceptedData(const QDropEvent &event, CSMWorld::ColumnBase::Display type) { if (canAcceptData(event, type)) { return getTableMimeData(event)->returnMatching(type); } return CSMWorld::UniversalId::Type_None; } openmw-openmw-0.38.0/apps/opencs/view/world/dragdroputils.hpp000066400000000000000000000014601264522266000243470ustar00rootroot00000000000000#ifndef CSV_WORLD_DRAGDROPUTILS_HPP #define CSV_WORLD_DRAGDROPUTILS_HPP #include "../../model/world/columnbase.hpp" class QDropEvent; namespace CSMWorld { class TableMimeData; class UniversalId; } namespace CSVWorld { namespace DragDropUtils { const CSMWorld::TableMimeData *getTableMimeData(const QDropEvent &event); bool canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type); ///< Checks whether the \a event contains a valid CSMWorld::TableMimeData that holds the \a type CSMWorld::UniversalId getAcceptedData(const QDropEvent &event, CSMWorld::ColumnBase::Display type); ///< Gets the accepted data from the \a event using the \a type ///< \return Type_None if the \a event data doesn't holds the \a type } } #endif openmw-openmw-0.38.0/apps/opencs/view/world/dragrecordtable.cpp000066400000000000000000000053671264522266000246150ustar00rootroot00000000000000#include "dragrecordtable.hpp" #include #include #include "../../model/doc/document.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commands.hpp" #include "dragdroputils.hpp" void CSVWorld::DragRecordTable::startDragFromTable (const CSVWorld::DragRecordTable& table) { std::vector records = table.getDraggedRecords(); if (records.empty()) { return; } CSMWorld::TableMimeData* mime = new CSMWorld::TableMimeData (records, mDocument); if (mime) { QDrag* drag = new QDrag (this); drag->setMimeData (mime); drag->setPixmap (QString::fromUtf8 (mime->getIcon().c_str())); drag->exec (Qt::CopyAction); } } CSVWorld::DragRecordTable::DragRecordTable (CSMDoc::Document& document, QWidget* parent) : QTableView(parent), mDocument(document), mEditLock(false) { setAcceptDrops(true); } void CSVWorld::DragRecordTable::setEditLock (bool locked) { mEditLock = locked; } void CSVWorld::DragRecordTable::dragEnterEvent(QDragEnterEvent *event) { event->acceptProposedAction(); } void CSVWorld::DragRecordTable::dragMoveEvent(QDragMoveEvent *event) { QModelIndex index = indexAt(event->pos()); if (CSVWorld::DragDropUtils::canAcceptData(*event, getIndexDisplayType(index))) { if (index.flags() & Qt::ItemIsEditable) { event->accept(); return; } } event->ignore(); } void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event) { QModelIndex index = indexAt(event->pos()); CSMWorld::ColumnBase::Display display = getIndexDisplayType(index); if (CSVWorld::DragDropUtils::canAcceptData(*event, display)) { const CSMWorld::TableMimeData *data = CSVWorld::DragDropUtils::getTableMimeData(*event); if (data->fromDocument(mDocument)) { CSMWorld::UniversalId id = CSVWorld::DragDropUtils::getAcceptedData(*event, display); QVariant newIndexData = QString::fromUtf8(id.getId().c_str()); QVariant oldIndexData = index.data(Qt::EditRole); if (newIndexData != oldIndexData) { mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*model(), index, newIndexData)); } } } } CSMWorld::ColumnBase::Display CSVWorld::DragRecordTable::getIndexDisplayType(const QModelIndex &index) const { Q_ASSERT(model() != NULL); if (index.isValid()) { QVariant display = model()->headerData(index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display); if (display.isValid()) { return static_cast(display.toInt()); } } return CSMWorld::ColumnBase::Display_None; } openmw-openmw-0.38.0/apps/opencs/view/world/dragrecordtable.hpp000066400000000000000000000020311264522266000246030ustar00rootroot00000000000000#ifndef CSV_WORLD_DRAGRECORDTABLE_H #define CSV_WORLD_DRAGRECORDTABLE_H #include #include #include "../../model/world/columnbase.hpp" class QWidget; class QAction; namespace CSMDoc { class Document; } namespace CSMWorld { class UniversalId; } namespace CSVWorld { class DragRecordTable : public QTableView { protected: CSMDoc::Document& mDocument; bool mEditLock; public: DragRecordTable(CSMDoc::Document& document, QWidget* parent = NULL); virtual std::vector getDraggedRecords() const = 0; void setEditLock(bool locked); protected: void startDragFromTable(const DragRecordTable& table); void dragEnterEvent(QDragEnterEvent *event); void dragMoveEvent(QDragMoveEvent *event); void dropEvent(QDropEvent *event); private: CSMWorld::ColumnBase::Display getIndexDisplayType(const QModelIndex &index) const; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/enumdelegate.cpp000066400000000000000000000124071264522266000241210ustar00rootroot00000000000000#include "enumdelegate.hpp" #include #include #include #include #include #include "../../model/world/commands.hpp" int CSVWorld::EnumDelegate::getValueIndex(const QModelIndex &index, int role) const { if (index.isValid() && index.data(role).isValid()) { int value = index.data(role).toInt(); int size = static_cast(mValues.size()); for (int i = 0; i < size; ++i) { if (value == mValues.at(i).first) { return i; } } } return -1; } void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { if (QComboBox *comboBox = dynamic_cast (editor)) { QString value = comboBox->currentText(); for (std::vector >::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) if (iter->second==value) { // do nothing if the value has not changed if (model->data(index).toInt() != iter->first) addCommands (model, index, iter->first); break; } } } void CSVWorld::EnumDelegate::addCommands (QAbstractItemModel *model, const QModelIndex& index, int type) const { getUndoStack().push (new CSMWorld::ModifyCommand (*model, index, type)); } CSVWorld::EnumDelegate::EnumDelegate (const std::vector >& values, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) : CommandDelegate (dispatcher, document, parent), mValues (values) { } QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_None); } QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid()) return 0; QComboBox *comboBox = new QComboBox (parent); for (std::vector >::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) comboBox->addItem (iter->second); return comboBox; } void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { if (QComboBox *comboBox = dynamic_cast(editor)) { int role = Qt::EditRole; if (tryDisplay && !index.data(role).isValid()) { role = Qt::DisplayRole; if (!index.data(role).isValid()) { return; } } int valueIndex = getValueIndex(index, role); if (valueIndex != -1) { comboBox->setCurrentIndex(valueIndex); } } } void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { int valueIndex = getValueIndex(index); if (valueIndex != -1) { QStyleOptionViewItemV4 itemOption(option); itemOption.text = mValues.at(valueIndex).second; QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter); } } QSize CSVWorld::EnumDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { int valueIndex = getValueIndex(index); if (valueIndex != -1) { // Calculate the size hint as for a combobox. // So, the whole text is visible (isn't elided) when the editor is created QStyleOptionComboBox itemOption; itemOption.fontMetrics = option.fontMetrics; itemOption.palette = option.palette; itemOption.rect = option.rect; itemOption.state = option.state; const QString &valueText = mValues.at(valueIndex).second; QSize valueSize = QSize(itemOption.fontMetrics.width(valueText), itemOption.fontMetrics.height()); itemOption.currentText = valueText; return QApplication::style()->sizeFromContents(QStyle::CT_ComboBox, &itemOption, valueSize); } return option.rect.size(); } CSVWorld::EnumDelegateFactory::EnumDelegateFactory() {} CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names, bool allowNone) { assert (names); if (allowNone) add (-1, ""); for (int i=0; names[i]; ++i) add (i, names[i]); } CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector& names, bool allowNone) { if (allowNone) add (-1, ""); int size = static_cast (names.size()); for (int i=0; i #include #include #include #include "util.hpp" namespace CSVWorld { /// \brief Integer value that represents an enum and is interacted with via a combobox class EnumDelegate : public CommandDelegate { protected: std::vector > mValues; int getValueIndex(const QModelIndex &index, int role = Qt::DisplayRole) const; private: virtual void setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; virtual void addCommands (QAbstractItemModel *model, const QModelIndex& index, int type) const; public: EnumDelegate (const std::vector >& values, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index, CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const; virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; virtual void paint (QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; }; class EnumDelegateFactory : public CommandDelegateFactory { protected: std::vector > mValues; public: EnumDelegateFactory(); EnumDelegateFactory (const char **names, bool allowNone = false); ///< \param names Array of char pointer with a 0-pointer as end mark /// \param allowNone Use value of -1 for "none selected" (empty string) EnumDelegateFactory (const std::vector& names, bool allowNone = false); /// \param allowNone Use value of -1 for "none selected" (empty string) virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. void add (int value, const QString& name); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/extendedcommandconfigurator.cpp000066400000000000000000000161171264522266000272460ustar00rootroot00000000000000#include "extendedcommandconfigurator.hpp" #include #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/commanddispatcher.hpp" #include "../../model/world/data.hpp" CSVWorld::ExtendedCommandConfigurator::ExtendedCommandConfigurator(CSMDoc::Document &document, const CSMWorld::UniversalId &id, QWidget *parent) : QWidget(parent), mNumUsedCheckBoxes(0), mNumChecked(0), mMode(Mode_None), mData(document.getData()), mEditLock(false) { mCommandDispatcher = new CSMWorld::CommandDispatcher(document, id, this); connect(&mData, SIGNAL(idListChanged()), this, SLOT(dataIdListChanged())); mPerformButton = new QPushButton(this); mPerformButton->setDefault(true); mPerformButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(mPerformButton, SIGNAL(clicked(bool)), this, SLOT(performExtendedCommand())); mCancelButton = new QPushButton("Cancel", this); mCancelButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(mCancelButton, SIGNAL(clicked(bool)), this, SIGNAL(done())); mTypeGroup = new QGroupBox(this); QGridLayout *groupLayout = new QGridLayout(mTypeGroup); groupLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); mTypeGroup->setLayout(groupLayout); QHBoxLayout *mainLayout = new QHBoxLayout(this); mainLayout->setSizeConstraint(QLayout::SetNoConstraint); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(mTypeGroup); mainLayout->addWidget(mPerformButton); mainLayout->addWidget(mCancelButton); } void CSVWorld::ExtendedCommandConfigurator::configure(CSVWorld::ExtendedCommandConfigurator::Mode mode, const std::vector &selectedIds) { mMode = mode; if (mMode != Mode_None) { mPerformButton->setText((mMode == Mode_Delete) ? "Extended Delete" : "Extended Revert"); mSelectedIds = selectedIds; mCommandDispatcher->setSelection(mSelectedIds); setupCheckBoxes(mCommandDispatcher->getExtendedTypes()); setupGroupLayout(); lockWidgets(mEditLock); } } void CSVWorld::ExtendedCommandConfigurator::setEditLock(bool locked) { if (mEditLock != locked) { mEditLock = locked; lockWidgets(mEditLock); } } void CSVWorld::ExtendedCommandConfigurator::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); setupGroupLayout(); } void CSVWorld::ExtendedCommandConfigurator::setupGroupLayout() { if (mMode == Mode_None) { return; } int groupWidth = mTypeGroup->geometry().width(); QGridLayout *layout = qobject_cast(mTypeGroup->layout()); // Find the optimal number of rows to place the checkboxes within the available space int divider = 1; do { while (layout->itemAt(0) != NULL) { layout->removeItem(layout->itemAt(0)); } int counter = 0; int itemsPerRow = mNumUsedCheckBoxes / divider; CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin(); CheckBoxMap::const_iterator end = mTypeCheckBoxes.end(); for (; current != end; ++current) { if (counter < mNumUsedCheckBoxes) { int row = counter / itemsPerRow; int column = counter - (counter / itemsPerRow) * itemsPerRow; layout->addWidget(current->first, row, column); } ++counter; } divider *= 2; } while (groupWidth < mTypeGroup->sizeHint().width() && divider <= mNumUsedCheckBoxes); } void CSVWorld::ExtendedCommandConfigurator::setupCheckBoxes(const std::vector &types) { // Make sure that we have enough checkboxes int numTypes = static_cast(types.size()); int numCheckBoxes = static_cast(mTypeCheckBoxes.size()); if (numTypes > numCheckBoxes) { for (int i = numTypes - numCheckBoxes; i > 0; --i) { QCheckBox *checkBox = new QCheckBox(mTypeGroup); connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxStateChanged(int))); mTypeCheckBoxes.insert(std::make_pair(checkBox, CSMWorld::UniversalId::Type_None)); } } // Set up the checkboxes int counter = 0; CheckBoxMap::iterator current = mTypeCheckBoxes.begin(); CheckBoxMap::iterator end = mTypeCheckBoxes.end(); for (; current != end; ++current) { if (counter < numTypes) { CSMWorld::UniversalId type = types[counter]; current->first->setText(QString::fromUtf8(type.getTypeName().c_str())); current->first->setChecked(true); current->second = type; ++counter; } else { current->first->hide(); } } mNumChecked = mNumUsedCheckBoxes = numTypes; } void CSVWorld::ExtendedCommandConfigurator::lockWidgets(bool locked) { mPerformButton->setEnabled(!mEditLock && mNumChecked > 0); CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin(); CheckBoxMap::const_iterator end = mTypeCheckBoxes.end(); for (int i = 0; current != end && i < mNumUsedCheckBoxes; ++current, ++i) { current->first->setEnabled(!mEditLock); } } void CSVWorld::ExtendedCommandConfigurator::performExtendedCommand() { std::vector types; CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin(); CheckBoxMap::const_iterator end = mTypeCheckBoxes.end(); for (; current != end; ++current) { if (current->first->isChecked()) { types.push_back(current->second); } } mCommandDispatcher->setExtendedTypes(types); if (mMode == Mode_Delete) { mCommandDispatcher->executeExtendedDelete(); } else { mCommandDispatcher->executeExtendedRevert(); } emit done(); } void CSVWorld::ExtendedCommandConfigurator::checkBoxStateChanged(int state) { switch (state) { case Qt::Unchecked: --mNumChecked; break; case Qt::Checked: ++mNumChecked; break; case Qt::PartiallyChecked: // Not used break; } mPerformButton->setEnabled(mNumChecked > 0); } void CSVWorld::ExtendedCommandConfigurator::dataIdListChanged() { bool idsRemoved = false; for (int i = 0; i < static_cast(mSelectedIds.size()); ++i) { if (!mData.hasId(mSelectedIds[i])) { std::swap(mSelectedIds[i], mSelectedIds.back()); mSelectedIds.pop_back(); idsRemoved = true; --i; } } // If all selected IDs were removed, cancel the configurator if (mSelectedIds.empty()) { emit done(); return; } if (idsRemoved) { mCommandDispatcher->setSelection(mSelectedIds); } } openmw-openmw-0.38.0/apps/opencs/view/world/extendedcommandconfigurator.hpp000066400000000000000000000035641264522266000272550ustar00rootroot00000000000000#ifndef CSVWORLD_EXTENDEDCOMMANDCONFIGURATOR_HPP #define CSVWORLD_EXTENDEDCOMMANDCONFIGURATOR_HPP #include #include #include "../../model/world/universalid.hpp" class QPushButton; class QGroupBox; class QCheckBox; class QLabel; class QHBoxLayout; namespace CSMDoc { class Document; } namespace CSMWorld { class CommandDispatcher; class Data; } namespace CSVWorld { class ExtendedCommandConfigurator : public QWidget { Q_OBJECT public: enum Mode { Mode_None, Mode_Delete, Mode_Revert }; private: typedef std::map CheckBoxMap; QPushButton *mPerformButton; QPushButton *mCancelButton; QGroupBox *mTypeGroup; CheckBoxMap mTypeCheckBoxes; int mNumUsedCheckBoxes; int mNumChecked; Mode mMode; CSMWorld::CommandDispatcher *mCommandDispatcher; CSMWorld::Data &mData; std::vector mSelectedIds; bool mEditLock; void setupGroupLayout(); void setupCheckBoxes(const std::vector &types); void lockWidgets(bool locked); public: ExtendedCommandConfigurator(CSMDoc::Document &document, const CSMWorld::UniversalId &id, QWidget *parent = 0); void configure(Mode mode, const std::vector &selectedIds); void setEditLock(bool locked); protected: virtual void resizeEvent(QResizeEvent *event); private slots: void performExtendedCommand(); void checkBoxStateChanged(int state); void dataIdListChanged(); signals: void done(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/genericcreator.cpp000066400000000000000000000204651264522266000244610ustar00rootroot00000000000000#include "genericcreator.hpp" #include #include #include #include #include #include #include #include #include "../../model/world/commands.hpp" #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" #include "idvalidator.hpp" void CSVWorld::GenericCreator::update() { mErrors = getErrors(); mCreate->setToolTip (QString::fromUtf8 (mErrors.c_str())); mId->setToolTip (QString::fromUtf8 (mErrors.c_str())); mCreate->setEnabled (mErrors.empty() && !mLocked); } void CSVWorld::GenericCreator::setManualEditing (bool enabled) { mId->setVisible (enabled); } void CSVWorld::GenericCreator::insertAtBeginning (QWidget *widget, bool stretched) { mLayout->insertWidget (0, widget, stretched ? 1 : 0); } void CSVWorld::GenericCreator::insertBeforeButtons (QWidget *widget, bool stretched) { mLayout->insertWidget (mLayout->count()-2, widget, stretched ? 1 : 0); } std::string CSVWorld::GenericCreator::getId() const { return mId->text().toUtf8().constData(); } std::string CSVWorld::GenericCreator::getIdValidatorResult() const { std::string errors; if (!mId->hasAcceptableInput()) errors = mValidator->getError(); return errors; } void CSVWorld::GenericCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const {} void CSVWorld::GenericCreator::pushCommand (std::auto_ptr command, const std::string& id) { mUndoStack.push (command.release()); } CSMWorld::Data& CSVWorld::GenericCreator::getData() const { return mData; } QUndoStack& CSVWorld::GenericCreator::getUndoStack() { return mUndoStack; } const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const { return mListId; } std::string CSVWorld::GenericCreator::getNamespace() const { CSMWorld::Scope scope = CSMWorld::Scope_Content; if (mScope) { scope = static_cast (mScope->itemData (mScope->currentIndex()).toInt()); } else { if (mScopes & CSMWorld::Scope_Project) scope = CSMWorld::Scope_Project; else if (mScopes & CSMWorld::Scope_Session) scope = CSMWorld::Scope_Session; } switch (scope) { case CSMWorld::Scope_Content: return ""; case CSMWorld::Scope_Project: return "project::"; case CSMWorld::Scope_Session: return "session::"; } return ""; } void CSVWorld::GenericCreator::updateNamespace() { std::string namespace_ = getNamespace(); mValidator->setNamespace (namespace_); int index = mId->text().indexOf ("::"); if (index==-1) { // no namespace in old text mId->setText (QString::fromUtf8 (namespace_.c_str()) + mId->text()); } else { std::string oldNamespace = Misc::StringUtils::lowerCase (mId->text().left (index).toUtf8().constData()); if (oldNamespace=="project" || oldNamespace=="session") mId->setText (QString::fromUtf8 (namespace_.c_str()) + mId->text().mid (index+2)); } } void CSVWorld::GenericCreator::addScope (const QString& name, CSMWorld::Scope scope, const QString& tooltip) { mScope->addItem (name, static_cast (scope)); mScope->setItemData (mScope->count()-1, tooltip, Qt::ToolTipRole); } CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, bool relaxedIdRules) : mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mClonedType (CSMWorld::UniversalId::Type_None), mScopes (CSMWorld::Scope_Content), mScope (0), mScopeLabel (0), mCloneMode (false) { // If the collection ID has a parent type, use it instead. // It will change IDs with Record/SubRecord class (used for creators in Dialogue subviews) // to IDs with general RecordList class (used for creators in Table subviews). CSMWorld::UniversalId::Type listParentType = CSMWorld::UniversalId::getParentType(mListId.getType()); if (listParentType != CSMWorld::UniversalId::Type_None) { mListId = listParentType; } mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); mId = new QLineEdit; mId->setValidator (mValidator = new IdValidator (relaxedIdRules, this)); mLayout->addWidget (mId, 1); mCreate = new QPushButton ("Create"); mLayout->addWidget (mCreate); QPushButton *cancelButton = new QPushButton ("Cancel"); mLayout->addWidget (cancelButton); setLayout (mLayout); connect (cancelButton, SIGNAL (clicked (bool)), this, SIGNAL (done())); connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create())); connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); connect (&mData, SIGNAL (idListChanged()), this, SLOT (dataIdListChanged())); } void CSVWorld::GenericCreator::setEditLock (bool locked) { mLocked = locked; update(); } void CSVWorld::GenericCreator::reset() { mCloneMode = false; mId->setText (""); update(); updateNamespace(); } std::string CSVWorld::GenericCreator::getErrors() const { std::string errors; if (!mId->hasAcceptableInput()) errors = mValidator->getError(); else if (mData.hasId (getId())) errors = "ID is already in use"; return errors; } void CSVWorld::GenericCreator::textChanged (const QString& text) { update(); } void CSVWorld::GenericCreator::create() { if (!mLocked) { std::string id = getId(); std::auto_ptr command; if (mCloneMode) { command.reset (new CSMWorld::CloneCommand ( dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType)); } else { command.reset (new CSMWorld::CreateCommand ( dynamic_cast (*mData.getTableModel (mListId)), id)); } configureCreateCommand (*command); pushCommand (command, id); emit done(); emit requestFocus(id); } } void CSVWorld::GenericCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) { mCloneMode = true; mClonedId = originId; mClonedType = type; } void CSVWorld::GenericCreator::toggleWidgets(bool active) { } void CSVWorld::GenericCreator::focus() { mId->setFocus(); } void CSVWorld::GenericCreator::setScope (unsigned int scope) { mScopes = scope; int count = (mScopes & CSMWorld::Scope_Content) + (mScopes & CSMWorld::Scope_Project) + (mScopes & CSMWorld::Scope_Session); // scope selector widget if (count>1) { mScope = new QComboBox (this); insertAtBeginning (mScope, false); if (mScopes & CSMWorld::Scope_Content) addScope ("Content", CSMWorld::Scope_Content, "Record will be stored in the currently edited content file."); if (mScopes & CSMWorld::Scope_Project) addScope ("Project", CSMWorld::Scope_Project, "Record will be stored in a local project file.

" "Record will be created in the reserved namespace \"project\".

" "Record is available when running OpenMW via OpenCS."); if (mScopes & CSMWorld::Scope_Session) addScope ("Session", CSMWorld::Scope_Session, "Record exists only for the duration of the current editing session.

" "Record will be created in the reserved namespace \"session\".

" "Record is not available when running OpenMW via OpenCS."); connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (scopeChanged (int))); mScopeLabel = new QLabel ("Scope", this); insertAtBeginning (mScopeLabel, false); mScope->setCurrentIndex (0); } else { delete mScope; mScope = 0; delete mScopeLabel; mScopeLabel = 0; } updateNamespace(); } void CSVWorld::GenericCreator::scopeChanged (int index) { update(); updateNamespace(); } void CSVWorld::GenericCreator::dataIdListChanged() { // If the original ID of cloned record was removed, cancel the creator if (mCloneMode && !mData.hasId(mClonedId)) { emit done(); } } openmw-openmw-0.38.0/apps/opencs/view/world/genericcreator.hpp000066400000000000000000000062311264522266000244610ustar00rootroot00000000000000#ifndef CSV_WORLD_GENERICCREATOR_H #define CSV_WORLD_GENERICCREATOR_H #include #include "../../model/world/universalid.hpp" #include "creator.hpp" class QString; class QPushButton; class QLineEdit; class QHBoxLayout; class QComboBox; class QLabel; class QUndoStack; namespace CSMWorld { class CreateCommand; class Data; } namespace CSVWorld { class IdValidator; class GenericCreator : public Creator { Q_OBJECT CSMWorld::Data& mData; QUndoStack& mUndoStack; CSMWorld::UniversalId mListId; QPushButton *mCreate; QLineEdit *mId; std::string mErrors; QHBoxLayout *mLayout; bool mLocked; std::string mClonedId; CSMWorld::UniversalId::Type mClonedType; unsigned int mScopes; QComboBox *mScope; QLabel *mScopeLabel; IdValidator *mValidator; protected: bool mCloneMode; protected: void update(); virtual void setManualEditing (bool enabled); ///< Enable/disable manual ID editing (enabled by default). void insertAtBeginning (QWidget *widget, bool stretched); void insertBeforeButtons (QWidget *widget, bool stretched); virtual std::string getId() const; virtual std::string getIdValidatorResult() const; /// Allow subclasses to add additional data to \a command. virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; /// Allow subclasses to wrap the create command together with additional commands /// into a macro. virtual void pushCommand (std::auto_ptr command, const std::string& id); CSMWorld::Data& getData() const; QUndoStack& getUndoStack(); const CSMWorld::UniversalId& getCollectionId() const; std::string getNamespace() const; private: void updateNamespace(); void addScope (const QString& name, CSMWorld::Scope scope, const QString& tooltip); public: GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, bool relaxedIdRules = false); virtual void setEditLock (bool locked); virtual void reset(); virtual void toggleWidgets (bool active = true); virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. virtual void setScope (unsigned int scope); /// Focus main input widget virtual void focus(); private slots: void textChanged (const QString& text); void create(); void scopeChanged (int index); void dataIdListChanged(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/idcompletiondelegate.cpp000066400000000000000000000035551264522266000256470ustar00rootroot00000000000000#include "idcompletiondelegate.hpp" #include "../../model/world/idcompletionmanager.hpp" #include "../widget/droplineedit.hpp" CSVWorld::IdCompletionDelegate::IdCompletionDelegate(CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) : CommandDelegate(dispatcher, document, parent) {} QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { return createEditor(parent, option, index, getDisplayTypeFromIndex(index)); } QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index, CSMWorld::ColumnBase::Display display) const { if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid()) { return NULL; } CSMWorld::IdCompletionManager &completionManager = getDocument().getIdCompletionManager(); CSVWidget::DropLineEdit *editor = new CSVWidget::DropLineEdit(display, parent); editor->setCompleter(completionManager.getCompleter(display).get()); return editor; } CSVWorld::CommandDelegate *CSVWorld::IdCompletionDelegateFactory::makeDelegate(CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { return new IdCompletionDelegate(dispatcher, document, parent); } openmw-openmw-0.38.0/apps/opencs/view/world/idcompletiondelegate.hpp000066400000000000000000000026121264522266000256450ustar00rootroot00000000000000#ifndef CSV_WORLD_IDCOMPLETIONDELEGATE_HPP #define CSV_WORLD_IDCOMPLETIONDELEGATE_HPP #include "util.hpp" namespace CSVWorld { /// \brief Enables the Id completion for a column class IdCompletionDelegate : public CommandDelegate { public: IdCompletionDelegate(CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); virtual QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index, CSMWorld::ColumnBase::Display display) const; }; class IdCompletionDelegateFactory : public CommandDelegateFactory { public: virtual CommandDelegate *makeDelegate(CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/idtypedelegate.cpp000077500000000000000000000020221264522266000244460ustar00rootroot00000000000000#include "idtypedelegate.hpp" #include "../../model/world/universalid.hpp" CSVWorld::IdTypeDelegate::IdTypeDelegate (const ValueList &values, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) : DataDisplayDelegate (values, icons, dispatcher, document, "Records", "type-format", parent) {} CSVWorld::IdTypeDelegateFactory::IdTypeDelegateFactory() { for (int i=0; i (i)); DataDisplayDelegateFactory::add (id.getType(), QString::fromUtf8 (id.getTypeName().c_str()), QString::fromUtf8 (id.getIcon().c_str())); } } CSVWorld::CommandDelegate *CSVWorld::IdTypeDelegateFactory::makeDelegate ( CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { return new IdTypeDelegate (mValues, mIcons, dispatcher, document, parent); } openmw-openmw-0.38.0/apps/opencs/view/world/idtypedelegate.hpp000077500000000000000000000015351264522266000244630ustar00rootroot00000000000000#ifndef IDTYPEDELEGATE_HPP #define IDTYPEDELEGATE_HPP #include "enumdelegate.hpp" #include "util.hpp" #include "../../model/world/universalid.hpp" #include "datadisplaydelegate.hpp" namespace CSVWorld { class IdTypeDelegate : public DataDisplayDelegate { public: IdTypeDelegate (const ValueList &mValues, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); }; class IdTypeDelegateFactory : public DataDisplayDelegateFactory { public: IdTypeDelegateFactory(); virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; } #endif // REFIDTYPEDELEGATE_HPP openmw-openmw-0.38.0/apps/opencs/view/world/idvalidator.cpp000066400000000000000000000060201264522266000237560ustar00rootroot00000000000000#include "idvalidator.hpp" #include bool CSVWorld::IdValidator::isValid (const QChar& c, bool first) const { if (c.isLetter() || c=='_') return true; if (!first && (c.isDigit() || c.isSpace())) return true; return false; } CSVWorld::IdValidator::IdValidator (bool relaxed, QObject *parent) : QValidator (parent), mRelaxed (relaxed) {} QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) const { mError.clear(); if (mRelaxed) { if (input.indexOf ('"')!=-1 || input.indexOf ("::")!=-1 || input.indexOf ("#")!=-1) return QValidator::Invalid; } else { if (input.isEmpty()) { mError = "Missing ID"; return QValidator::Intermediate; } bool first = true; bool scope = false; bool prevScope = false; QString::const_iterator iter = input.begin(); if (!mNamespace.empty()) { std::string namespace_ = input.left (mNamespace.size()).toUtf8().constData(); if (Misc::StringUtils::lowerCase (namespace_)!=mNamespace) return QValidator::Invalid; // incorrect namespace iter += namespace_.size(); first = false; prevScope = true; } else { int index = input.indexOf (":"); if (index!=-1) { QString namespace_ = input.left (index); if (namespace_=="project" || namespace_=="session") return QValidator::Invalid; // reserved namespace } } for (; iter!=input.end(); ++iter, first = false) { if (*iter==':') { if (first) return QValidator::Invalid; // scope operator at the beginning if (scope) { scope = false; prevScope = true; } else { if (prevScope) return QValidator::Invalid; // sequence of two scope operators scope = true; } } else if (scope) return QValidator::Invalid; // incomplete scope operator else { prevScope = false; if (!isValid (*iter, first)) return QValidator::Invalid; } } if (scope) { mError = "ID ending with incomplete scope operator"; return QValidator::Intermediate; } if (prevScope) { mError = "ID ending with scope operator"; return QValidator::Intermediate; } } return QValidator::Acceptable; } void CSVWorld::IdValidator::setNamespace (const std::string& namespace_) { mNamespace = Misc::StringUtils::lowerCase (namespace_); } std::string CSVWorld::IdValidator::getError() const { return mError; } openmw-openmw-0.38.0/apps/opencs/view/world/idvalidator.hpp000066400000000000000000000020111264522266000237570ustar00rootroot00000000000000#ifndef CSV_WORLD_IDVALIDATOR_H #define CSV_WORLD_IDVALIDATOR_H #include #include namespace CSVWorld { class IdValidator : public QValidator { bool mRelaxed; std::string mNamespace; mutable std::string mError; private: bool isValid (const QChar& c, bool first) const; public: IdValidator (bool relaxed = false, QObject *parent = 0); ///< \param relaxed Relaxed rules for IDs that also functino as user visible text virtual State validate (QString& input, int& pos) const; void setNamespace (const std::string& namespace_); /// Return a description of the error that resulted in the last call of validate /// returning QValidator::Intermediate. If the last call to validate returned /// a different value (or if there was no such call yet), an empty string is /// returned. std::string getError() const; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/infocreator.cpp000066400000000000000000000072661264522266000240040ustar00rootroot00000000000000#include "infocreator.hpp" #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/idcompletionmanager.hpp" #include "../widget/droplineedit.hpp" std::string CSVWorld::InfoCreator::getId() const { std::string id = Misc::StringUtils::lowerCase (mTopic->text().toUtf8().constData()); std::string unique = QUuid::createUuid().toByteArray().data(); unique.erase (std::remove (unique.begin(), unique.end(), '-'), unique.end()); unique = unique.substr (1, unique.size()-2); return id + '#' + unique; } void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const { int index = dynamic_cast (*getData().getTableModel (getCollectionId())). findColumnIndex ( getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ? CSMWorld::Columns::ColumnId_Topic : CSMWorld::Columns::ColumnId_Journal); command.addValue (index, mTopic->text()); } CSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager& completionManager) : GenericCreator (data, undoStack, id) { QLabel *label = new QLabel ("Topic", this); insertBeforeButtons (label, false); CSMWorld::ColumnBase::Display displayType = CSMWorld::ColumnBase::Display_Topic; if (getCollectionId().getType() == CSMWorld::UniversalId::Type_JournalInfos) { displayType = CSMWorld::ColumnBase::Display_Journal; } mTopic = new CSVWidget::DropLineEdit(displayType, this); mTopic->setCompleter(completionManager.getCompleter(displayType).get()); insertBeforeButtons (mTopic, true); setManualEditing (false); connect (mTopic, SIGNAL (textChanged (const QString&)), this, SLOT (topicChanged())); } void CSVWorld::InfoCreator::cloneMode (const std::string& originId, const CSMWorld::UniversalId::Type type) { CSMWorld::IdTable& infoTable = dynamic_cast (*getData().getTableModel (getCollectionId())); int topicColumn = infoTable.findColumnIndex ( getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ? CSMWorld::Columns::ColumnId_Topic : CSMWorld::Columns::ColumnId_Journal); mTopic->setText ( infoTable.data (infoTable.getModelIndex (originId, topicColumn)).toString()); GenericCreator::cloneMode (originId, type); } void CSVWorld::InfoCreator::reset() { mTopic->setText (""); GenericCreator::reset(); } std::string CSVWorld::InfoCreator::getErrors() const { // We ignore errors from GenericCreator here, because they can never happen in an InfoCreator. std::string errors; std::string topic = mTopic->text().toUtf8().constData(); if ((getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ? getData().getTopics() : getData().getJournals()).searchId (topic)==-1) { errors += "Invalid Topic ID"; } return errors; } void CSVWorld::InfoCreator::focus() { mTopic->setFocus(); } void CSVWorld::InfoCreator::topicChanged() { update(); } CSVWorld::Creator *CSVWorld::InfoCreatorFactory::makeCreator(CSMDoc::Document& document, const CSMWorld::UniversalId& id) const { return new InfoCreator(document.getData(), document.getUndoStack(), id, document.getIdCompletionManager()); } openmw-openmw-0.38.0/apps/opencs/view/world/infocreator.hpp000066400000000000000000000027561264522266000240100ustar00rootroot00000000000000#ifndef CSV_WORLD_INFOCREATOR_H #define CSV_WORLD_INFOCREATOR_H #include "genericcreator.hpp" namespace CSMWorld { class InfoCollection; class IdCompletionManager; } namespace CSVWidget { class DropLineEdit; } namespace CSVWorld { class InfoCreator : public GenericCreator { Q_OBJECT CSVWidget::DropLineEdit *mTopic; virtual std::string getId() const; virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; public: InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager& completionManager); virtual void cloneMode (const std::string& originId, const CSMWorld::UniversalId::Type type); virtual void reset(); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. /// Focus main input widget virtual void focus(); private slots: void topicChanged(); }; class InfoCreatorFactory : public CreatorFactoryBase { public: virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/nestedtable.cpp000066400000000000000000000107101264522266000237470ustar00rootroot00000000000000#include "nestedtable.hpp" #include #include #include #include #include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/universalid.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/commanddispatcher.hpp" #include "tableeditidaction.hpp" #include "util.hpp" CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, CSMWorld::UniversalId id, CSMWorld::NestedTableProxyModel* model, QWidget* parent, bool editable, bool fixedRows) : DragRecordTable(document, parent), mAddNewRowAction(NULL), mRemoveRowAction(NULL), mEditIdAction(NULL), mModel(model) { mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); #else horizontalHeader()->setResizeMode (QHeaderView::Interactive); #endif verticalHeader()->hide(); int columns = model->columnCount(QModelIndex()); for(int i = 0 ; i < columns; ++i) { CSMWorld::ColumnBase::Display display = static_cast ( model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate(display, mDispatcher, document, this); setItemDelegateForColumn(i, delegate); } setModel(model); if (editable) { if (!fixedRows) { mAddNewRowAction = new QAction (tr ("Add new row"), this); connect(mAddNewRowAction, SIGNAL(triggered()), this, SLOT(addNewRowActionTriggered())); mRemoveRowAction = new QAction (tr ("Remove row"), this); connect(mRemoveRowAction, SIGNAL(triggered()), this, SLOT(removeRowActionTriggered())); } mEditIdAction = new TableEditIdAction(*this, this); connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editCell())); } } std::vector CSVWorld::NestedTable::getDraggedRecords() const { // No drag support for nested tables return std::vector(); } void CSVWorld::NestedTable::contextMenuEvent (QContextMenuEvent *event) { if (!mEditIdAction) return; QModelIndexList selectedRows = selectionModel()->selectedRows(); QMenu menu(this); int currentRow = rowAt(event->y()); int currentColumn = columnAt(event->x()); if (mEditIdAction->isValidIdCell(currentRow, currentColumn)) { mEditIdAction->setCell(currentRow, currentColumn); menu.addAction(mEditIdAction); menu.addSeparator(); } if (mAddNewRowAction && mRemoveRowAction) { if (selectionModel()->selectedRows().size() == 1) menu.addAction(mRemoveRowAction); menu.addAction(mAddNewRowAction); } menu.exec (event->globalPos()); } void CSVWorld::NestedTable::removeRowActionTriggered() { mDocument.getUndoStack().push(new CSMWorld::DeleteNestedCommand(*(mModel->model()), mModel->getParentId(), selectionModel()->selectedRows().begin()->row(), mModel->getParentColumn())); } void CSVWorld::NestedTable::addNewRowActionTriggered() { mDocument.getUndoStack().push(new CSMWorld::AddNestedCommand(*(mModel->model()), mModel->getParentId(), selectionModel()->selectedRows().size(), mModel->getParentColumn())); } void CSVWorld::NestedTable::editCell() { emit editRequest(mEditIdAction->getCurrentId(), ""); } openmw-openmw-0.38.0/apps/opencs/view/world/nestedtable.hpp000066400000000000000000000024661264522266000237650ustar00rootroot00000000000000#ifndef CSV_WORLD_NESTEDTABLE_H #define CSV_WORLD_NESTEDTABLE_H #include #include "dragrecordtable.hpp" class QAction; class QContextMenuEvent; namespace CSMWorld { class NestedTableProxyModel; class UniversalId; class CommandDispatcher; } namespace CSMDoc { class Document; } namespace CSVWorld { class TableEditIdAction; class NestedTable : public DragRecordTable { Q_OBJECT QAction *mAddNewRowAction; QAction *mRemoveRowAction; TableEditIdAction *mEditIdAction; CSMWorld::NestedTableProxyModel* mModel; CSMWorld::CommandDispatcher *mDispatcher; public: NestedTable(CSMDoc::Document& document, CSMWorld::UniversalId id, CSMWorld::NestedTableProxyModel* model, QWidget* parent = NULL, bool editable = true, bool fixedRows = false); virtual std::vector getDraggedRecords() const; private: void contextMenuEvent (QContextMenuEvent *event); private slots: void removeRowActionTriggered(); void addNewRowActionTriggered(); void editCell(); signals: void editRequest(const CSMWorld::UniversalId &id, const std::string &hint); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/previewsubview.cpp000066400000000000000000000037511264522266000245520ustar00rootroot00000000000000#include "previewsubview.hpp" #include #include "../render/previewwidget.hpp" #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolmode.hpp" CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mTitle (id.toString().c_str()) { QHBoxLayout *layout = new QHBoxLayout; if (document.getData().getReferenceables().searchId (id.getId())==-1) { std::string referenceableId = document.getData().getReferences().getRecord (id.getId()).get().mRefID; referenceableIdChanged (referenceableId); mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), false, this); } else mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), true, this); CSVWidget::SceneToolbar *toolbar = new CSVWidget::SceneToolbar (48+6, this); CSVWidget::SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); toolbar->addTool (lightingTool); layout->addWidget (toolbar, 0); layout->addWidget (mScene, 1); QWidget *widget = new QWidget; widget->setLayout (layout); setWidget (widget); connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest())); connect (mScene, SIGNAL (referenceableIdChanged (const std::string&)), this, SLOT (referenceableIdChanged (const std::string&))); connect (mScene, SIGNAL (focusToolbarRequest()), toolbar, SLOT (setFocus())); connect (toolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus())); } void CSVWorld::PreviewSubView::setEditLock (bool locked) {} std::string CSVWorld::PreviewSubView::getTitle() const { return mTitle; } void CSVWorld::PreviewSubView::referenceableIdChanged (const std::string& id) { if (id.empty()) mTitle = "Preview: Reference to "; else mTitle = "Preview: Reference to " + id; setWindowTitle (QString::fromUtf8 (mTitle.c_str())); emit updateTitle(); } openmw-openmw-0.38.0/apps/opencs/view/world/previewsubview.hpp000066400000000000000000000012621264522266000245520ustar00rootroot00000000000000#ifndef CSV_WORLD_PREVIEWSUBVIEW_H #define CSV_WORLD_PREVIEWSUBVIEW_H #include "../doc/subview.hpp" namespace CSMDoc { class Document; } namespace CSVRender { class PreviewWidget; } namespace CSVWorld { class PreviewSubView : public CSVDoc::SubView { Q_OBJECT CSVRender::PreviewWidget *mScene; std::string mTitle; public: PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); virtual std::string getTitle() const; private slots: void referenceableIdChanged (const std::string& id); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/recordbuttonbar.cpp000066400000000000000000000146131264522266000246620ustar00rootroot00000000000000#include "recordbuttonbar.hpp" #include #include #include "../../model/world/idtable.hpp" #include "../../model/world/commanddispatcher.hpp" #include "../../model/prefs/state.hpp" #include "../world/tablebottombox.hpp" void CSVWorld::RecordButtonBar::updateModificationButtons() { bool createAndDeleteDisabled = !mBottom || !mBottom->canCreateAndDelete() || mLocked; mCloneButton->setDisabled (createAndDeleteDisabled); mAddButton->setDisabled (createAndDeleteDisabled); bool commandDisabled = !mCommandDispatcher || mLocked; mRevertButton->setDisabled (commandDisabled); mDeleteButton->setDisabled (commandDisabled || createAndDeleteDisabled); } void CSVWorld::RecordButtonBar::updatePrevNextButtons() { int rows = mTable.rowCount(); if (rows<=1) { mPrevButton->setDisabled (true); mNextButton->setDisabled (true); } else if (CSMPrefs::get()["General Input"]["cycle"].isTrue()) { mPrevButton->setDisabled (false); mNextButton->setDisabled (false); } else { int row = mTable.getModelIndex (mId.getId(), 0).row(); mPrevButton->setDisabled (row<=0); mNextButton->setDisabled (row>=rows-1); } } CSVWorld::RecordButtonBar::RecordButtonBar (const CSMWorld::UniversalId& id, CSMWorld::IdTable& table, TableBottomBox *bottomBox, CSMWorld::CommandDispatcher *commandDispatcher, QWidget *parent) : QWidget (parent), mId (id), mTable (table), mBottom (bottomBox), mCommandDispatcher (commandDispatcher), mLocked (false) { QHBoxLayout *buttonsLayout = new QHBoxLayout; buttonsLayout->setContentsMargins (0, 0, 0, 0); // left section mPrevButton = new QToolButton (this); mPrevButton->setIcon(QIcon(":/go-previous.png")); mPrevButton->setToolTip ("Switch to previous record"); buttonsLayout->addWidget (mPrevButton, 0); mNextButton = new QToolButton (this); mNextButton->setIcon(QIcon(":/go-next.png")); mNextButton->setToolTip ("Switch to next record"); buttonsLayout->addWidget (mNextButton, 1); buttonsLayout->addStretch(2); // optional buttons of the right section if (mTable.getFeatures() & CSMWorld::IdTable::Feature_Preview) { QToolButton* previewButton = new QToolButton (this); previewButton->setIcon(QIcon(":/edit-preview.png")); previewButton->setToolTip ("Open a preview of this record"); buttonsLayout->addWidget(previewButton); connect (previewButton, SIGNAL(clicked()), this, SIGNAL (showPreview())); } if (mTable.getFeatures() & CSMWorld::IdTable::Feature_View) { QToolButton* viewButton = new QToolButton (this); viewButton->setIcon(QIcon(":/cell.png")); viewButton->setToolTip ("Open a scene view of the cell this record is located in"); buttonsLayout->addWidget(viewButton); connect (viewButton, SIGNAL(clicked()), this, SIGNAL (viewRecord())); } // right section mCloneButton = new QToolButton (this); mCloneButton->setIcon(QIcon(":/edit-clone.png")); mCloneButton->setToolTip ("Clone record"); buttonsLayout->addWidget(mCloneButton); mAddButton = new QToolButton (this); mAddButton->setIcon(QIcon(":/add.png")); mAddButton->setToolTip ("Add new record"); buttonsLayout->addWidget(mAddButton); mDeleteButton = new QToolButton (this); mDeleteButton->setIcon(QIcon(":/edit-delete.png")); mDeleteButton->setToolTip ("Delete record"); buttonsLayout->addWidget(mDeleteButton); mRevertButton = new QToolButton (this); mRevertButton->setIcon(QIcon(":/edit-undo.png")); mRevertButton->setToolTip ("Revert record"); buttonsLayout->addWidget(mRevertButton); setLayout (buttonsLayout); // connections if(mBottom && mBottom->canCreateAndDelete()) { connect (mAddButton, SIGNAL (clicked()), mBottom, SLOT (createRequest())); connect (mCloneButton, SIGNAL (clicked()), this, SLOT (cloneRequest())); } connect (mNextButton, SIGNAL (clicked()), this, SLOT (nextId())); connect (mPrevButton, SIGNAL (clicked()), this, SLOT (prevId())); if (mCommandDispatcher) { connect (mRevertButton, SIGNAL (clicked()), mCommandDispatcher, SLOT (executeRevert())); connect (mDeleteButton, SIGNAL (clicked()), mCommandDispatcher, SLOT (executeDelete())); } connect (&mTable, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (rowNumberChanged (const QModelIndex&, int, int))); connect (&mTable, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (rowNumberChanged (const QModelIndex&, int, int))); connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), this, SLOT (settingChanged (const CSMPrefs::Setting *))); updateModificationButtons(); updatePrevNextButtons(); } void CSVWorld::RecordButtonBar::setEditLock (bool locked) { mLocked = locked; updateModificationButtons(); } void CSVWorld::RecordButtonBar::universalIdChanged (const CSMWorld::UniversalId& id) { mId = id; updatePrevNextButtons(); } void CSVWorld::RecordButtonBar::settingChanged (const CSMPrefs::Setting *setting) { if (*setting=="General Input/cycle") updatePrevNextButtons(); } void CSVWorld::RecordButtonBar::cloneRequest() { if (mBottom) { int typeColumn = mTable.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType); QModelIndex typeIndex = mTable.getModelIndex (mId.getId(), typeColumn); CSMWorld::UniversalId::Type type = static_cast ( mTable.data (typeIndex).toInt()); mBottom->cloneRequest (mId.getId(), type); } } void CSVWorld::RecordButtonBar::nextId() { int newRow = mTable.getModelIndex (mId.getId(), 0).row() + 1; if (newRow >= mTable.rowCount()) { if (CSMPrefs::get()["General Input"]["cycle"].isTrue()) newRow = 0; else return; } emit switchToRow (newRow); } void CSVWorld::RecordButtonBar::prevId() { int newRow = mTable.getModelIndex (mId.getId(), 0).row() - 1; if (newRow < 0) { if (CSMPrefs::get()["General Input"]["cycle"].isTrue()) newRow = mTable.rowCount()-1; else return; } emit switchToRow (newRow); } void CSVWorld::RecordButtonBar::rowNumberChanged (const QModelIndex& parent, int start, int end) { updatePrevNextButtons(); } openmw-openmw-0.38.0/apps/opencs/view/world/recordbuttonbar.hpp000066400000000000000000000037101264522266000246630ustar00rootroot00000000000000#ifndef CSV_WORLD_RECORDBUTTONBAR_H #define CSV_WORLD_RECORDBUTTONBAR_H #include #include "../../model/world/universalid.hpp" class QToolButton; class QModelIndex; namespace CSMWorld { class IdTable; class CommandDispatcher; } namespace CSMPrefs { class Setting; } namespace CSVWorld { class TableBottomBox; /// \brief Button bar for use in dialogue-type subviews /// /// Contains the following buttons: /// - next/prev /// - clone /// - add /// - delete /// - revert /// - preview (optional) /// - view (optional) class RecordButtonBar : public QWidget { Q_OBJECT CSMWorld::UniversalId mId; CSMWorld::IdTable& mTable; TableBottomBox *mBottom; CSMWorld::CommandDispatcher *mCommandDispatcher; QToolButton *mPrevButton; QToolButton *mNextButton; QToolButton *mCloneButton; QToolButton *mAddButton; QToolButton *mDeleteButton; QToolButton *mRevertButton; bool mLocked; private: void updateModificationButtons(); void updatePrevNextButtons(); public: RecordButtonBar (const CSMWorld::UniversalId& id, CSMWorld::IdTable& table, TableBottomBox *bottomBox = 0, CSMWorld::CommandDispatcher *commandDispatcher = 0, QWidget *parent = 0); void setEditLock (bool locked); public slots: void universalIdChanged (const CSMWorld::UniversalId& id); private slots: void settingChanged (const CSMPrefs::Setting *setting); void cloneRequest(); void nextId(); void prevId(); void rowNumberChanged (const QModelIndex& parent, int start, int end); signals: void showPreview(); void viewRecord(); void switchToRow (int row); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/recordstatusdelegate.cpp000066400000000000000000000024041264522266000256730ustar00rootroot00000000000000#include "recordstatusdelegate.hpp" #include #include #include #include "../../model/world/columns.hpp" CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values, const IconList & icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) : DataDisplayDelegate (values, icons, dispatcher, document, "Records", "status-format", parent) {} CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate ( CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { return new RecordStatusDelegate (mValues, mIcons, dispatcher, document, parent); } CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory() { std::vector enums = CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification); static const char *sIcons[] = { ":./base.png", ":./modified.png", ":./added.png", ":./removed.png", ":./removed.png", 0 }; for (int i=0; sIcons[i]; ++i) add (i, enums.at (i).c_str(), sIcons[i]); } openmw-openmw-0.38.0/apps/opencs/view/world/recordstatusdelegate.hpp000066400000000000000000000016701264522266000257040ustar00rootroot00000000000000#ifndef RECORDSTATUSDELEGATE_H #define RECORDSTATUSDELEGATE_H #include "util.hpp" #include #include #include "datadisplaydelegate.hpp" #include "../../model/world/record.hpp" class QIcon; class QFont; namespace CSVWorld { class RecordStatusDelegate : public DataDisplayDelegate { public: RecordStatusDelegate (const ValueList& values, const IconList& icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent = 0); }; class RecordStatusDelegateFactory : public DataDisplayDelegateFactory { public: RecordStatusDelegateFactory(); virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; } #endif // RECORDSTATUSDELEGATE_HPP openmw-openmw-0.38.0/apps/opencs/view/world/referenceablecreator.cpp000066400000000000000000000031731264522266000256240ustar00rootroot00000000000000#include "referenceablecreator.hpp" #include #include #include "../../model/world/universalid.hpp" #include "../../model/world/commands.hpp" void CSVWorld::ReferenceableCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const { command.setType ( static_cast (mType->itemData (mType->currentIndex()).toInt())); } CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) : GenericCreator (data, undoStack, id) { QLabel *label = new QLabel ("Type", this); insertBeforeButtons (label, false); std::vector types = CSMWorld::UniversalId::listReferenceableTypes(); mType = new QComboBox (this); for (std::vector::const_iterator iter (types.begin()); iter!=types.end(); ++iter) { CSMWorld::UniversalId id (*iter, ""); mType->addItem (QIcon (id.getIcon().c_str()), id.getTypeName().c_str(), static_cast (id.getType())); } insertBeforeButtons (mType, false); } void CSVWorld::ReferenceableCreator::reset() { mType->setCurrentIndex (0); GenericCreator::reset(); } void CSVWorld::ReferenceableCreator::cloneMode (const std::string& originId, const CSMWorld::UniversalId::Type type) { GenericCreator::cloneMode (originId, type); mType->setCurrentIndex (mType->findData (static_cast (type))); } void CSVWorld::ReferenceableCreator::toggleWidgets(bool active) { CSVWorld::GenericCreator::toggleWidgets(active); mType->setEnabled(active); } openmw-openmw-0.38.0/apps/opencs/view/world/referenceablecreator.hpp000066400000000000000000000013671264522266000256340ustar00rootroot00000000000000#ifndef CSV_WORLD_REFERENCEABLECREATOR_H #define CSV_WORLD_REFERENCEABLECREATOR_H class QComboBox; #include "genericcreator.hpp" namespace CSVWorld { class ReferenceableCreator : public GenericCreator { Q_OBJECT QComboBox *mType; private: virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; public: ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); virtual void reset(); virtual void cloneMode (const std::string& originId, const CSMWorld::UniversalId::Type type); virtual void toggleWidgets(bool active = true); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/referencecreator.cpp000066400000000000000000000117121264522266000247760ustar00rootroot00000000000000#include "referencecreator.hpp" #include #include "../../model/doc/document.hpp" #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/idcompletionmanager.hpp" #include "../widget/droplineedit.hpp" std::string CSVWorld::ReferenceCreator::getId() const { return mId; } void CSVWorld::ReferenceCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const { // Set cellID int cellIdColumn = dynamic_cast (*getData().getTableModel (getCollectionId())). findColumnIndex (CSMWorld::Columns::ColumnId_Cell); command.addValue (cellIdColumn, mCell->text()); // Set RefNum int refNumColumn = dynamic_cast ( *getData().getTableModel (CSMWorld::UniversalId::Type_References)). findColumnIndex (CSMWorld::Columns::ColumnId_RefNum); command.addValue (refNumColumn, getRefNumCount()); } void CSVWorld::ReferenceCreator::pushCommand (std::auto_ptr command, const std::string& id) { // get the old count std::string cellId = mCell->text().toUtf8().constData(); CSMWorld::IdTable& cellTable = dynamic_cast ( *getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); int countColumn = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_RefNumCounter); QModelIndex countIndex = cellTable.getModelIndex (cellId, countColumn); int count = cellTable.data (countIndex).toInt(); // command for incrementing counter std::auto_ptr increment (new CSMWorld::ModifyCommand (cellTable, countIndex, count+1)); getUndoStack().beginMacro (command->text()); GenericCreator::pushCommand (command, id); getUndoStack().push (increment.release()); getUndoStack().endMacro(); } int CSVWorld::ReferenceCreator::getRefNumCount() const { std::string cellId = mCell->text().toUtf8().constData(); CSMWorld::IdTable& cellTable = dynamic_cast ( *getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); int countColumn = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_RefNumCounter); QModelIndex countIndex = cellTable.getModelIndex (cellId, countColumn); return cellTable.data (countIndex).toInt(); } CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager &completionManager) : GenericCreator (data, undoStack, id) { QLabel *label = new QLabel ("Cell", this); insertBeforeButtons (label, false); mCell = new CSVWidget::DropLineEdit(CSMWorld::ColumnBase::Display_Cell, this); mCell->setCompleter(completionManager.getCompleter(CSMWorld::ColumnBase::Display_Cell).get()); insertBeforeButtons (mCell, true); setManualEditing (false); connect (mCell, SIGNAL (textChanged (const QString&)), this, SLOT (cellChanged())); } void CSVWorld::ReferenceCreator::reset() { GenericCreator::reset(); mCell->setText (""); mId = getData().getReferences().getNewId(); } std::string CSVWorld::ReferenceCreator::getErrors() const { // We are ignoring errors coming from GenericCreator here, because the ID of the new // record is internal and requires neither user input nor verification. std::string errors; std::string cell = mCell->text().toUtf8().constData(); if (cell.empty()) { if (!errors.empty()) errors += "
"; errors += "Missing Cell ID"; } else if (getData().getCells().searchId (cell)==-1) { if (!errors.empty()) errors += "
"; errors += "Invalid Cell ID"; } return errors; } void CSVWorld::ReferenceCreator::focus() { mCell->setFocus(); } void CSVWorld::ReferenceCreator::cellChanged() { update(); } void CSVWorld::ReferenceCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) { CSMWorld::IdTable& referenceTable = dynamic_cast ( *getData().getTableModel (CSMWorld::UniversalId::Type_References)); int cellIdColumn = referenceTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell); mCell->setText ( referenceTable.data (referenceTable.getModelIndex (originId, cellIdColumn)).toString()); CSVWorld::GenericCreator::cloneMode(originId, type); cellChanged(); //otherwise ok button will remain disabled } CSVWorld::Creator *CSVWorld::ReferenceCreatorFactory::makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const { return new ReferenceCreator(document.getData(), document.getUndoStack(), id, document.getIdCompletionManager()); } openmw-openmw-0.38.0/apps/opencs/view/world/referencecreator.hpp000066400000000000000000000032761264522266000250110ustar00rootroot00000000000000#ifndef CSV_WORLD_REFERENCECREATOR_H #define CSV_WORLD_REFERENCECREATOR_H #include "genericcreator.hpp" namespace CSMWorld { class IdCompletionManager; } namespace CSVWidget { class DropLineEdit; } namespace CSVWorld { class ReferenceCreator : public GenericCreator { Q_OBJECT CSVWidget::DropLineEdit *mCell; std::string mId; private: virtual std::string getId() const; virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; virtual void pushCommand (std::auto_ptr command, const std::string& id); int getRefNumCount() const; public: ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, CSMWorld::IdCompletionManager &completionManager); virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); virtual void reset(); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. /// Focus main input widget virtual void focus(); private slots: void cellChanged(); }; class ReferenceCreatorFactory : public CreatorFactoryBase { public: virtual Creator *makeCreator (CSMDoc::Document& document, const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/regionmap.cpp000066400000000000000000000301771264522266000234470ustar00rootroot00000000000000#include "regionmap.hpp" #include #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/regionmap.hpp" #include "../../model/world/universalid.hpp" #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/tablemimedata.hpp" void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) { QMenu menu (this); if (getUnselectedCells().size()>0) menu.addAction (mSelectAllAction); if (selectionModel()->selectedIndexes().size()>0) menu.addAction (mClearSelectionAction); if (getMissingRegionCells().size()>0) menu.addAction (mSelectRegionsAction); int selectedNonExistentCells = getSelectedCells (false, true).size(); if (selectedNonExistentCells>0) { if (selectedNonExistentCells==1) mCreateCellsAction->setText ("Create one Cell"); else { std::ostringstream stream; stream << "Create " << selectedNonExistentCells << " cells"; mCreateCellsAction->setText (QString::fromUtf8 (stream.str().c_str())); } menu.addAction (mCreateCellsAction); } if (getSelectedCells().size()>0) { if (!mRegionId.empty()) { mSetRegionAction->setText (QString::fromUtf8 (("Set Region to " + mRegionId).c_str())); menu.addAction (mSetRegionAction); } menu.addAction (mUnsetRegionAction); menu.addAction (mViewInTableAction); } if (selectionModel()->selectedIndexes().size()>0) menu.addAction (mViewAction); menu.exec (event->globalPos()); } QModelIndexList CSVWorld::RegionMap::getUnselectedCells() const { const QAbstractItemModel *model = QTableView::model(); int rows = model->rowCount(); int columns = model->columnCount(); QModelIndexList selected = selectionModel()->selectedIndexes(); std::sort (selected.begin(), selected.end()); QModelIndexList all; for (int y=0; yindex (y, x); if (model->data (index, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern)) all.push_back (index); } std::sort (all.begin(), all.end()); QModelIndexList list; std::set_difference (all.begin(), all.end(), selected.begin(), selected.end(), std::back_inserter (list)); return list; } QModelIndexList CSVWorld::RegionMap::getSelectedCells (bool existent, bool nonExistent) const { const QAbstractItemModel *model = QTableView::model(); QModelIndexList selected = selectionModel()->selectedIndexes(); QModelIndexList list; for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) { bool exists = model->data (*iter, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern); if ((exists && existent) || (!exists && nonExistent)) list.push_back (*iter); } return list; } QModelIndexList CSVWorld::RegionMap::getMissingRegionCells() const { const QAbstractItemModel *model = QTableView::model(); QModelIndexList selected = selectionModel()->selectedIndexes(); std::set regions; for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) { std::string region = model->data (*iter, CSMWorld::RegionMap::Role_Region).toString().toUtf8().constData(); if (!region.empty()) regions.insert (region); } QModelIndexList list; QModelIndexList unselected = getUnselectedCells(); for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) { std::string region = model->data (*iter, CSMWorld::RegionMap::Role_Region).toString().toUtf8().constData(); if (!region.empty() && regions.find (region)!=regions.end()) list.push_back (*iter); } return list; } void CSVWorld::RegionMap::setRegion (const std::string& regionId) { QModelIndexList selected = getSelectedCells(); QAbstractItemModel *regionModel = model(); CSMWorld::IdTable *cellsModel = &dynamic_cast (* mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); QString regionId2 = QString::fromUtf8 (regionId.c_str()); if (selected.size()>1) mDocument.getUndoStack().beginMacro (tr ("Set Region")); for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) { std::string cellId = regionModel->data (*iter, CSMWorld::RegionMap::Role_CellId). toString().toUtf8().constData(); QModelIndex index = cellsModel->getModelIndex (cellId, cellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Region)); mDocument.getUndoStack().push ( new CSMWorld::ModifyCommand (*cellsModel, index, regionId2)); } if (selected.size()>1) mDocument.getUndoStack().endMacro(); } CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId, CSMDoc::Document& document, QWidget *parent) : DragRecordTable(document, parent) { verticalHeader()->hide(); horizontalHeader()->hide(); setSelectionMode (QAbstractItemView::ExtendedSelection); setModel (document.getData().getTableModel (universalId)); resizeColumnsToContents(); resizeRowsToContents(); mSelectAllAction = new QAction (tr ("Select All"), this); connect (mSelectAllAction, SIGNAL (triggered()), this, SLOT (selectAll())); addAction (mSelectAllAction); mClearSelectionAction = new QAction (tr ("Clear Selection"), this); connect (mClearSelectionAction, SIGNAL (triggered()), this, SLOT (clearSelection())); addAction (mClearSelectionAction); mSelectRegionsAction = new QAction (tr ("Select Regions"), this); connect (mSelectRegionsAction, SIGNAL (triggered()), this, SLOT (selectRegions())); addAction (mSelectRegionsAction); mCreateCellsAction = new QAction (tr ("Create Cells Action"), this); connect (mCreateCellsAction, SIGNAL (triggered()), this, SLOT (createCells())); addAction (mCreateCellsAction); mSetRegionAction = new QAction (tr ("Set Region"), this); connect (mSetRegionAction, SIGNAL (triggered()), this, SLOT (setRegion())); addAction (mSetRegionAction); mUnsetRegionAction = new QAction (tr ("Unset Region"), this); connect (mUnsetRegionAction, SIGNAL (triggered()), this, SLOT (unsetRegion())); addAction (mUnsetRegionAction); mViewAction = new QAction (tr ("View Cells"), this); connect (mViewAction, SIGNAL (triggered()), this, SLOT (view())); addAction (mViewAction); mViewInTableAction = new QAction (tr ("View Cells in Table"), this); connect (mViewInTableAction, SIGNAL (triggered()), this, SLOT (viewInTable())); addAction (mViewInTableAction); setAcceptDrops(true); } void CSVWorld::RegionMap::selectAll() { QModelIndexList unselected = getUnselectedCells(); for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) selectionModel()->select (*iter, QItemSelectionModel::Select); } void CSVWorld::RegionMap::clearSelection() { selectionModel()->clearSelection(); } void CSVWorld::RegionMap::selectRegions() { QModelIndexList unselected = getMissingRegionCells(); for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) selectionModel()->select (*iter, QItemSelectionModel::Select); } void CSVWorld::RegionMap::createCells() { if (mEditLock) return; QModelIndexList selected = getSelectedCells (false, true); CSMWorld::IdTable *cellsModel = &dynamic_cast (* mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); if (selected.size()>1) mDocument.getUndoStack().beginMacro (tr ("Create cells")); for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) { std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). toString().toUtf8().constData(); mDocument.getUndoStack().push (new CSMWorld::CreateCommand (*cellsModel, cellId)); } if (selected.size()>1) mDocument.getUndoStack().endMacro(); } void CSVWorld::RegionMap::setRegion() { if (mEditLock) return; setRegion (mRegionId); } void CSVWorld::RegionMap::unsetRegion() { if (mEditLock) return; setRegion (""); } void CSVWorld::RegionMap::view() { std::ostringstream hint; hint << "c:"; QModelIndexList selected = selectionModel()->selectedIndexes(); bool first = true; for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) { std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). toString().toUtf8().constData(); if (first) first = false; else hint << "; "; hint << cellId; } emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Scene, "sys::default"), hint.str()); } void CSVWorld::RegionMap::viewInTable() { std::ostringstream hint; hint << "f:!or("; QModelIndexList selected = getSelectedCells(); bool first = true; for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) { std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). toString().toUtf8().constData(); if (first) first = false; else hint << ","; hint << "string(ID,\"" << cellId << "\")"; } hint << ")"; emit editRequest (CSMWorld::UniversalId::Type_Cells, hint.str()); } void CSVWorld::RegionMap::mouseMoveEvent (QMouseEvent* event) { startDragFromTable(*this); } std::vector< CSMWorld::UniversalId > CSVWorld::RegionMap::getDraggedRecords() const { QModelIndexList selected(getSelectedCells(true, false)); std::vector ids; foreach (QModelIndex it, selected) { ids.push_back( CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Cell, model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData())); } selected = getSelectedCells(false, true); foreach (QModelIndex it, selected) { ids.push_back( CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Cell_Missing, model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData())); } return ids; } void CSVWorld::RegionMap::dropEvent (QDropEvent* event) { QModelIndex index = indexAt (event->pos()); bool exists = QTableView::model()->data(index, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern); if (!index.isValid() || !exists) { return; } const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped return; if (mime->fromDocument(mDocument) && mime->holdsType(CSMWorld::UniversalId::Type_Region)) { CSMWorld::UniversalId record (mime->returnMatching (CSMWorld::UniversalId::Type_Region)); QAbstractItemModel *regionModel = model(); CSMWorld::IdTable *cellsModel = &dynamic_cast (* mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); std::string cellId(regionModel->data (index, CSMWorld::RegionMap::Role_CellId). toString().toUtf8().constData()); QModelIndex index2(cellsModel->getModelIndex (cellId, cellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Region))); mDocument.getUndoStack().push(new CSMWorld::ModifyCommand (*cellsModel, index2, QString::fromUtf8(record.getId().c_str()))); mRegionId = record.getId(); } } openmw-openmw-0.38.0/apps/opencs/view/world/regionmap.hpp000066400000000000000000000041461264522266000234510ustar00rootroot00000000000000#ifndef CSV_WORLD_REGIONMAP_H #define CSV_WORLD_REGIONMAP_H #include #include #include #include #include "./dragrecordtable.hpp" class QAction; namespace CSMDoc { class Document; } namespace CSMWorld { class UniversalId; } namespace CSVWorld { class RegionMap : public DragRecordTable { Q_OBJECT QAction *mSelectAllAction; QAction *mClearSelectionAction; QAction *mSelectRegionsAction; QAction *mCreateCellsAction; QAction *mSetRegionAction; QAction *mUnsetRegionAction; QAction *mViewAction; QAction *mViewInTableAction; std::string mRegionId; private: void contextMenuEvent (QContextMenuEvent *event); QModelIndexList getUnselectedCells() const; ///< \note Non-existent cells are not listed. QModelIndexList getSelectedCells (bool existent = true, bool nonExistent = false) const; ///< \param existant Include existant cells. /// \param nonExistant Include non-existant cells. QModelIndexList getMissingRegionCells() const; ///< Unselected cells within all regions that have at least one selected cell. void setRegion (const std::string& regionId); ///< Set region Id of selected cells. void mouseMoveEvent(QMouseEvent *event); void dropEvent(QDropEvent* event); public: RegionMap (const CSMWorld::UniversalId& universalId, CSMDoc::Document& document, QWidget *parent = 0); virtual std::vector getDraggedRecords() const; signals: void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); private slots: void selectAll(); void clearSelection(); void selectRegions(); void createCells(); void setRegion(); void unsetRegion(); void view(); void viewInTable(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/regionmapsubview.cpp000066400000000000000000000013231264522266000250430ustar00rootroot00000000000000#include "regionmapsubview.hpp" #include "regionmap.hpp" CSVWorld::RegionMapSubView::RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document) : CSVDoc::SubView (universalId) { mRegionMap = new RegionMap (universalId, document, this); setWidget (mRegionMap); connect (mRegionMap, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)), this, SLOT (editRequest (const CSMWorld::UniversalId&, const std::string&))); } void CSVWorld::RegionMapSubView::setEditLock (bool locked) { mRegionMap->setEditLock (locked); } void CSVWorld::RegionMapSubView::editRequest (const CSMWorld::UniversalId& id, const std::string& hint) { focusId (id, hint); } openmw-openmw-0.38.0/apps/opencs/view/world/regionmapsubview.hpp000066400000000000000000000011521264522266000250500ustar00rootroot00000000000000#ifndef CSV_WORLD_REGIONMAPSUBVIEW_H #define CSV_WORLD_REGIONMAPSUBVIEW_H #include "../doc/subview.hpp" class QAction; namespace CSMDoc { class Document; } namespace CSVWorld { class RegionMap; class RegionMapSubView : public CSVDoc::SubView { Q_OBJECT RegionMap *mRegionMap; public: RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document); virtual void setEditLock (bool locked); private slots: void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/scenesubview.cpp000066400000000000000000000167541264522266000241750ustar00rootroot00000000000000#include "scenesubview.hpp" #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/cellselection.hpp" #include "../filter/filterbox.hpp" #include "../render/pagedworldspacewidget.hpp" #include "../render/unpagedworldspacewidget.hpp" #include "../render/editmode.hpp" #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolmode.hpp" #include "../widget/scenetooltoggle.hpp" #include "../widget/scenetooltoggle2.hpp" #include "../widget/scenetoolrun.hpp" #include "tablebottombox.hpp" #include "creator.hpp" CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mScene(NULL), mLayout(new QHBoxLayout), mDocument(document), mToolbar(NULL) { QVBoxLayout *layout = new QVBoxLayout; layout->addWidget (mBottom = new TableBottomBox (NullCreatorFactory(), document, id, this), 0); mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); CSVRender::WorldspaceWidget* worldspaceWidget = NULL; widgetType whatWidget; if (id.getId()=="sys::default") { whatWidget = widget_Paged; CSVRender::PagedWorldspaceWidget *newWidget = new CSVRender::PagedWorldspaceWidget (this, document); worldspaceWidget = newWidget; makeConnections(newWidget); } else { whatWidget = widget_Unpaged; CSVRender::UnpagedWorldspaceWidget *newWidget = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); worldspaceWidget = newWidget; makeConnections(newWidget); } replaceToolbarAndWorldspace(worldspaceWidget, makeToolbar(worldspaceWidget, whatWidget)); layout->insertLayout (0, mLayout, 1); CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); layout->insertWidget (0, filterBox); QWidget *widget = new QWidget; widget->setLayout (layout); setWidget (widget); } void CSVWorld::SceneSubView::makeConnections (CSVRender::UnpagedWorldspaceWidget* widget) { connect (widget, SIGNAL (closeRequest()), this, SLOT (closeRequest())); connect(widget, SIGNAL(dataDropped(const std::vector&)), this, SLOT(handleDrop(const std::vector&))); connect(widget, SIGNAL(cellChanged(const CSMWorld::UniversalId&)), this, SLOT(cellSelectionChanged(const CSMWorld::UniversalId&))); } void CSVWorld::SceneSubView::makeConnections (CSVRender::PagedWorldspaceWidget* widget) { connect (widget, SIGNAL (closeRequest()), this, SLOT (closeRequest())); connect(widget, SIGNAL(dataDropped(const std::vector&)), this, SLOT(handleDrop(const std::vector&))); connect (widget, SIGNAL (cellSelectionChanged (const CSMWorld::CellSelection&)), this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&))); } CSVWidget::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type) { CSVWidget::SceneToolbar* toolbar = new CSVWidget::SceneToolbar (48+6, this); CSVWidget::SceneToolMode *navigationTool = widget->makeNavigationSelector (toolbar); toolbar->addTool (navigationTool); CSVWidget::SceneToolMode *lightingTool = widget->makeLightingSelector (toolbar); toolbar->addTool (lightingTool); CSVWidget::SceneToolToggle2 *sceneVisibilityTool = widget->makeSceneVisibilitySelector (toolbar); toolbar->addTool (sceneVisibilityTool); if (type==widget_Paged) { CSVWidget::SceneToolToggle *controlVisibilityTool = dynamic_cast (*widget). makeControlVisibilitySelector (toolbar); toolbar->addTool (controlVisibilityTool); } CSVWidget::SceneToolRun *runTool = widget->makeRunTool (toolbar); toolbar->addTool (runTool); toolbar->addTool (widget->makeEditModeSelector (toolbar)); return toolbar; } void CSVWorld::SceneSubView::setEditLock (bool locked) { mScene->setEditLock (locked); } void CSVWorld::SceneSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); } void CSVWorld::SceneSubView::useHint (const std::string& hint) { mScene->useViewHint (hint); } std::string CSVWorld::SceneSubView::getTitle() const { return mTitle; } void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::UniversalId& id) { setUniversalId(id); std::ostringstream stream; stream << "Scene: " << getUniversalId().getId(); mTitle = stream.str(); setWindowTitle (QString::fromUtf8 (mTitle.c_str())); emit updateTitle(); } void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection& selection) { setUniversalId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Scene, "sys::default")); int size = selection.getSize(); std::ostringstream stream; stream << "Scene: " << getUniversalId().getId(); if (size==0) stream << " (empty)"; else if (size==1) { stream << " (" << *selection.begin() << ")"; } else { stream << " (" << selection.getCentre() << " and " << size-1 << " more "; if (size>1) stream << "cells around it)"; else stream << "cell around it)"; } mTitle = stream.str(); setWindowTitle (QString::fromUtf8 (mTitle.c_str())); emit updateTitle(); } void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& data) { CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL; CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL; CSVWidget::SceneToolbar* toolbar = NULL; CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (data); switch (mScene->getDropRequirements (type)) { case CSVRender::WorldspaceWidget::canHandle: mScene->handleDrop (data, type); break; case CSVRender::WorldspaceWidget::needPaged: pagedNewWidget = new CSVRender::PagedWorldspaceWidget(this, mDocument); toolbar = makeToolbar(pagedNewWidget, widget_Paged); makeConnections(pagedNewWidget); replaceToolbarAndWorldspace(pagedNewWidget, toolbar); mScene->handleDrop (data, type); break; case CSVRender::WorldspaceWidget::needUnpaged: unPagedNewWidget = new CSVRender::UnpagedWorldspaceWidget(data.begin()->getId(), mDocument, this); toolbar = makeToolbar(unPagedNewWidget, widget_Unpaged); makeConnections(unPagedNewWidget); replaceToolbarAndWorldspace(unPagedNewWidget, toolbar); cellSelectionChanged(*(data.begin())); break; case CSVRender::WorldspaceWidget::ignored: return; } } void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar) { assert(mLayout); if (mScene) { mLayout->removeWidget(mScene); mScene->deleteLater(); } if (mToolbar) { mLayout->removeWidget(mToolbar); mToolbar->deleteLater(); } mScene = widget; mToolbar = toolbar; connect (mScene, SIGNAL (focusToolbarRequest()), mToolbar, SLOT (setFocus())); connect (mToolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus())); mLayout->addWidget (mToolbar, 0); mLayout->addWidget (mScene, 1); mScene->selectDefaultNavigationMode(); setFocusProxy (mScene); } openmw-openmw-0.38.0/apps/opencs/view/world/scenesubview.hpp000066400000000000000000000036421264522266000241720ustar00rootroot00000000000000#ifndef CSV_WORLD_SCENESUBVIEW_H #define CSV_WORLD_SCENESUBVIEW_H #include #include "../doc/subview.hpp" class QModelIndex; namespace CSMWorld { class CellSelection; } namespace CSMDoc { class Document; } namespace CSVRender { class WorldspaceWidget; class PagedWorldspaceWidget; class UnpagedWorldspaceWidget; } namespace CSVWidget { class SceneToolbar; class SceneToolMode; } namespace CSVWorld { class Table; class TableBottomBox; class CreatorFactoryBase; class SceneSubView : public CSVDoc::SubView { Q_OBJECT TableBottomBox *mBottom; CSVRender::WorldspaceWidget *mScene; QHBoxLayout* mLayout; CSMDoc::Document& mDocument; CSVWidget::SceneToolbar* mToolbar; std::string mTitle; public: SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); virtual void setStatusBar (bool show); virtual void useHint (const std::string& hint); virtual std::string getTitle() const; private: void makeConnections(CSVRender::PagedWorldspaceWidget* widget); void makeConnections(CSVRender::UnpagedWorldspaceWidget* widget); void replaceToolbarAndWorldspace(CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar); enum widgetType { widget_Paged, widget_Unpaged }; CSVWidget::SceneToolbar* makeToolbar(CSVRender::WorldspaceWidget* widget, widgetType type); private slots: void cellSelectionChanged (const CSMWorld::CellSelection& selection); void cellSelectionChanged (const CSMWorld::UniversalId& id); void handleDrop(const std::vector& data); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/scriptedit.cpp000066400000000000000000000241551264522266000236370ustar00rootroot00000000000000#include "scriptedit.hpp" #include #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/universalid.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/prefs/state.hpp" CSVWorld::ScriptEdit::ChangeLock::ChangeLock (ScriptEdit& edit) : mEdit (edit) { ++mEdit.mChangeLocked; } CSVWorld::ScriptEdit::ChangeLock::~ChangeLock() { --mEdit.mChangeLocked; } bool CSVWorld::ScriptEdit::event (QEvent *event) { // ignore undo and redo shortcuts if (event->type()==QEvent::ShortcutOverride) { QKeyEvent *keyEvent = static_cast (event); if (keyEvent->matches (QKeySequence::Undo) || keyEvent->matches (QKeySequence::Redo)) return true; } return QPlainTextEdit::event (event); } CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighlighter::Mode mode, QWidget* parent) : QPlainTextEdit (parent), mChangeLocked (0), mShowLineNum(false), mLineNumberArea(0), mDefaultFont(font()), mMonoFont(QFont("Monospace")), mDocument (document), mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive) { // setAcceptRichText (false); setLineWrapMode (QPlainTextEdit::NoWrap); setTabStopWidth (4); setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead mAllowedTypes < (event->mimeData()); if (!mime) QPlainTextEdit::dragEnterEvent(event); else { setTextCursor (cursorForPosition (event->pos())); event->acceptProposedAction(); } } void CSVWorld::ScriptEdit::dragMoveEvent (QDragMoveEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); if (!mime) QPlainTextEdit::dragMoveEvent(event); else { setTextCursor (cursorForPosition (event->pos())); event->accept(); } } void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped { QPlainTextEdit::dropEvent(event); return; } setTextCursor (cursorForPosition (event->pos())); if (mime->fromDocument (mDocument)) { std::vector records (mime->getData()); for (std::vector::iterator it = records.begin(); it != records.end(); ++it) { if (mAllowedTypes.contains (it->getType())) { if (stringNeedsQuote(it->getId())) { insertPlainText(QString::fromUtf8 (('"' + it->getId() + '"').c_str())); } else { insertPlainText(QString::fromUtf8 (it->getId().c_str())); } } } } } bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const { const QString string(QString::fromUtf8(id.c_str())); // is only for c++11, so let's use qregexp for now. //I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than… return !(string.contains(mWhiteListQoutes)); } void CSVWorld::ScriptEdit::settingChanged (const CSMPrefs::Setting *setting) { if (mHighlighter->settingChanged (setting)) updateHighlighting(); else if (*setting=="Scripts/mono-font") setFont (setting->isTrue() ? mMonoFont : mDefaultFont); else if (*setting=="Scripts/show-linenum") showLineNum (setting->isTrue()); } void CSVWorld::ScriptEdit::idListChanged() { mHighlighter->invalidateIds(); if (!mUpdateTimer.isActive()) mUpdateTimer.start (0); } void CSVWorld::ScriptEdit::updateHighlighting() { if (isChangeLocked()) return; ChangeLock lock (*this); mHighlighter->rehighlight(); } int CSVWorld::ScriptEdit::lineNumberAreaWidth() { if(!mShowLineNum) return 0; int digits = 1; int max = qMax(1, blockCount()); while (max >= 10) { max /= 10; ++digits; } int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits; return space; } void CSVWorld::ScriptEdit::updateLineNumberAreaWidth(int /* newBlockCount */) { setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); } void CSVWorld::ScriptEdit::updateLineNumberArea(const QRect &rect, int dy) { if (dy) mLineNumberArea->scroll(0, dy); else mLineNumberArea->update(0, rect.y(), mLineNumberArea->width(), rect.height()); if (rect.contains(viewport()->rect())) updateLineNumberAreaWidth(0); } void CSVWorld::ScriptEdit::resizeEvent(QResizeEvent *e) { QPlainTextEdit::resizeEvent(e); QRect cr = contentsRect(); mLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); } void CSVWorld::ScriptEdit::lineNumberAreaPaintEvent(QPaintEvent *event) { QPainter painter(mLineNumberArea); QTextBlock block = firstVisibleBlock(); int blockNumber = block.blockNumber(); int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); int bottom = top + (int) blockBoundingRect(block).height(); int startBlock = textCursor().blockNumber(); int endBlock = textCursor().blockNumber(); if(textCursor().hasSelection()) { QString str = textCursor().selection().toPlainText(); int offset = str.count("\n"); if(textCursor().position() < textCursor().anchor()) endBlock += offset; else startBlock -= offset; } painter.setBackgroundMode(Qt::OpaqueMode); QFont font = painter.font(); QBrush background = painter.background(); while (block.isValid() && top <= event->rect().bottom()) { if (block.isVisible() && bottom >= event->rect().top()) { QFont newFont = painter.font(); QString number = QString::number(blockNumber + 1); if(blockNumber >= startBlock && blockNumber <= endBlock) { painter.setBackground(Qt::cyan); painter.setPen(Qt::darkMagenta); newFont.setBold(true); } else { painter.setBackground(background); painter.setPen(Qt::black); } painter.setFont(newFont); painter.drawText(0, top, mLineNumberArea->width(), fontMetrics().height(), Qt::AlignRight, number); painter.setFont(font); } block = block.next(); top = bottom; bottom = top + (int) blockBoundingRect(block).height(); ++blockNumber; } } CSVWorld::LineNumberArea::LineNumberArea(ScriptEdit *editor) : QWidget(editor), mScriptEdit(editor) {} QSize CSVWorld::LineNumberArea::sizeHint() const { return QSize(mScriptEdit->lineNumberAreaWidth(), 0); } void CSVWorld::LineNumberArea::paintEvent(QPaintEvent *event) { mScriptEdit->lineNumberAreaPaintEvent(event); } openmw-openmw-0.38.0/apps/opencs/view/world/scriptedit.hpp000066400000000000000000000052401264522266000236360ustar00rootroot00000000000000#ifndef SCRIPTEDIT_H #define SCRIPTEDIT_H #include #include #include #include #include #include "../../model/world/universalid.hpp" #include "scripthighlighter.hpp" class QRegExp; namespace CSMDoc { class Document; } namespace CSVWorld { class LineNumberArea; class ScriptEdit : public QPlainTextEdit { Q_OBJECT public: class ChangeLock { ScriptEdit& mEdit; ChangeLock (const ChangeLock&); ChangeLock& operator= (const ChangeLock&); public: ChangeLock (ScriptEdit& edit); ~ChangeLock(); }; friend class ChangeLock; private: int mChangeLocked; ScriptHighlighter *mHighlighter; QTimer mUpdateTimer; bool mShowLineNum; LineNumberArea *mLineNumberArea; QFont mDefaultFont; QFont mMonoFont; protected: bool event (QEvent *event); public: ScriptEdit (const CSMDoc::Document& document, ScriptHighlighter::Mode mode, QWidget* parent); /// Should changes to the data be ignored (i.e. not cause updated)? /// /// \note This mechanism is used to avoid infinite update recursions bool isChangeLocked() const; void lineNumberAreaPaintEvent(QPaintEvent *event); int lineNumberAreaWidth(); void showLineNum(bool show); void setMonoFont(bool show); protected: virtual void resizeEvent(QResizeEvent *e); private: QVector mAllowedTypes; const CSMDoc::Document& mDocument; const QRegExp mWhiteListQoutes; void dragEnterEvent (QDragEnterEvent* event); void dropEvent (QDropEvent* event); void dragMoveEvent (QDragMoveEvent* event); bool stringNeedsQuote(const std::string& id) const; private slots: void settingChanged (const CSMPrefs::Setting *setting); void idListChanged(); void updateHighlighting(); void updateLineNumberAreaWidth(int newBlockCount); void updateLineNumberArea(const QRect &, int); }; class LineNumberArea : public QWidget { ScriptEdit *mScriptEdit; public: LineNumberArea(ScriptEdit *editor); QSize sizeHint() const; protected: void paintEvent(QPaintEvent *event); }; } #endif // SCRIPTEDIT_H openmw-openmw-0.38.0/apps/opencs/view/world/scripterrortable.cpp000066400000000000000000000114451264522266000250510ustar00rootroot00000000000000#include "scripterrortable.hpp" #include #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/prefs/state.hpp" void CSVWorld::ScriptErrorTable::report (const std::string& message, const Compiler::TokenLoc& loc, Type type) { std::ostringstream stream; stream << message << " (" << loc.mLiteral << ")"; addMessage (stream.str(), type==Compiler::ErrorHandler::WarningMessage ? CSMDoc::Message::Severity_Warning : CSMDoc::Message::Severity_Error, loc.mLine, loc.mColumn-loc.mLiteral.length()); } void CSVWorld::ScriptErrorTable::report (const std::string& message, Type type) { addMessage (message, type==Compiler::ErrorHandler::WarningMessage ? CSMDoc::Message::Severity_Warning : CSMDoc::Message::Severity_Error); } void CSVWorld::ScriptErrorTable::addMessage (const std::string& message, CSMDoc::Message::Severity severity, int line, int column) { int row = rowCount(); setRowCount (row+1); QTableWidgetItem *severityItem = new QTableWidgetItem ( QString::fromUtf8 (CSMDoc::Message::toString (severity).c_str())); severityItem->setFlags (severityItem->flags() ^ Qt::ItemIsEditable); setItem (row, 0, severityItem); if (line!=-1) { QTableWidgetItem *lineItem = new QTableWidgetItem; lineItem->setData (Qt::DisplayRole, line+1); lineItem->setFlags (lineItem->flags() ^ Qt::ItemIsEditable); setItem (row, 1, lineItem); QTableWidgetItem *columnItem = new QTableWidgetItem; columnItem->setData (Qt::DisplayRole, column); columnItem->setFlags (columnItem->flags() ^ Qt::ItemIsEditable); setItem (row, 3, columnItem); } QTableWidgetItem *messageItem = new QTableWidgetItem (QString::fromUtf8 (message.c_str())); messageItem->setFlags (messageItem->flags() ^ Qt::ItemIsEditable); setItem (row, 2, messageItem); } void CSVWorld::ScriptErrorTable::setWarningsMode (const std::string& value) { if (value=="Ignore") Compiler::ErrorHandler::setWarningsMode (0); else if (value=="Normal") Compiler::ErrorHandler::setWarningsMode (1); else if (value=="Strict") Compiler::ErrorHandler::setWarningsMode (2); } CSVWorld::ScriptErrorTable::ScriptErrorTable (const CSMDoc::Document& document, QWidget *parent) : QTableWidget (parent), mContext (document.getData()) { setColumnCount (4); QStringList headers; headers << "Severity" << "Line" << "Description"; setHorizontalHeaderLabels (headers); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) horizontalHeader()->setSectionResizeMode (0, QHeaderView::ResizeToContents); horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents); #else horizontalHeader()->setResizeMode (0, QHeaderView::ResizeToContents); horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents); #endif horizontalHeader()->setStretchLastSection (true); verticalHeader()->hide(); setColumnHidden (3, true); setSelectionMode (QAbstractItemView::NoSelection); Compiler::registerExtensions (mExtensions); mContext.setExtensions (&mExtensions); connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), this, SLOT (settingChanged (const CSMPrefs::Setting *))); CSMPrefs::get()["Scripts"].update(); connect (this, SIGNAL (cellClicked (int, int)), this, SLOT (cellClicked (int, int))); } void CSVWorld::ScriptErrorTable::update (const std::string& source) { clear(); try { std::istringstream input (source); Compiler::Scanner scanner (*this, input, mContext.getExtensions()); Compiler::FileParser parser (*this, mContext); scanner.scan (parser); } catch (const Compiler::SourceException&) { // error has already been reported via error handler } catch (const std::exception& error) { addMessage (error.what(), CSMDoc::Message::Severity_SeriousError); } } void CSVWorld::ScriptErrorTable::clear() { setRowCount (0); } bool CSVWorld::ScriptErrorTable::clearLocals (const std::string& script) { return mContext.clearLocals (script); } void CSVWorld::ScriptErrorTable::settingChanged (const CSMPrefs::Setting *setting) { if (*setting=="Scripts/warnings") setWarningsMode (setting->toString()); } void CSVWorld::ScriptErrorTable::cellClicked (int row, int column) { if (item (row, 1)) { int scriptLine = item (row, 1)->data (Qt::DisplayRole).toInt(); int scriptColumn = item (row, 3)->data (Qt::DisplayRole).toInt(); emit highlightError (scriptLine-1, scriptColumn); } } openmw-openmw-0.38.0/apps/opencs/view/world/scripterrortable.hpp000066400000000000000000000032261264522266000250540ustar00rootroot00000000000000#ifndef CSV_WORLD_SCRIPTERRORTABLE_H #define CSV_WORLD_SCRIPTERRORTABLE_H #include #include #include #include "../../model/world/scriptcontext.hpp" #include "../../model/doc/messages.hpp" namespace CSMDoc { class Document; } namespace CSMPrefs { class Setting; } namespace CSVWorld { class ScriptErrorTable : public QTableWidget, private Compiler::ErrorHandler { Q_OBJECT Compiler::Extensions mExtensions; CSMWorld::ScriptContext mContext; virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type); ///< Report error to the user. virtual void report (const std::string& message, Type type); ///< Report a file related error void addMessage (const std::string& message, CSMDoc::Message::Severity severity, int line = -1, int column = -1); void setWarningsMode (const std::string& value); public: ScriptErrorTable (const CSMDoc::Document& document, QWidget *parent = 0); void update (const std::string& source); void clear(); /// Clear local variable cache for \a script. /// /// \return Were there any locals that needed clearing? bool clearLocals (const std::string& script); private slots: void settingChanged (const CSMPrefs::Setting *setting); void cellClicked (int row, int column); signals: void highlightError (int line, int column); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/scripthighlighter.cpp000066400000000000000000000077021264522266000252070ustar00rootroot00000000000000#include "scripthighlighter.hpp" #include #include #include #include "../../model/prefs/setting.hpp" #include "../../model/prefs/category.hpp" bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) { highlight (loc, Type_Int); return true; } bool CSVWorld::ScriptHighlighter::parseFloat (float value, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) { highlight (loc, Type_Float); return true; } bool CSVWorld::ScriptHighlighter::parseName (const std::string& name, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) { highlight (loc, mContext.isId (name) ? Type_Id : Type_Name); return true; } bool CSVWorld::ScriptHighlighter::parseKeyword (int keyword, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) { if (((mMode==Mode_Console || mMode==Mode_Dialogue) && (keyword==Compiler::Scanner::K_begin || keyword==Compiler::Scanner::K_end || keyword==Compiler::Scanner::K_short || keyword==Compiler::Scanner::K_long || keyword==Compiler::Scanner::K_float)) || (mMode==Mode_Console && (keyword==Compiler::Scanner::K_if || keyword==Compiler::Scanner::K_endif || keyword==Compiler::Scanner::K_else || keyword==Compiler::Scanner::K_elseif || keyword==Compiler::Scanner::K_while || keyword==Compiler::Scanner::K_endwhile))) return parseName (loc.mLiteral, loc, scanner); highlight (loc, Type_Keyword); return true; } bool CSVWorld::ScriptHighlighter::parseSpecial (int code, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) { highlight (loc, Type_Special); return true; } bool CSVWorld::ScriptHighlighter::parseComment (const std::string& comment, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) { highlight (loc, Type_Comment); return true; } void CSVWorld::ScriptHighlighter::parseEOF (Compiler::Scanner& scanner) {} void CSVWorld::ScriptHighlighter::highlight (const Compiler::TokenLoc& loc, Type type) { int length = static_cast (loc.mLiteral.size()); int index = loc.mColumn; // compensate for bug in Compiler::Scanner (position of token is the character after the token) index -= length; setFormat (index, length, mScheme[type]); } CSVWorld::ScriptHighlighter::ScriptHighlighter (const CSMWorld::Data& data, Mode mode, QTextDocument *parent) : QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext), mContext (data), mMode (mode) { QColor color ("black"); QTextCharFormat format; format.setForeground (color); for (int i=0; i<=Type_Id; ++i) mScheme.insert (std::make_pair (static_cast (i), format)); // configure compiler Compiler::registerExtensions (mExtensions); mContext.setExtensions (&mExtensions); } void CSVWorld::ScriptHighlighter::highlightBlock (const QString& text) { std::istringstream stream (text.toUtf8().constData()); Compiler::Scanner scanner (mErrorHandler, stream, mContext.getExtensions()); try { scanner.scan (*this); } catch (...) {} // ignore syntax errors } void CSVWorld::ScriptHighlighter::invalidateIds() { mContext.invalidateIds(); } bool CSVWorld::ScriptHighlighter::settingChanged (const CSMPrefs::Setting *setting) { if (setting->getParent()->getKey()=="Scripts") { static const char *const colours[Type_Id+2] = { "colour-int", "colour-float", "colour-name", "colour-keyword", "colour-special", "colour-comment", "colour-id", 0 }; for (int i=0; colours[i]; ++i) if (setting->getKey()==colours[i]) { QTextCharFormat format; format.setForeground (setting->toColor()); mScheme[static_cast (i)] = format; return true; } } return false; } openmw-openmw-0.38.0/apps/opencs/view/world/scripthighlighter.hpp000066400000000000000000000055621264522266000252160ustar00rootroot00000000000000#ifndef CSV_WORLD_SCRIPTHIGHLIGHTER_H #define CSV_WORLD_SCRIPTHIGHLIGHTER_H #include #include #include #include #include #include "../../model/world/scriptcontext.hpp" namespace CSMPrefs { class Setting; } namespace CSVWorld { class ScriptHighlighter : public QSyntaxHighlighter, private Compiler::Parser { public: enum Type { Type_Int = 0, Type_Float = 1, Type_Name = 2, Type_Keyword = 3, Type_Special = 4, Type_Comment = 5, Type_Id = 6 }; enum Mode { Mode_General, Mode_Console, Mode_Dialogue }; private: Compiler::NullErrorHandler mErrorHandler; Compiler::Extensions mExtensions; CSMWorld::ScriptContext mContext; std::map mScheme; Mode mMode; private: virtual bool parseInt (int value, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner); ///< Handle an int token. /// \return fetch another token? virtual bool parseFloat (float value, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner); ///< Handle a float token. /// \return fetch another token? virtual bool parseName (const std::string& name, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? virtual bool parseComment (const std::string& comment, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner); ///< Handle comment token. /// \return fetch another token? virtual void parseEOF (Compiler::Scanner& scanner); ///< Handle EOF token. void highlight (const Compiler::TokenLoc& loc, Type type); public: ScriptHighlighter (const CSMWorld::Data& data, Mode mode, QTextDocument *parent); virtual void highlightBlock (const QString& text); void invalidateIds(); bool settingChanged (const CSMPrefs::Setting *setting); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/scriptsubview.cpp000066400000000000000000000245261264522266000244000ustar00rootroot00000000000000#include "scriptsubview.hpp" #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/universalid.hpp" #include "../../model/world/data.hpp" #include "../../model/world/columnbase.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/idtable.hpp" #include "../../model/prefs/state.hpp" #include "scriptedit.hpp" #include "recordbuttonbar.hpp" #include "tablebottombox.hpp" #include "genericcreator.hpp" #include "scripterrortable.hpp" void CSVWorld::ScriptSubView::addButtonBar() { if (mButtons) return; mButtons = new RecordButtonBar (getUniversalId(), *mModel, mBottom, &mCommandDispatcher, this); mLayout.insertWidget (1, mButtons); connect (mButtons, SIGNAL (switchToRow (int)), this, SLOT (switchToRow (int))); connect (this, SIGNAL (universalIdChanged (const CSMWorld::UniversalId&)), mButtons, SLOT (universalIdChanged (const CSMWorld::UniversalId&))); } void CSVWorld::ScriptSubView::recompile() { if (!mCompileDelay->isActive() && !isDeleted()) mCompileDelay->start (CSMPrefs::get()["Scripts"]["compile-delay"].toInt()); } bool CSVWorld::ScriptSubView::isDeleted() const { return mModel->data (mModel->getModelIndex (getUniversalId().getId(), mStateColumn)).toInt() ==CSMWorld::RecordBase::State_Deleted; } void CSVWorld::ScriptSubView::updateDeletedState() { if (isDeleted()) { mErrors->clear(); adjustSplitter(); mEditor->setEnabled (false); } else { mEditor->setEnabled (true); recompile(); } } void CSVWorld::ScriptSubView::adjustSplitter() { QList sizes; if (mErrors->rowCount()) { if (mErrors->height()) return; // keep old height if the error panel was already open sizes << (mMain->height()-mErrorHeight-mMain->handleWidth()) << mErrorHeight; } else { if (mErrors->height()) mErrorHeight = mErrors->height(); sizes << 1 << 0; } mMain->setSizes (sizes); } CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mDocument (document), mColumn (-1), mBottom(0), mButtons (0), mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())), mErrorHeight (CSMPrefs::get()["Scripts"]["error-height"].toInt()) { std::vector selection (1, id.getId()); mCommandDispatcher.setSelection (selection); mMain = new QSplitter (this); mMain->setOrientation (Qt::Vertical); mLayout.addWidget (mMain, 2); mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this); mMain->addWidget (mEditor); mMain->setCollapsible (0, false); mErrors = new ScriptErrorTable (document, this); mMain->addWidget (mErrors); QList sizes; sizes << 1 << 0; mMain->setSizes (sizes); QWidget *widget = new QWidget (this);; widget->setLayout (&mLayout); setWidget (widget); mModel = &dynamic_cast ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); mColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_ScriptText); mIdColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); mStateColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); QString source = mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString(); mEditor->setPlainText (source); // bottom box and buttons mBottom = new TableBottomBox (CreatorFactory(), document, id, this); connect (mBottom, SIGNAL (requestFocus (const std::string&)), this, SLOT (switchToId (const std::string&))); mLayout.addWidget (mBottom); // signals connect (mEditor, SIGNAL (textChanged()), this, SLOT (textChanged())); connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (dataChanged (const QModelIndex&, const QModelIndex&))); connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (rowsAboutToBeRemoved (const QModelIndex&, int, int))); updateStatusBar(); connect(mEditor, SIGNAL(cursorPositionChanged()), this, SLOT(updateStatusBar())); mErrors->update (source.toUtf8().constData()); connect (mErrors, SIGNAL (highlightError (int, int)), this, SLOT (highlightError (int, int))); mCompileDelay = new QTimer (this); mCompileDelay->setSingleShot (true); connect (mCompileDelay, SIGNAL (timeout()), this, SLOT (updateRequest())); updateDeletedState(); connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), this, SLOT (settingChanged (const CSMPrefs::Setting *))); CSMPrefs::get()["Scripts"].update(); } void CSVWorld::ScriptSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); } void CSVWorld::ScriptSubView::settingChanged (const CSMPrefs::Setting *setting) { if (*setting=="Scripts/toolbar") { if (setting->isTrue()) { addButtonBar(); } else if (mButtons) { mLayout.removeWidget (mButtons); delete mButtons; mButtons = 0; } } else if (*setting=="Scripts/compile-delay") { mCompileDelay->setInterval (setting->toInt()); } else if (*setting=="Scripts/warnings") recompile(); } void CSVWorld::ScriptSubView::updateStatusBar () { mBottom->positionChanged (mEditor->textCursor().blockNumber() + 1, mEditor->textCursor().columnNumber() + 1); } void CSVWorld::ScriptSubView::setEditLock (bool locked) { mEditor->setReadOnly (locked); if (mButtons) mButtons->setEditLock (locked); mCommandDispatcher.setEditLock (locked); } void CSVWorld::ScriptSubView::useHint (const std::string& hint) { if (hint.empty()) return; unsigned line = 0, column = 0; char c; std::istringstream stream (hint.c_str()+1); switch(hint[0]){ case 'R': case 'r': { QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); QString source = mModel->data (index).toString(); unsigned pos, dummy; if (!(stream >> c >> dummy >> pos) ) return; for (unsigned i = 0; i <= pos; ++i){ if (source[i] == '\n'){ ++line; column = i+1; } } column = pos - column; break; } case 'l': if (!(stream >> c >> line >> column)) return; } QTextCursor cursor = mEditor->textCursor(); cursor.movePosition (QTextCursor::Start); if (cursor.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, line)) cursor.movePosition (QTextCursor::Right, QTextCursor::MoveAnchor, column); mEditor->setFocus(); mEditor->setTextCursor (cursor); } void CSVWorld::ScriptSubView::textChanged() { if (mEditor->isChangeLocked()) return; ScriptEdit::ChangeLock lock (*mEditor); QString source = mEditor->toPlainText(); mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel, mModel->getModelIndex (getUniversalId().getId(), mColumn), source)); recompile(); } void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (mEditor->isChangeLocked()) return; ScriptEdit::ChangeLock lock (*mEditor); bool updateRequired = false; for (int i=topLeft.row(); i<=bottomRight.row(); ++i) { std::string id = mModel->data (mModel->index (i, mIdColumn)).toString().toUtf8().constData(); if (mErrors->clearLocals (id)) updateRequired = true; } QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) { if (mStateColumn>=topLeft.column() && mStateColumn<=bottomRight.column()) updateDeletedState(); if (mColumn>=topLeft.column() && mColumn<=bottomRight.column()) { QString source = mModel->data (index).toString(); QTextCursor cursor = mEditor->textCursor(); mEditor->setPlainText (source); mEditor->setTextCursor (cursor); updateRequired = true; } } if (updateRequired) recompile(); } void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end) { bool updateRequired = false; for (int i=start; i<=end; ++i) { std::string id = mModel->data (mModel->index (i, mIdColumn)).toString().toUtf8().constData(); if (mErrors->clearLocals (id)) updateRequired = true; } if (updateRequired) recompile(); QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); if (!parent.isValid() && index.row()>=start && index.row()<=end) emit closeRequest(); } void CSVWorld::ScriptSubView::switchToRow (int row) { int idColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); std::string id = mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData(); setUniversalId (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, id)); mEditor->setPlainText (mModel->data (mModel->index (row, mColumn)).toString()); std::vector selection (1, id); mCommandDispatcher.setSelection (selection); updateDeletedState(); } void CSVWorld::ScriptSubView::switchToId (const std::string& id) { switchToRow (mModel->getModelIndex (id, 0).row()); } void CSVWorld::ScriptSubView::highlightError (int line, int column) { QTextCursor cursor = mEditor->textCursor(); cursor.movePosition (QTextCursor::Start); if (cursor.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, line)) cursor.movePosition (QTextCursor::Right, QTextCursor::MoveAnchor, column); mEditor->setFocus(); mEditor->setTextCursor (cursor); } void CSVWorld::ScriptSubView::updateRequest() { QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); QString source = mModel->data (index).toString(); mErrors->update (source.toUtf8().constData()); adjustSplitter(); } openmw-openmw-0.38.0/apps/opencs/view/world/scriptsubview.hpp000066400000000000000000000041161264522266000243760ustar00rootroot00000000000000#ifndef CSV_WORLD_SCRIPTSUBVIEW_H #define CSV_WORLD_SCRIPTSUBVIEW_H #include #include "../../model/world/commanddispatcher.hpp" #include "../doc/subview.hpp" class QModelIndex; class QLabel; class QVBoxLayout; class QSplitter; class QTime; namespace CSMDoc { class Document; } namespace CSMWorld { class IdTable; } namespace CSMPrefs { class Setting; } namespace CSVWorld { class ScriptEdit; class RecordButtonBar; class TableBottomBox; class ScriptErrorTable; class ScriptSubView : public CSVDoc::SubView { Q_OBJECT ScriptEdit *mEditor; CSMDoc::Document& mDocument; CSMWorld::IdTable *mModel; int mColumn; int mIdColumn; int mStateColumn; TableBottomBox *mBottom; RecordButtonBar *mButtons; CSMWorld::CommandDispatcher mCommandDispatcher; QVBoxLayout mLayout; QSplitter *mMain; ScriptErrorTable *mErrors; QTimer *mCompileDelay; int mErrorHeight; private: void addButtonBar(); void recompile(); bool isDeleted() const; void updateDeletedState(); void adjustSplitter(); public: ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); virtual void useHint (const std::string& hint); virtual void setStatusBar (bool show); public slots: void textChanged(); void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); private slots: void settingChanged (const CSMPrefs::Setting *setting); void updateStatusBar(); void switchToRow (int row); void switchToId (const std::string& id); void highlightError (int line, int column); void updateRequest(); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/startscriptcreator.cpp000066400000000000000000000012431264522266000254200ustar00rootroot00000000000000#include "startscriptcreator.hpp" CSVWorld::StartScriptCreator::StartScriptCreator(CSMWorld::Data &data, QUndoStack &undoStack, const CSMWorld::UniversalId &id, bool relaxedIdRules): GenericCreator (data, undoStack, id, true) {} std::string CSVWorld::StartScriptCreator::getErrors() const { std::string errors; errors = getIdValidatorResult(); if (errors.length() > 0) return errors; else if (getData().getScripts().searchId(getId()) == -1) errors = "Script ID not found"; else if (getData().getStartScripts().searchId(getId()) > -1 ) errors = "Script with this ID already registered as Start Script"; return errors; } openmw-openmw-0.38.0/apps/opencs/view/world/startscriptcreator.hpp000066400000000000000000000011741264522266000254300ustar00rootroot00000000000000#ifndef STARTSCRIPTCREATOR_HPP #define STARTSCRIPTCREATOR_HPP #include "genericcreator.hpp" namespace CSVWorld { class StartScriptCreator : public GenericCreator { Q_OBJECT public: StartScriptCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, bool relaxedIdRules = false); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. }; } #endif // STARTSCRIPTCREATOR_HPP openmw-openmw-0.38.0/apps/opencs/view/world/subviews.cpp000066400000000000000000000202721264522266000233300ustar00rootroot00000000000000#include "subviews.hpp" #include "../doc/subviewfactoryimp.hpp" #include "tablesubview.hpp" #include "dialoguesubview.hpp" #include "scriptsubview.hpp" #include "regionmapsubview.hpp" #include "genericcreator.hpp" #include "cellcreator.hpp" #include "referenceablecreator.hpp" #include "referencecreator.hpp" #include "startscriptcreator.hpp" #include "scenesubview.hpp" #include "dialoguecreator.hpp" #include "infocreator.hpp" #include "previewsubview.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { // Regular record tables (including references which are actually sub-records, but are promoted // to top-level records within the editor) manager.add (CSMWorld::UniversalId::Type_Gmsts, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Skills, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_MagicEffects, new CSVDoc::SubViewFactoryWithCreator); static const CSMWorld::UniversalId::Type sTableTypes[] = { CSMWorld::UniversalId::Type_Globals, CSMWorld::UniversalId::Type_Classes, CSMWorld::UniversalId::Type_Factions, CSMWorld::UniversalId::Type_Races, CSMWorld::UniversalId::Type_Sounds, CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, CSMWorld::UniversalId::Type_Enchantments, CSMWorld::UniversalId::Type_BodyParts, CSMWorld::UniversalId::Type_SoundGens, CSMWorld::UniversalId::Type_Pathgrids, CSMWorld::UniversalId::Type_None // end marker }; for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i) manager.add (sTableTypes[i], new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_StartScripts, new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_Cells, new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_Referenceables, new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_References, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Topics, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Journals, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_TopicInfos, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_JournalInfos, new CSVDoc::SubViewFactoryWithCreator); // Subviews for resources tables manager.add (CSMWorld::UniversalId::Type_Meshes, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Icons, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Musics, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_SoundsRes, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Textures, new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Videos, new CSVDoc::SubViewFactoryWithCreator); // Subviews for editing/viewing individual records manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); // Other stuff (combined record tables) manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory); manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory); // More other stuff manager.add (CSMWorld::UniversalId::Type_Filters, new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_DebugProfiles, new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_Scripts, new CSVDoc::SubViewFactoryWithCreator >); // Dialogue subviews static const CSMWorld::UniversalId::Type sTableTypes2[] = { CSMWorld::UniversalId::Type_Region, CSMWorld::UniversalId::Type_Spell, CSMWorld::UniversalId::Type_Birthsign, CSMWorld::UniversalId::Type_Global, CSMWorld::UniversalId::Type_Race, CSMWorld::UniversalId::Type_Class, CSMWorld::UniversalId::Type_Sound, CSMWorld::UniversalId::Type_Faction, CSMWorld::UniversalId::Type_Enchantment, CSMWorld::UniversalId::Type_BodyPart, CSMWorld::UniversalId::Type_SoundGen, CSMWorld::UniversalId::Type_Pathgrid, CSMWorld::UniversalId::Type_None // end marker }; for (int i=0; sTableTypes2[i]!=CSMWorld::UniversalId::Type_None; ++i) manager.add (sTableTypes2[i], new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_StartScript, new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Skill, new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_MagicEffect, new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_Gmst, new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_Referenceable, new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Reference, new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_Cell, new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_JournalInfo, new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_TopicInfo, new CSVDoc::SubViewFactoryWithCreator(false)); manager.add (CSMWorld::UniversalId::Type_Topic, new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_Journal, new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_DebugProfile, new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Filter, new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_MetaData, new CSVDoc::SubViewFactory); //preview manager.add (CSMWorld::UniversalId::Type_Preview, new CSVDoc::SubViewFactory); } openmw-openmw-0.38.0/apps/opencs/view/world/subviews.hpp000066400000000000000000000003301264522266000233260ustar00rootroot00000000000000#ifndef CSV_WORLD_SUBVIEWS_H #define CSV_WORLD_SUBVIEWS_H namespace CSVDoc { class SubViewFactoryManager; } namespace CSVWorld { void addSubViewFactories (CSVDoc::SubViewFactoryManager& manager); } #endif openmw-openmw-0.38.0/apps/opencs/view/world/table.cpp000066400000000000000000000566051264522266000225610ustar00rootroot00000000000000#include "table.hpp" #include #include #include #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/infotableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtablebase.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/record.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commanddispatcher.hpp" #include "../../model/prefs/state.hpp" #include "recordstatusdelegate.hpp" #include "tableeditidaction.hpp" #include "util.hpp" void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { // configure dispatcher mDispatcher->setSelection (getSelectedIds()); std::vector extendedTypes = mDispatcher->getExtendedTypes(); mDispatcher->setExtendedTypes (extendedTypes); // create context menu QModelIndexList selectedRows = selectionModel()->selectedRows(); QMenu menu (this); /// \todo add menu items for select all and clear selection int currentRow = rowAt(event->y()); int currentColumn = columnAt(event->x()); if (mEditIdAction->isValidIdCell(currentRow, currentColumn)) { mEditIdAction->setCell(currentRow, currentColumn); menu.addAction(mEditIdAction); menu.addSeparator(); } if (!mEditLock && !(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) { if (selectedRows.size()==1) { menu.addAction (mEditAction); if (mCreateAction) menu.addAction(mCloneAction); } if (mCreateAction) menu.addAction (mCreateAction); if (mDispatcher->canRevert()) { menu.addAction (mRevertAction); if (!extendedTypes.empty()) menu.addAction (mExtendedRevertAction); } if (mDispatcher->canDelete()) { menu.addAction (mDeleteAction); if (!extendedTypes.empty()) menu.addAction (mExtendedDeleteAction); } if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_ReorderWithinTopic) { /// \todo allow reordering of multiple rows if (selectedRows.size()==1) { int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); if (column==-1) column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal); if (column!=-1) { int row = mProxyModel->mapToSource ( mProxyModel->index (selectedRows.begin()->row(), 0)).row(); QString curData = mModel->data(mModel->index(row, column)).toString(); if (row > 0) { QString prevData = mModel->data(mModel->index(row - 1, column)).toString(); if (Misc::StringUtils::ciEqual(curData.toStdString(), prevData.toStdString())) { menu.addAction(mMoveUpAction); } } if (row < mModel->rowCount() - 1) { QString nextData = mModel->data(mModel->index(row + 1, column)).toString(); if (Misc::StringUtils::ciEqual(curData.toStdString(), nextData.toStdString())) { menu.addAction(mMoveDownAction); } } } } } } if (selectedRows.size()==1) { int row = selectedRows.begin()->row(); row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_View) { CSMWorld::UniversalId id = mModel->view (row).first; int index = mDocument.getData().getCells().searchId (id.getId()); // index==-1: the ID references a worldspace instead of a cell (ignore for now and go // ahead) if (index==-1 || !mDocument.getData().getCells().getRecord (index).isDeleted()) menu.addAction (mViewAction); } if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Preview) { QModelIndex index = mModel->index (row, mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification)); CSMWorld::RecordBase::State state = static_cast ( mModel->data (index).toInt()); if (state!=CSMWorld::RecordBase::State_Deleted) menu.addAction (mPreviewAction); } } menu.exec (event->globalPos()); } void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event) { Qt::KeyboardModifiers modifiers = event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier); QModelIndex index = currentIndex(); selectionModel()->select (index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Rows); std::map::iterator iter = mDoubleClickActions.find (modifiers); if (iter==mDoubleClickActions.end()) { event->accept(); return; } switch (iter->second) { case Action_None: event->accept(); break; case Action_InPlaceEdit: DragRecordTable::mouseDoubleClickEvent (event); break; case Action_EditRecord: event->accept(); editRecord(); break; case Action_View: event->accept(); viewRecord(); break; case Action_Revert: event->accept(); if (mDispatcher->canRevert()) mDispatcher->executeRevert(); break; case Action_Delete: event->accept(); if (mDispatcher->canDelete()) mDispatcher->executeDelete(); break; case Action_EditRecordAndClose: event->accept(); editRecord(); emit closeRequest(); break; case Action_ViewAndClose: event->accept(); viewRecord(); emit closeRequest(); break; } } CSVWorld::Table::Table (const CSMWorld::UniversalId& id, bool createAndDelete, bool sorting, CSMDoc::Document& document) : DragRecordTable(document), mCreateAction (0), mCloneAction(0),mRecordStatusDisplay (0) { mModel = &dynamic_cast (*mDocument.getData().getTableModel (id)); bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos || id.getType() == CSMWorld::UniversalId::Type_JournalInfos; if (isInfoTable) { mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this); } else { mProxyModel = new CSMWorld::IdTableProxyModel (this); } mProxyModel->setSourceModel (mModel); mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); setModel (mProxyModel); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); #else horizontalHeader()->setResizeMode (QHeaderView::Interactive); #endif verticalHeader()->hide(); setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); setSortingEnabled (sorting); if (sorting) { sortByColumn (mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder); } int columns = mModel->columnCount(); for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); if (flags & CSMWorld::ColumnBase::Flag_Table) { CSMWorld::ColumnBase::Display display = static_cast ( mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, mDispatcher, document, this); mDelegates.push_back (delegate); setItemDelegateForColumn (i, delegate); } else hideColumn (i); } mEditAction = new QAction (tr ("Edit Record"), this); connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); addAction (mEditAction); if (createAndDelete) { mCreateAction = new QAction (tr ("Add Record"), this); connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); mCloneAction = new QAction (tr ("Clone Record"), this); connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); addAction(mCloneAction); } mRevertAction = new QAction (tr ("Revert Record"), this); connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert())); addAction (mRevertAction); mDeleteAction = new QAction (tr ("Delete Record"), this); connect (mDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeDelete())); addAction (mDeleteAction); mMoveUpAction = new QAction (tr ("Move Up"), this); connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); addAction (mMoveUpAction); mMoveDownAction = new QAction (tr ("Move Down"), this); connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); addAction (mMoveDownAction); mViewAction = new QAction (tr ("View"), this); connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord())); addAction (mViewAction); mPreviewAction = new QAction (tr ("Preview"), this); connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord())); addAction (mPreviewAction); mExtendedDeleteAction = new QAction (tr ("Extended Delete Record"), this); connect (mExtendedDeleteAction, SIGNAL (triggered()), this, SLOT (executeExtendedDelete())); addAction (mExtendedDeleteAction); mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this); connect (mExtendedRevertAction, SIGNAL (triggered()), this, SLOT (executeExtendedRevert())); addAction (mExtendedRevertAction); mEditIdAction = new TableEditIdAction (*this, this); connect (mEditIdAction, SIGNAL (triggered()), this, SLOT (editCell())); addAction (mEditIdAction); connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (tableSizeUpdate())); //connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), // this, SLOT (rowsInsertedEvent(const QModelIndex&, int, int))); connect (mProxyModel, SIGNAL (rowAdded (const std::string &)), this, SLOT (rowAdded (const std::string &))); /// \note This signal could instead be connected to a slot that filters out changes not affecting /// the records status column (for permanence reasons) connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (tableSizeUpdate())); connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), this, SLOT (selectionSizeUpdate ())); setAcceptDrops(true); mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_InPlaceEdit)); mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_EditRecord)); mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_View)); mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier | Qt::ControlModifier, Action_EditRecordAndClose)); connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)), this, SLOT (settingChanged (const CSMPrefs::Setting *))); CSMPrefs::get()["ID Tables"].update(); } void CSVWorld::Table::setEditLock (bool locked) { for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) (*iter)->setEditLock (locked); DragRecordTable::setEditLock(locked); mDispatcher->setEditLock (locked); } CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const { row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); int idColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); int typeColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType); return CSMWorld::UniversalId ( static_cast (mModel->data (mModel->index (row, typeColumn)).toInt()), mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData()); } std::vector CSVWorld::Table::getSelectedIds() const { std::vector ids; QModelIndexList selectedRows = selectionModel()->selectedRows(); int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter != selectedRows.end(); ++iter) { int row = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)).row(); ids.push_back (mModel->data (mModel->index (row, columnIndex)).toString().toUtf8().constData()); } return ids; } void CSVWorld::Table::editRecord() { if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) { QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) emit editRequest (getUniversalId (selectedRows.begin()->row()), ""); } } void CSVWorld::Table::cloneRecord() { if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) { QModelIndexList selectedRows = selectionModel()->selectedRows(); const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); if (selectedRows.size() == 1) { emit cloneRequest (toClone); } } } void CSVWorld::Table::moveUpRecord() { if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) return; QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) { int row2 =selectedRows.begin()->row(); if (row2>0) { int row = row2-1; row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); if (row2<=row) throw std::runtime_error ("Inconsistent row order"); std::vector newOrder (row2-row+1); newOrder[0] = row2-row; newOrder[row2-row] = 0; for (int i=1; i (*mModel), row, newOrder)); } } } void CSVWorld::Table::moveDownRecord() { if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) return; QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) { int row =selectedRows.begin()->row(); if (rowrowCount()-1) { int row2 = row+1; row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); if (row2<=row) throw std::runtime_error ("Inconsistent row order"); std::vector newOrder (row2-row+1); newOrder[0] = row2-row; newOrder[row2-row] = 0; for (int i=1; i (*mModel), row, newOrder)); } } } void CSVWorld::Table::editCell() { emit editRequest(mEditIdAction->getCurrentId(), ""); } void CSVWorld::Table::viewRecord() { if (!(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_View)) return; QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) { int row = selectedRows.begin()->row(); row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); std::pair params = mModel->view (row); if (params.first.getType()!=CSMWorld::UniversalId::Type_None) emit editRequest (params.first, params.second); } } void CSVWorld::Table::previewRecord() { QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) { std::string id = getUniversalId (selectedRows.begin()->row()).getId(); QModelIndex index = mModel->getModelIndex (id, mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification)); if (mModel->data (index)!=CSMWorld::RecordBase::State_Deleted) emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id), ""); } } void CSVWorld::Table::executeExtendedDelete() { if (CSMPrefs::get()["ID Tables"]["extended-config"].isTrue()) { emit extendedDeleteConfigRequest(getSelectedIds()); } else { QMetaObject::invokeMethod(mDispatcher, "executeExtendedDelete", Qt::QueuedConnection); } } void CSVWorld::Table::executeExtendedRevert() { if (CSMPrefs::get()["ID Tables"]["extended-config"].isTrue()) { emit extendedRevertConfigRequest(getSelectedIds()); } else { QMetaObject::invokeMethod(mDispatcher, "executeExtendedRevert", Qt::QueuedConnection); } } void CSVWorld::Table::settingChanged (const CSMPrefs::Setting *setting) { if (*setting=="ID Tables/jump-to-added") { if (setting->toString()=="Jump and Select") { mJumpToAddedRecord = true; mUnselectAfterJump = false; } else if (setting->toString()=="Jump Only") { mJumpToAddedRecord = true; mUnselectAfterJump = true; } else // No Jump { mJumpToAddedRecord = false; mUnselectAfterJump = false; } } else if (*setting=="Records/type-format" || *setting=="Records/status-format") { int columns = mModel->columnCount(); for (int i=0; i (*delegate).settingChanged (setting); emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); } } else if (setting->getParent()->getKey()=="ID Tables" && setting->getKey().substr (0, 6)=="double") { std::string modifierString = setting->getKey().substr (6); Qt::KeyboardModifiers modifiers = 0; if (modifierString=="-s") modifiers = Qt::ShiftModifier; else if (modifierString=="-c") modifiers = Qt::ControlModifier; else if (modifierString=="-sc") modifiers = Qt::ShiftModifier | Qt::ControlModifier; DoubleClickAction action = Action_None; std::string value = setting->toString(); if (value=="Edit in Place") action = Action_InPlaceEdit; else if (value=="Edit Record") action = Action_EditRecord; else if (value=="View") action = Action_View; else if (value=="Revert") action = Action_Revert; else if (value=="Delete") action = Action_Delete; else if (value=="Edit Record and Close") action = Action_EditRecordAndClose; else if (value=="View and Close") action = Action_ViewAndClose; mDoubleClickActions[modifiers] = action; } } void CSVWorld::Table::tableSizeUpdate() { int size = 0; int deleted = 0; int modified = 0; if (mProxyModel->columnCount()>0) { int rows = mProxyModel->rowCount(); int columnIndex = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Modification); if (columnIndex!=-1) { for (int i=0; imapToSource (mProxyModel->index (i, 0)); int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); switch (state) { case CSMWorld::RecordBase::State_BaseOnly: ++size; break; case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; } } } else size = rows; } emit tableSizeChanged (size, deleted, modified); } void CSVWorld::Table::selectionSizeUpdate() { emit selectionSizeChanged (selectionModel()->selectedRows().size()); } void CSVWorld::Table::requestFocus (const std::string& id) { QModelIndex index = mProxyModel->getModelIndex (id, 0); if (index.isValid()) scrollTo (index, QAbstractItemView::PositionAtTop); } void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) { mProxyModel->setFilter (filter); tableSizeUpdate(); selectionSizeUpdate(); } void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) { startDragFromTable(*this); } } std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const { const int count = mModel->columnCount(); std::vector titles; for (int i = 0; i < count; ++i) { CSMWorld::ColumnBase::Display columndisplay = static_cast (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); if (display == columndisplay) { titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toUtf8().constData()); } } return titles; } std::vector< CSMWorld::UniversalId > CSVWorld::Table::getDraggedRecords() const { QModelIndexList selectedRows = selectionModel()->selectedRows(); std::vector idToDrag; foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. { idToDrag.push_back (getUniversalId (it.row())); } return idToDrag; } void CSVWorld::Table::rowAdded(const std::string &id) { tableSizeUpdate(); if(mJumpToAddedRecord) { int idColumn = mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id); selectRow(mProxyModel->getModelIndex(id, idColumn).row()); if(mUnselectAfterJump) clearSelection(); } } openmw-openmw-0.38.0/apps/opencs/view/world/table.hpp000066400000000000000000000102221264522266000225470ustar00rootroot00000000000000#ifndef CSV_WORLD_TABLE_H #define CSV_WORLD_TABLE_H #include #include #include #include "../../model/filter/node.hpp" #include "../../model/world/columnbase.hpp" #include "../../model/world/universalid.hpp" #include "dragrecordtable.hpp" class QUndoStack; class QAction; namespace CSMDoc { class Document; } namespace CSMWorld { class Data; class IdTableProxyModel; class IdTableBase; class CommandDispatcher; } namespace CSMPrefs { class Setting; } namespace CSVWorld { class CommandDelegate; class TableEditIdAction; ///< Table widget class Table : public DragRecordTable { Q_OBJECT enum DoubleClickAction { Action_None, Action_InPlaceEdit, Action_EditRecord, Action_View, Action_Revert, Action_Delete, Action_EditRecordAndClose, Action_ViewAndClose }; std::vector mDelegates; QAction *mEditAction; QAction *mCreateAction; QAction *mCloneAction; QAction *mRevertAction; QAction *mDeleteAction; QAction *mMoveUpAction; QAction *mMoveDownAction; QAction *mViewAction; QAction *mPreviewAction; QAction *mExtendedDeleteAction; QAction *mExtendedRevertAction; TableEditIdAction *mEditIdAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTableBase *mModel; int mRecordStatusDisplay; CSMWorld::CommandDispatcher *mDispatcher; std::map mDoubleClickActions; bool mJumpToAddedRecord; bool mUnselectAfterJump; private: void contextMenuEvent (QContextMenuEvent *event); void mouseMoveEvent(QMouseEvent *event); protected: virtual void mouseDoubleClickEvent (QMouseEvent *event); public: Table (const CSMWorld::UniversalId& id, bool createAndDelete, bool sorting, CSMDoc::Document& document); ///< \param createAndDelete Allow creation and deletion of records. /// \param sorting Allow changing order of rows in the view via column headers. virtual void setEditLock (bool locked); CSMWorld::UniversalId getUniversalId (int row) const; std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; std::vector getSelectedIds() const; virtual std::vector getDraggedRecords() const; signals: void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void selectionSizeChanged (int size); void tableSizeChanged (int size, int deleted, int modified); ///< \param size Number of not deleted records /// \param deleted Number of deleted records /// \param modified Number of added and modified records void createRequest(); void cloneRequest(const CSMWorld::UniversalId&); void closeRequest(); void extendedDeleteConfigRequest(const std::vector &selectedIds); void extendedRevertConfigRequest(const std::vector &selectedIds); private slots: void editCell(); void editRecord(); void cloneRecord(); void moveUpRecord(); void moveDownRecord(); void viewRecord(); void previewRecord(); void executeExtendedDelete(); void executeExtendedRevert(); public slots: void settingChanged (const CSMPrefs::Setting *setting); void tableSizeUpdate(); void selectionSizeUpdate(); void requestFocus (const std::string& id); void recordFilterChanged (boost::shared_ptr filter); void rowAdded(const std::string &id); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/tablebottombox.cpp000066400000000000000000000147261264522266000245150ustar00rootroot00000000000000#include "tablebottombox.hpp" #include #include #include #include #include #include #include "creator.hpp" void CSVWorld::TableBottomBox::updateSize() { // Make sure that the size of the bottom box is determined by the currently visible widget for (int i = 0; i < mLayout->count(); ++i) { QSizePolicy::Policy verPolicy = QSizePolicy::Ignored; if (mLayout->widget(i) == mLayout->currentWidget()) { verPolicy = QSizePolicy::Expanding; } mLayout->widget(i)->setSizePolicy(QSizePolicy::Expanding, verPolicy); } } void CSVWorld::TableBottomBox::updateStatus() { if (mShowStatusBar) { static const char *sLabels[4] = { "record", "deleted", "touched", "selected" }; static const char *sLabelsPlural[4] = { "records", "deleted", "touched", "selected" }; std::ostringstream stream; bool first = true; for (int i=0; i<4; ++i) { if (mStatusCount[i]>0) { if (first) first = false; else stream << ", "; stream << mStatusCount[i] << ' ' << (mStatusCount[i]==1 ? sLabels[i] : sLabelsPlural[i]); } } if (mHasPosition) { if (!first) stream << " -- "; stream << "(" << mRow << ", " << mColumn << ")"; } mStatus->setText (QString::fromUtf8 (stream.str().c_str())); } } void CSVWorld::TableBottomBox::extendedConfigRequest(CSVWorld::ExtendedCommandConfigurator::Mode mode, const std::vector &selectedIds) { mExtendedConfigurator->configure (mode, selectedIds); mLayout->setCurrentWidget (mExtendedConfigurator); mEditMode = EditMode_ExtendedConfig; setVisible (true); mExtendedConfigurator->setFocus(); } CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, CSMDoc::Document& document, const CSMWorld::UniversalId& id, QWidget *parent) : QWidget (parent), mShowStatusBar (false), mEditMode(EditMode_None), mHasPosition(false) { for (int i=0; i<4; ++i) mStatusCount[i] = 0; setVisible (false); mLayout = new QStackedLayout; mLayout->setContentsMargins (0, 0, 0, 0); connect (mLayout, SIGNAL (currentChanged (int)), this, SLOT (currentWidgetChanged (int))); mStatus = new QLabel; mStatusBar = new QStatusBar; mStatusBar->addWidget (mStatus); mLayout->addWidget (mStatusBar); setLayout (mLayout); mCreator = creatorFactory.makeCreator (document, id); if (mCreator) { mCreator->installEventFilter(this); mLayout->addWidget (mCreator); connect (mCreator, SIGNAL (done()), this, SLOT (requestDone())); connect (mCreator, SIGNAL (requestFocus (const std::string&)), this, SIGNAL (requestFocus (const std::string&))); } mExtendedConfigurator = new ExtendedCommandConfigurator (document, id, this); mExtendedConfigurator->installEventFilter(this); mLayout->addWidget (mExtendedConfigurator); connect (mExtendedConfigurator, SIGNAL (done()), this, SLOT (requestDone())); updateSize(); } void CSVWorld::TableBottomBox::setEditLock (bool locked) { if (mCreator) mCreator->setEditLock (locked); mExtendedConfigurator->setEditLock (locked); } CSVWorld::TableBottomBox::~TableBottomBox() { delete mCreator; } bool CSVWorld::TableBottomBox::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape) { requestDone(); return true; } } return QWidget::eventFilter(object, event); } void CSVWorld::TableBottomBox::setStatusBar (bool show) { if (show!=mShowStatusBar) { setVisible (show || (mEditMode != EditMode_None)); mShowStatusBar = show; if (show) updateStatus(); } } bool CSVWorld::TableBottomBox::canCreateAndDelete() const { return mCreator; } void CSVWorld::TableBottomBox::requestDone() { if (!mShowStatusBar) setVisible (false); else updateStatus(); mLayout->setCurrentWidget (mStatusBar); mEditMode = EditMode_None; } void CSVWorld::TableBottomBox::currentWidgetChanged(int /*index*/) { updateSize(); } void CSVWorld::TableBottomBox::selectionSizeChanged (int size) { if (mStatusCount[3]!=size) { mStatusCount[3] = size; updateStatus(); } } void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modified) { bool changed = false; if (mStatusCount[0]!=size) { mStatusCount[0] = size; changed = true; } if (mStatusCount[1]!=deleted) { mStatusCount[1] = deleted; changed = true; } if (mStatusCount[2]!=modified) { mStatusCount[2] = modified; changed = true; } if (changed) updateStatus(); } void CSVWorld::TableBottomBox::positionChanged (int row, int column) { mRow = row; mColumn = column; mHasPosition = true; updateStatus(); } void CSVWorld::TableBottomBox::noMorePosition() { mHasPosition = false; updateStatus(); } void CSVWorld::TableBottomBox::createRequest() { mCreator->reset(); mCreator->toggleWidgets(true); mLayout->setCurrentWidget (mCreator); setVisible (true); mEditMode = EditMode_Creation; mCreator->focus(); } void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type) { mCreator->reset(); mCreator->cloneMode(id, type); mLayout->setCurrentWidget(mCreator); mCreator->toggleWidgets(false); setVisible (true); mEditMode = EditMode_Creation; mCreator->focus(); } void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector &selectedIds) { extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds); } void CSVWorld::TableBottomBox::extendedRevertConfigRequest(const std::vector &selectedIds) { extendedConfigRequest(ExtendedCommandConfigurator::Mode_Revert, selectedIds); } openmw-openmw-0.38.0/apps/opencs/view/world/tablebottombox.hpp000066400000000000000000000060301264522266000245070ustar00rootroot00000000000000#ifndef CSV_WORLD_BOTTOMBOX_H #define CSV_WORLD_BOTTOMBOX_H #include #include #include "extendedcommandconfigurator.hpp" class QLabel; class QStackedLayout; class QStatusBar; namespace CSMDoc { class Document; } namespace CSVWorld { class CreatorFactoryBase; class Creator; class TableBottomBox : public QWidget { Q_OBJECT enum EditMode { EditMode_None, EditMode_Creation, EditMode_ExtendedConfig }; bool mShowStatusBar; QLabel *mStatus; QStatusBar *mStatusBar; int mStatusCount[4]; EditMode mEditMode; Creator *mCreator; ExtendedCommandConfigurator *mExtendedConfigurator; QStackedLayout *mLayout; bool mHasPosition; int mRow; int mColumn; private: // not implemented TableBottomBox (const TableBottomBox&); TableBottomBox& operator= (const TableBottomBox&); void updateSize(); void updateStatus(); void extendedConfigRequest(ExtendedCommandConfigurator::Mode mode, const std::vector &selectedIds); public: TableBottomBox (const CreatorFactoryBase& creatorFactory, CSMDoc::Document& document, const CSMWorld::UniversalId& id, QWidget *parent = 0); virtual ~TableBottomBox(); virtual bool eventFilter(QObject *object, QEvent *event); void setEditLock (bool locked); void setStatusBar (bool show); bool canCreateAndDelete() const; ///< Is record creation and deletion supported? /// /// \note The BotomBox does not partake in the deletion of records. signals: void requestFocus (const std::string& id); ///< Request owner of this box to focus the just created \a id. The owner may /// ignore this request. private slots: void requestDone(); ///< \note This slot being called does not imply success. void currentWidgetChanged(int index); public slots: void selectionSizeChanged (int size); void tableSizeChanged (int size, int deleted, int modified); ///< \param size Number of not deleted records /// \param deleted Number of deleted records /// \param modified Number of added and modified records void positionChanged (int row, int column); void noMorePosition(); void createRequest(); void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); void extendedDeleteConfigRequest(const std::vector &selectedIds); void extendedRevertConfigRequest(const std::vector &selectedIds); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/tableeditidaction.cpp000066400000000000000000000032321264522266000251260ustar00rootroot00000000000000#include "tableeditidaction.hpp" #include #include "../../model/world/tablemimedata.hpp" CSVWorld::TableEditIdAction::CellData CSVWorld::TableEditIdAction::getCellData(int row, int column) const { QModelIndex index = mTable.model()->index(row, column); if (index.isValid()) { QVariant display = mTable.model()->data(index, CSMWorld::ColumnBase::Role_Display); QString value = mTable.model()->data(index).toString(); return std::make_pair(static_cast(display.toInt()), value); } return std::make_pair(CSMWorld::ColumnBase::Display_None, ""); } CSVWorld::TableEditIdAction::TableEditIdAction(const QTableView &table, QWidget *parent) : QAction(parent), mTable(table), mCurrentId(CSMWorld::UniversalId::Type_None) {} void CSVWorld::TableEditIdAction::setCell(int row, int column) { CellData data = getCellData(row, column); CSMWorld::UniversalId::Type idType = CSMWorld::TableMimeData::convertEnums(data.first); if (idType != CSMWorld::UniversalId::Type_None) { mCurrentId = CSMWorld::UniversalId(idType, data.second.toUtf8().constData()); setText("Edit '" + data.second + "'"); } } CSMWorld::UniversalId CSVWorld::TableEditIdAction::getCurrentId() const { return mCurrentId; } bool CSVWorld::TableEditIdAction::isValidIdCell(int row, int column) const { CellData data = getCellData(row, column); CSMWorld::UniversalId::Type idType = CSMWorld::TableMimeData::convertEnums(data.first); return CSMWorld::ColumnBase::isId(data.first) && idType != CSMWorld::UniversalId::Type_None && !data.second.isEmpty(); } openmw-openmw-0.38.0/apps/opencs/view/world/tableeditidaction.hpp000066400000000000000000000014151264522266000251340ustar00rootroot00000000000000#ifndef CSVWORLD_TABLEEDITIDACTION_HPP #define CSVWORLD_TABLEEDITIDACTION_HPP #include #include "../../model/world/columnbase.hpp" #include "../../model/world/universalid.hpp" class QTableView; namespace CSVWorld { class TableEditIdAction : public QAction { const QTableView &mTable; CSMWorld::UniversalId mCurrentId; typedef std::pair CellData; CellData getCellData(int row, int column) const; public: TableEditIdAction(const QTableView &table, QWidget *parent = 0); void setCell(int row, int column); CSMWorld::UniversalId getCurrentId() const; bool isValidIdCell(int row, int column) const; }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/tablesubview.cpp000066400000000000000000000136611264522266000241610ustar00rootroot00000000000000#include "tablesubview.hpp" #include #include #include #include #include #include #include "../../model/doc/document.hpp" #include "../../model/world/tablemimedata.hpp" #include "../doc/sizehint.hpp" #include "../filter/filterbox.hpp" #include "table.hpp" #include "tablebottombox.hpp" #include "creator.hpp" CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting) : SubView (id) { QVBoxLayout *layout = new QVBoxLayout; layout->addWidget (mBottom = new TableBottomBox (creatorFactory, document, id, this), 0); layout->insertWidget (0, mTable = new Table (id, mBottom->canCreateAndDelete(), sorting, document), 2); mFilterBox = new CSVFilter::FilterBox (document.getData(), this); layout->insertWidget (0, mFilterBox); CSVDoc::SizeHintWidget *widget = new CSVDoc::SizeHintWidget; widget->setLayout (layout); setWidget (widget); // prefer height of the screen and full width of the table const QRect rect = QApplication::desktop()->screenGeometry(this); int frameHeight = 40; // set a reasonable default QWidget *topLevel = QApplication::topLevelAt(pos()); if (topLevel) frameHeight = topLevel->frameGeometry().height() - topLevel->height(); widget->setSizeHint(QSize(mTable->horizontalHeader()->length(), rect.height()-frameHeight)); connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)), this, SLOT (editRequest (const CSMWorld::UniversalId&, const std::string&))); connect (mTable, SIGNAL (selectionSizeChanged (int)), mBottom, SLOT (selectionSizeChanged (int))); connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), mBottom, SLOT (tableSizeChanged (int, int, int))); mTable->tableSizeUpdate(); mTable->selectionSizeUpdate(); mTable->viewport()->installEventFilter(this); mBottom->installEventFilter(this); mFilterBox->installEventFilter(this); if (mBottom->canCreateAndDelete()) { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, SLOT(cloneRequest(const CSMWorld::UniversalId&))); connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); connect (mTable, SIGNAL(extendedDeleteConfigRequest(const std::vector &)), mBottom, SLOT(extendedDeleteConfigRequest(const std::vector &))); connect (mTable, SIGNAL(extendedRevertConfigRequest(const std::vector &)), mBottom, SLOT(extendedRevertConfigRequest(const std::vector &))); } connect (mBottom, SIGNAL (requestFocus (const std::string&)), mTable, SLOT (requestFocus (const std::string&))); connect (mFilterBox, SIGNAL (recordFilterChanged (boost::shared_ptr)), mTable, SLOT (recordFilterChanged (boost::shared_ptr))); connect(mFilterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); connect (mTable, SIGNAL (closeRequest()), this, SLOT (closeRequest())); } void CSVWorld::TableSubView::setEditLock (bool locked) { mTable->setEditLock (locked); mBottom->setEditLock (locked); } void CSVWorld::TableSubView::editRequest (const CSMWorld::UniversalId& id, const std::string& hint) { focusId (id, hint); } void CSVWorld::TableSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); } void CSVWorld::TableSubView::useHint (const std::string& hint) { if (hint.empty()) return; if (hint[0]=='f' && hint.size()>=2) mFilterBox->setRecordFilter (hint.substr (2)); } void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) { emit cloneRequest(toClone.getId(), toClone.getType()); } void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) { std::vector > > filterSource; std::vector refIdColumns = mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(CSMWorld::UniversalId::Type_Referenceable)); bool hasRefIdDisplay = !refIdColumns.empty(); for (std::vector::iterator it(types.begin()); it != types.end(); ++it) { CSMWorld::UniversalId::Type type = it->getType(); std::vector col = mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(type)); if(!col.empty()) { filterSource.push_back(std::make_pair(it->getId(), col)); } if(hasRefIdDisplay && CSMWorld::TableMimeData::isReferencable(type)) { filterSource.push_back(std::make_pair(it->getId(), refIdColumns)); } } mFilterBox->createFilterRequest(filterSource, action); } bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) { if (event->type() == QEvent::Drop) { if (QDropEvent* drop = dynamic_cast(event)) { const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped return false; bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); if (handled) { mFilterBox->setRecordFilter(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); } return handled; } } return false; } openmw-openmw-0.38.0/apps/opencs/view/world/tablesubview.hpp000066400000000000000000000026721264522266000241660ustar00rootroot00000000000000#ifndef CSV_WORLD_TABLESUBVIEW_H #define CSV_WORLD_TABLESUBVIEW_H #include "../doc/subview.hpp" #include class QModelIndex; namespace CSMWorld { class IdTable; } namespace CSMDoc { class Document; } namespace CSVFilter { class FilterBox; } namespace CSVWorld { class Table; class TableBottomBox; class CreatorFactoryBase; class TableSubView : public CSVDoc::SubView { Q_OBJECT Table *mTable; TableBottomBox *mBottom; CSVFilter::FilterBox *mFilterBox; public: TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting); virtual void setEditLock (bool locked); virtual void setStatusBar (bool show); virtual void useHint (const std::string& hint); protected: bool eventFilter(QObject* object, QEvent *event); signals: void cloneRequest(const std::string&, const CSMWorld::UniversalId::Type); private slots: void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void cloneRequest (const CSMWorld::UniversalId& toClone); void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, Qt::DropAction action); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/util.cpp000066400000000000000000000236611264522266000224430ustar00rootroot00000000000000#include "util.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "../../model/world/commands.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commanddispatcher.hpp" #include "../widget/coloreditor.hpp" #include "../widget/droplineedit.hpp" #include "dialoguespinbox.hpp" #include "scriptedit.hpp" CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) : mModel (model) {} int CSVWorld::NastyTableModelHack::rowCount (const QModelIndex & parent) const { return mModel.rowCount (parent); } int CSVWorld::NastyTableModelHack::columnCount (const QModelIndex & parent) const { return mModel.columnCount (parent); } QVariant CSVWorld::NastyTableModelHack::data (const QModelIndex & index, int role) const { return mModel.data (index, role); } bool CSVWorld::NastyTableModelHack::setData ( const QModelIndex &index, const QVariant &value, int role) { mData = value; return true; } QVariant CSVWorld::NastyTableModelHack::getData() const { return mData; } CSVWorld::CommandDelegateFactory::~CommandDelegateFactory() {} CSVWorld::CommandDelegateFactoryCollection *CSVWorld::CommandDelegateFactoryCollection::sThis = 0; CSVWorld::CommandDelegateFactoryCollection::CommandDelegateFactoryCollection() { if (sThis) throw std::logic_error ("multiple instances of CSVWorld::CommandDelegateFactoryCollection"); sThis = this; } CSVWorld::CommandDelegateFactoryCollection::~CommandDelegateFactoryCollection() { sThis = 0; for (std::map::iterator iter ( mFactories.begin()); iter!=mFactories.end(); ++iter) delete iter->second; } void CSVWorld::CommandDelegateFactoryCollection::add (CSMWorld::ColumnBase::Display display, CommandDelegateFactory *factory) { mFactories.insert (std::make_pair (display, factory)); } CSVWorld::CommandDelegate *CSVWorld::CommandDelegateFactoryCollection::makeDelegate ( CSMWorld::ColumnBase::Display display, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { std::map::const_iterator iter = mFactories.find (display); if (iter!=mFactories.end()) return iter->second->makeDelegate (dispatcher, document, parent); return new CommandDelegate (dispatcher, document, parent); } const CSVWorld::CommandDelegateFactoryCollection& CSVWorld::CommandDelegateFactoryCollection::get() { if (!sThis) throw std::logic_error ("no instance of CSVWorld::CommandDelegateFactoryCollection"); return *sThis; } QUndoStack& CSVWorld::CommandDelegate::getUndoStack() const { return mDocument.getUndoStack(); } CSMDoc::Document& CSVWorld::CommandDelegate::getDocument() const { return mDocument; } CSMWorld::ColumnBase::Display CSVWorld::CommandDelegate::getDisplayTypeFromIndex(const QModelIndex &index) const { int rawDisplay = index.data(CSMWorld::ColumnBase::Role_Display).toInt(); return static_cast(rawDisplay); } void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { if (!mCommandDispatcher) return; QVariant new_; // Color columns use a custom editor, so we need explicitly extract a data from it CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); if (colorEditor != NULL) { new_ = colorEditor->color(); } else { NastyTableModelHack hack (*model); QStyledItemDelegate::setModelData (editor, &hack, index); new_ = hack.getData(); } if ((model->data (index)!=new_) && (model->flags(index) & Qt::ItemIsEditable)) mCommandDispatcher->executeModify (model, index, new_); } CSVWorld::CommandDelegate::CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher, CSMDoc::Document& document, QObject *parent) : QStyledItemDelegate (parent), mEditLock (false), mCommandDispatcher (commandDispatcher), mDocument (document) {} void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { if (!mEditLock) { setModelDataImp (editor, model, index); } ///< \todo provide some kind of feedback to the user, indicating that editing is currently not possible. } QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { CSMWorld::ColumnBase::Display display = getDisplayTypeFromIndex(index); // This createEditor() method is called implicitly from tables. // For boolean values in tables use the default editor (combobox). // Checkboxes is looking ugly in the table view. // TODO: Find a better solution? if (display == CSMWorld::ColumnBase::Display_Boolean) { return QItemEditorFactory::defaultFactory()->createEditor(QVariant::Bool, parent); } // For tables the pop-up of the color editor should appear immediately after the editor creation // (the third parameter of ColorEditor's constructor) else if (display == CSMWorld::ColumnBase::Display_Colour) { return new CSVWidget::ColorEditor(index.data().value(), parent, true); } return createEditor (parent, option, index, display); } QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { QVariant variant = index.data(); if (!variant.isValid()) { variant = index.data(Qt::DisplayRole); if (!variant.isValid()) { return 0; } } // NOTE: for each editor type (e.g. QLineEdit) there needs to be a corresponding // entry in CSVWorld::DialogueDelegateDispatcher::makeEditor() switch (display) { case CSMWorld::ColumnBase::Display_Colour: return new CSVWidget::ColorEditor(index.data().value(), parent); case CSMWorld::ColumnBase::Display_Integer: { DialogueSpinBox *sb = new DialogueSpinBox(parent); sb->setRange(INT_MIN, INT_MAX); return sb; } case CSMWorld::ColumnBase::Display_Var: return new QLineEdit(parent); case CSMWorld::ColumnBase::Display_Float: { DialogueDoubleSpinBox *dsb = new DialogueDoubleSpinBox(parent); dsb->setRange(-FLT_MAX, FLT_MAX); dsb->setSingleStep(0.01f); dsb->setDecimals(3); return dsb; } case CSMWorld::ColumnBase::Display_LongString: { QPlainTextEdit *edit = new QPlainTextEdit(parent); edit->setUndoRedoEnabled (false); return edit; } case CSMWorld::ColumnBase::Display_LongString256: { /// \todo implement size limit. QPlainTextEdit does not support a size limit. QPlainTextEdit *edit = new QPlainTextEdit(parent); edit->setUndoRedoEnabled (false); return edit; } case CSMWorld::ColumnBase::Display_Boolean: return new QCheckBox(parent); case CSMWorld::ColumnBase::Display_ScriptLines: return new ScriptEdit (mDocument, ScriptHighlighter::Mode_Console, parent); case CSMWorld::ColumnBase::Display_String: // For other Display types (that represent record IDs) with drop support IdCompletionDelegate is used return new CSVWidget::DropLineEdit(display, parent); case CSMWorld::ColumnBase::Display_String32: { // For other Display types (that represent record IDs) with drop support IdCompletionDelegate is used CSVWidget::DropLineEdit *widget = new CSVWidget::DropLineEdit(display, parent); widget->setMaxLength (32); return widget; } default: return QStyledItemDelegate::createEditor (parent, option, index); } } void CSVWorld::CommandDelegate::setEditLock (bool locked) { mEditLock = locked; } bool CSVWorld::CommandDelegate::isEditLocked() const { return mEditLock; } void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index) const { setEditorData (editor, index, false); } void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { QVariant v = index.data(Qt::EditRole); if (tryDisplay) { if (!v.isValid()) { v = index.data(Qt::DisplayRole); if (!v.isValid()) { return; } } QPlainTextEdit* plainTextEdit = qobject_cast(editor); if(plainTextEdit) //for some reason it is easier to brake the loop here { if(plainTextEdit->toPlainText() == v.toString()) { return; } } } // Color columns use a custom editor, so we need explicitly set a data for it CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); if (colorEditor != NULL) { colorEditor->setColor(index.data().value()); return; } QByteArray n = editor->metaObject()->userProperty().name(); if (n == "dateTime") { if (editor->inherits("QTimeEdit")) n = "time"; else if (editor->inherits("QDateEdit")) n = "date"; } if (!n.isEmpty()) { if (!v.isValid()) v = QVariant(editor->property(n).userType(), (const void *)0); editor->setProperty(n, v); } } void CSVWorld::CommandDelegate::settingChanged (const CSMPrefs::Setting *setting) {} openmw-openmw-0.38.0/apps/opencs/view/world/util.hpp000066400000000000000000000116251264522266000224450ustar00rootroot00000000000000#ifndef CSV_WORLD_UTIL_H #define CSV_WORLD_UTIL_H #include #include #include #include "../../model/world/columnbase.hpp" #include "../../model/doc/document.hpp" class QUndoStack; namespace CSMWorld { class TableMimeData; class UniversalId; class CommandDispatcher; } namespace CSMPrefs { class Setting; } namespace CSVWorld { ///< \brief Getting the data out of an editor widget /// /// Really, Qt? Really? class NastyTableModelHack : public QAbstractTableModel { QAbstractItemModel& mModel; QVariant mData; public: NastyTableModelHack (QAbstractItemModel& model); int rowCount (const QModelIndex & parent = QModelIndex()) const; int columnCount (const QModelIndex & parent = QModelIndex()) const; QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); QVariant getData() const; }; class CommandDelegate; class CommandDelegateFactory { public: virtual ~CommandDelegateFactory(); virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const = 0; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; class CommandDelegateFactoryCollection { static CommandDelegateFactoryCollection *sThis; std::map mFactories; private: // not implemented CommandDelegateFactoryCollection (const CommandDelegateFactoryCollection&); CommandDelegateFactoryCollection& operator= (const CommandDelegateFactoryCollection&); public: CommandDelegateFactoryCollection(); ~CommandDelegateFactoryCollection(); void add (CSMWorld::ColumnBase::Display display, CommandDelegateFactory *factory); ///< The ownership of \a factory is transferred to *this. /// /// This function must not be called more than once per value of \a display. CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. /// /// If no factory is registered for \a display, a CommandDelegate will be returned. static const CommandDelegateFactoryCollection& get(); }; ///< \brief Use commands instead of manipulating the model directly class CommandDelegate : public QStyledItemDelegate { Q_OBJECT bool mEditLock; CSMWorld::CommandDispatcher *mCommandDispatcher; CSMDoc::Document& mDocument; protected: QUndoStack& getUndoStack() const; CSMDoc::Document& getDocument() const; CSMWorld::ColumnBase::Display getDisplayTypeFromIndex(const QModelIndex &index) const; virtual void setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; public: /// \param commandDispatcher If CommandDelegate will be only be used on read-only /// cells, a 0-pointer can be passed here. CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher, CSMDoc::Document& document, QObject *parent); virtual void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; virtual QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const; void setEditLock (bool locked); bool isEditLocked() const; ///< \return Does column require update? virtual void setEditorData (QWidget *editor, const QModelIndex& index) const; virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const; /// \attention This is not a slot. For ordering reasons this function needs to be /// called manually from the parent object's settingChanged function. virtual void settingChanged (const CSMPrefs::Setting *setting); }; } #endif openmw-openmw-0.38.0/apps/opencs/view/world/vartypedelegate.cpp000066400000000000000000000044351264522266000246510ustar00rootroot00000000000000#include "vartypedelegate.hpp" #include #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QModelIndex& index, int type) const { QModelIndex next = model->index (index.row(), index.column()+1); QVariant old = model->data (next); QVariant value; switch (type) { case ESM::VT_Short: case ESM::VT_Int: case ESM::VT_Long: value = old.toInt(); break; case ESM::VT_Float: value = old.toFloat(); break; case ESM::VT_String: value = old.toString(); break; default: break; // ignore the rest } getUndoStack().beginMacro ( "Modify " + model->headerData (index.column(), Qt::Horizontal, Qt::DisplayRole).toString()); getUndoStack().push (new CSMWorld::ModifyCommand (*model, index, type)); getUndoStack().push (new CSMWorld::ModifyCommand (*model, next, value)); getUndoStack().endMacro(); } CSVWorld::VarTypeDelegate::VarTypeDelegate (const std::vector >& values, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) : EnumDelegate (values, dispatcher, document, parent) {} CSVWorld::VarTypeDelegateFactory::VarTypeDelegateFactory (ESM::VarType type0, ESM::VarType type1, ESM::VarType type2, ESM::VarType type3) { if (type0!=ESM::VT_Unknown) add (type0); if (type1!=ESM::VT_Unknown) add (type1); if (type2!=ESM::VT_Unknown) add (type2); if (type3!=ESM::VT_Unknown) add (type3); } CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate ( CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { return new VarTypeDelegate (mValues, dispatcher, document, parent); } void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type) { std::vector enums = CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_ValueType); if (static_cast(type) >= enums.size()) throw std::logic_error ("Unsupported variable type"); mValues.push_back (std::make_pair (type, QString::fromUtf8 (enums[type].c_str()))); } openmw-openmw-0.38.0/apps/opencs/view/world/vartypedelegate.hpp000066400000000000000000000023441264522266000246530ustar00rootroot00000000000000#ifndef CSV_WORLD_VARTYPEDELEGATE_H #define CSV_WORLD_VARTYPEDELEGATE_H #include #include "enumdelegate.hpp" namespace CSVWorld { class VarTypeDelegate : public EnumDelegate { private: virtual void addCommands (QAbstractItemModel *model, const QModelIndex& index, int type) const; public: VarTypeDelegate (const std::vector >& values, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); }; class VarTypeDelegateFactory : public CommandDelegateFactory { std::vector > mValues; public: VarTypeDelegateFactory (ESM::VarType type0 = ESM::VT_Unknown, ESM::VarType type1 = ESM::VT_Unknown, ESM::VarType type2 = ESM::VT_Unknown, ESM::VarType type3 = ESM::VT_Unknown); virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. void add (ESM::VarType type); }; } #endif openmw-openmw-0.38.0/apps/openmw/000077500000000000000000000000001264522266000166675ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/CMakeLists.txt000066400000000000000000000146761264522266000214450ustar00rootroot00000000000000# local files set(GAME main.cpp engine.cpp ${CMAKE_SOURCE_DIR}/files/windows/openmw.rc ) if (ANDROID) set(GAME ${GAME} android_commandLine.cpp) set(GAME ${GAME} android_main.c) endif() if(NOT WIN32 AND NOT ANDROID) set(GAME ${GAME} crashcatcher.cpp) endif() set(GAME_HEADER engine.hpp ) source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation renderbin ) add_openmw_dir (mwinput inputmanagerimp ) add_openmw_dir (mwgui layout textinput widgets race class birth review windowmanagerimp console dialogue windowbase statswindow messagebox journalwindow charactercreation mapwindow windowpinnablebase tooltips scrollwindow bookwindow formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog enchantingdialog trainingwindow travelwindow exposedwindow cursor spellicons merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview draganddrop timeadvancer jailscreen ) add_openmw_dir (mwdialogue dialoguemanagerimp journalimp journalentry quest topic filter selectwrapper hypertextparser keywordsearch scripttest ) add_openmw_dir (mwscript locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions guiextensions soundextensions skyextensions statsextensions containerextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions animationextensions transformationextensions consoleextensions userextensions ) add_openmw_dir (mwsound soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output loudness movieaudiofactory ) add_openmw_dir (mwworld refdata worldimp scene globals class action nullaction actionteleport containerstore actiontalk actiontake manualref player cellvisitors failedaction cells localscripts customdata inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager ) add_openmw_dir (mwphysics physicssystem trace collisiontype actor convert ) add_openmw_dir (mwclass classes activator creature npc weapon armor potion apparatus book clothing container door ingredient creaturelevlist itemlevlist light lockpick misc probe repair static actor bodypart ) add_openmw_dir (mwmechanics mechanicsmanagerimp stat creaturestats magiceffects movement actorutil drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning character actors objects aistate coordinateconverter ) add_openmw_dir (mwstate statemanagerimp charactermanager character ) add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager inputmanager windowmanager statemanager ) # Main executable if (NOT ANDROID) add_executable(openmw ${OPENMW_FILES} ${GAME} ${GAME_HEADER} ${APPLE_BUNDLE_RESOURCES} ) else () add_library(openmw SHARED ${OPENMW_FILES} ${GAME} ${GAME_HEADER} ) endif () # Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING # when we change the backend. include_directories( ${FFMPEG_INCLUDE_DIRS} ) target_link_libraries(openmw ${OSG_LIBRARIES} ${OPENTHREADS_LIBRARIES} ${OSGPARTICLE_LIBRARIES} ${OSGUTIL_LIBRARIES} ${OSGDB_LIBRARIES} ${OSGVIEWER_LIBRARIES} ${OSGGA_LIBRARIES} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${OPENAL_LIBRARY} ${FFMPEG_LIBRARIES} ${BULLET_LIBRARIES} ${MYGUI_LIBRARIES} ${SDL2_LIBRARY} "osg-ffmpeg-videoplayer" "oics" components ) if (ANDROID) set (OSG_PLUGINS -Wl,--whole-archive ${OSG_PLUGINS_DIR}/libosgdb_dds.a ${OSG_PLUGINS_DIR}/libosgdb_bmp.a ${OSG_PLUGINS_DIR}/libosgdb_tga.a ${OSG_PLUGINS_DIR}/libosgdb_gif.a ${OSG_PLUGINS_DIR}/libosgdb_jpeg.a ${OSG_PLUGINS_DIR}/libosgdb_png.a -Wl,--no-whole-archive ) target_link_libraries(openmw EGL android log dl z ${OPENSCENEGRAPH_LIBRARIES} ${OSG_PLUGINS} jpeg gif png ) endif (ANDROID) if (USE_SYSTEM_TINYXML) target_link_libraries(openmw ${TINYXML_LIBRARIES}) endif() if (NOT UNIX) target_link_libraries(openmw ${SDL2MAIN_LIBRARY}) endif() # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) endif() if(APPLE) find_library(COCOA_FRAMEWORK Cocoa) find_library(IOKIT_FRAMEWORK IOKit) target_link_libraries(openmw ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK}) if (FFMPEG_FOUND) find_library(COREVIDEO_FRAMEWORK CoreVideo) find_library(VDA_FRAMEWORK VideoDecodeAcceleration) target_link_libraries(openmw ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK}) endif() endif(APPLE) if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw gcov) endif() if (MSVC) # Debug version needs increased number of sections beyond 2^16 if (CMAKE_CL_64) set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj") endif (CMAKE_CL_64) add_definitions("-D_USE_MATH_DEFINES") endif (MSVC) openmw-openmw-0.38.0/apps/openmw/android_commandLine.cpp000066400000000000000000000013271264522266000233240ustar00rootroot00000000000000#include "android_commandLine.h" #include "string.h" const char **argvData; int argcData; extern "C" void releaseArgv(); void releaseArgv() { delete[] argvData; } JNIEXPORT void JNICALL Java_ui_activity_GameActivity_commandLine(JNIEnv *env, jobject obj, jint argc, jobjectArray stringArray) { jboolean iscopy; argcData = (int) argc; argvData = new const char *[argcData + 1]; argvData[0] = "openmw"; for (int i = 1; i < argcData + 1; i++) { jstring string = (jstring) (env)->GetObjectArrayElement(stringArray, i - 1); argvData[i] = (env)->GetStringUTFChars(string, &iscopy); (env)->DeleteLocalRef(string); } (env)->DeleteLocalRef(stringArray); } openmw-openmw-0.38.0/apps/openmw/android_commandLine.h000066400000000000000000000006061264522266000227700ustar00rootroot00000000000000 /* DO NOT EDIT THIS FILE - it is machine generated */ #include #ifndef _Included_ui_activity_GameActivity_commandLine #define _Included_ui_activity_GameActivity_commandLine #ifdef __cplusplus extern "C" { #endif JNIEXPORT void JNICALL Java_ui_activity_GameActivity_commandLine(JNIEnv *env, jobject obj,jint argcData, jobjectArray stringArray); #ifdef __cplusplus } #endif #endif openmw-openmw-0.38.0/apps/openmw/android_main.c000066400000000000000000000015451264522266000214640ustar00rootroot00000000000000 #ifdef __ANDROID__ #include "SDL_main.h" /******************************************************************************* Functions called by JNI *******************************************************************************/ #include /* Called before to initialize JNI bindings */ extern void SDL_Android_Init(JNIEnv* env, jclass cls); extern int argcData; extern const char **argvData; void releaseArgv(); int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) { SDL_Android_Init(env, cls); SDL_SetMainReady(); /* Run the application code! */ int status; status = main(argcData+1, argvData); releaseArgv(); /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */ /* exit(status); */ return status; } #endif /* __ANDROID__ */ openmw-openmw-0.38.0/apps/openmw/crashcatcher.cpp000066400000000000000000000277661264522266000220470ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #ifndef PR_SET_PTRACER #define PR_SET_PTRACER 0x59616d61 #endif #elif defined (__APPLE__) #include #endif #define UNUSED(x) (void)(x) static const char crash_switch[] = "--cc-handle-crash"; static const char fatal_err[] = "\n\n*** Fatal Error ***\n"; static const char pipe_err[] = "!!! Failed to create pipe\n"; static const char fork_err[] = "!!! Failed to fork debug process\n"; static const char exec_err[] = "!!! Failed to exec debug process\n"; #ifndef PATH_MAX /* Not all platforms (GNU Hurd) have this. */ # define PATH_MAX 256 #endif static char argv0[PATH_MAX]; static char altstack[SIGSTKSZ]; static struct { int signum; pid_t pid; int has_siginfo; siginfo_t siginfo; char buf[1024]; } crash_info; static const struct { const char *name; int signum; } signals[] = { { "Segmentation fault", SIGSEGV }, { "Illegal instruction", SIGILL }, { "FPU exception", SIGFPE }, { "System BUS error", SIGBUS }, { NULL, 0 } }; static const struct { int code; const char *name; } sigill_codes[] = { #if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) { ILL_ILLOPC, "Illegal opcode" }, { ILL_ILLOPN, "Illegal operand" }, { ILL_ILLADR, "Illegal addressing mode" }, { ILL_ILLTRP, "Illegal trap" }, { ILL_PRVOPC, "Privileged opcode" }, { ILL_PRVREG, "Privileged register" }, { ILL_COPROC, "Coprocessor error" }, { ILL_BADSTK, "Internal stack error" }, #endif { 0, NULL } }; static const struct { int code; const char *name; } sigfpe_codes[] = { { FPE_INTDIV, "Integer divide by zero" }, { FPE_INTOVF, "Integer overflow" }, { FPE_FLTDIV, "Floating point divide by zero" }, { FPE_FLTOVF, "Floating point overflow" }, { FPE_FLTUND, "Floating point underflow" }, { FPE_FLTRES, "Floating point inexact result" }, { FPE_FLTINV, "Floating point invalid operation" }, { FPE_FLTSUB, "Subscript out of range" }, { 0, NULL } }; static const struct { int code; const char *name; } sigsegv_codes[] = { #ifndef __FreeBSD__ { SEGV_MAPERR, "Address not mapped to object" }, { SEGV_ACCERR, "Invalid permissions for mapped object" }, #endif { 0, NULL } }; static const struct { int code; const char *name; } sigbus_codes[] = { #ifndef __FreeBSD__ { BUS_ADRALN, "Invalid address alignment" }, { BUS_ADRERR, "Non-existent physical address" }, { BUS_OBJERR, "Object specific hardware error" }, #endif { 0, NULL } }; static int (*cc_user_info)(char*, char*); static void gdb_info(pid_t pid) { char respfile[64]; FILE *f; int fd; /* Create a temp file to put gdb commands into */ strcpy(respfile, "/tmp/gdb-respfile-XXXXXX"); if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) { fprintf(f, "attach %d\n" "shell echo \"\"\n" "shell echo \"* Loaded Libraries\"\n" "info sharedlibrary\n" "shell echo \"\"\n" "shell echo \"* Threads\"\n" "info threads\n" "shell echo \"\"\n" "shell echo \"* FPU Status\"\n" "info float\n" "shell echo \"\"\n" "shell echo \"* Registers\"\n" "info registers\n" "shell echo \"\"\n" "shell echo \"* Backtrace\"\n" "thread apply all backtrace full\n" "detach\n" "quit\n", pid); fclose(f); /* Run gdb and print process info. */ char cmd_buf[128]; snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile); printf("Executing: %s\n", cmd_buf); fflush(stdout); { /* another special exception for "ignoring return value..." */ int unused; unused = system(cmd_buf); UNUSED(unused); } /* Clean up */ remove(respfile); } else { /* Error creating temp file */ if(fd >= 0) { close(fd); remove(respfile); } printf("!!! Could not create gdb command file\n"); } fflush(stdout); } static void sys_info(void) { #ifdef __unix__ struct utsname info; if(uname(&info)) printf("!!! Failed to get system information\n"); else printf("System: %s %s %s %s %s\n", info.sysname, info.nodename, info.release, info.version, info.machine); fflush(stdout); #endif } static size_t safe_write(int fd, const void *buf, size_t len) { size_t ret = 0; while(ret < len) { ssize_t rem; if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1) { if(errno == EINTR) continue; break; } ret += rem; } return ret; } static void crash_catcher(int signum, siginfo_t *siginfo, void *context) { //ucontext_t *ucontext = (ucontext_t*)context; pid_t dbg_pid; int fd[2]; /* Make sure the effective uid is the real uid */ if(getuid() != geteuid()) { raise(signum); return; } safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1); if(pipe(fd) == -1) { safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1); raise(signum); return; } crash_info.signum = signum; crash_info.pid = getpid(); crash_info.has_siginfo = !!siginfo; if(siginfo) crash_info.siginfo = *siginfo; if(cc_user_info) cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf)); /* Fork off to start a crash handler */ switch((dbg_pid=fork())) { /* Error */ case -1: safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1); raise(signum); return; case 0: dup2(fd[0], STDIN_FILENO); close(fd[0]); close(fd[1]); execl(argv0, argv0, crash_switch, NULL); safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); _exit(1); default: #ifdef __linux__ prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0); #endif safe_write(fd[1], &crash_info, sizeof(crash_info)); close(fd[0]); close(fd[1]); /* Wait; we'll be killed when gdb is done */ do { int status; if(waitpid(dbg_pid, &status, 0) == dbg_pid && (WIFEXITED(status) || WIFSIGNALED(status))) { /* The debug process died before it could kill us */ raise(signum); break; } } while(1); } } static void crash_handler(const char *logfile) { const char *sigdesc = ""; int i; if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1) { fprintf(stderr, "!!! Failed to retrieve info from crashed process\n"); exit(1); } /* Get the signal description */ for(i = 0;signals[i].name;++i) { if(signals[i].signum == crash_info.signum) { sigdesc = signals[i].name; break; } } if(crash_info.has_siginfo) { switch(crash_info.signum) { case SIGSEGV: for(i = 0;sigsegv_codes[i].name;++i) { if(sigsegv_codes[i].code == crash_info.siginfo.si_code) { sigdesc = sigsegv_codes[i].name; break; } } break; case SIGFPE: for(i = 0;sigfpe_codes[i].name;++i) { if(sigfpe_codes[i].code == crash_info.siginfo.si_code) { sigdesc = sigfpe_codes[i].name; break; } } break; case SIGILL: for(i = 0;sigill_codes[i].name;++i) { if(sigill_codes[i].code == crash_info.siginfo.si_code) { sigdesc = sigill_codes[i].name; break; } } break; case SIGBUS: for(i = 0;sigbus_codes[i].name;++i) { if(sigbus_codes[i].code == crash_info.siginfo.si_code) { sigdesc = sigbus_codes[i].name; break; } } break; } } fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum); if(crash_info.has_siginfo) fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr); fputc('\n', stderr); if(logfile) { /* Create crash log file and redirect shell output to it */ if(freopen(logfile, "wa", stdout) != stdout) { fprintf(stderr, "!!! Could not create %s following signal\n", logfile); exit(1); } fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid); printf("*** Fatal Error ***\n" "%s (signal %i)\n", sigdesc, crash_info.signum); if(crash_info.has_siginfo) printf("Address: %p\n", crash_info.siginfo.si_addr); fputc('\n', stdout); fflush(stdout); } sys_info(); crash_info.buf[sizeof(crash_info.buf)-1] = '\0'; printf("%s\n", crash_info.buf); fflush(stdout); if(crash_info.pid > 0) { gdb_info(crash_info.pid); kill(crash_info.pid, SIGKILL); } // delay between killing of the crashed process and showing the message box to // work around occasional X server lock-up. this can only be a bug in X11 since // even faulty applications shouldn't be able to freeze the X server. usleep(100000); if(logfile) { std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !"; SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL); } exit(0); } int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*)) { struct sigaction sa; stack_t altss; int retval; if(argc == 2 && strcmp(argv[1], crash_switch) == 0) crash_handler(logfile); cc_user_info = user_info; if(argv[0][0] == '/') snprintf(argv0, sizeof(argv0), "%s", argv[0]); else { { /* we don't want to disable "ignoring return value" warnings, so we make * a special exception here. */ char * unused; unused = getcwd(argv0, sizeof(argv0)); UNUSED(unused); } retval = strlen(argv0); snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]); } /* Set an alternate signal stack so SIGSEGVs caused by stack overflows * still run */ altss.ss_sp = altstack; altss.ss_flags = 0; altss.ss_size = sizeof(altstack); sigaltstack(&altss, NULL); memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = crash_catcher; sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK; sigemptyset(&sa.sa_mask); retval = 0; while(num_signals--) { if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && *signals != SIGABRT && *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) { *signals = 0; retval = -1; } ++signals; } return retval; } // gdb apparently opens FD(s) 3,4,5 (whereas a typical prog uses only stdin=0, stdout=1,stderr=2) bool is_debugger_attached(void) { bool rc = false; FILE *fd = fopen("/tmp", "r"); if (fileno(fd) > 5) { rc = true; } fclose(fd); return rc; } openmw-openmw-0.38.0/apps/openmw/doc.hpp000066400000000000000000000016441264522266000201520ustar00rootroot00000000000000// Note: This is not a regular source file. /// \ingroup apps /// \defgroup openmw OpenMW /// \namespace OMW /// \ingroup openmw /// \brief Integration of OpenMW-subsystems /// \namespace MWDialogue /// \ingroup openmw /// \brief NPC dialogues /// \namespace MWMechanics /// \ingroup openmw /// \brief Game mechanics and NPC-AI /// \namespace MWSound /// \ingroup openmw /// \brief Sound & music /// \namespace MWGUI /// \ingroup openmw /// \brief HUD and windows /// \namespace MWRender /// \ingroup openmw /// \brief Rendering /// \namespace MWWorld /// \ingroup openmw /// \brief World data /// \namespace MWClass /// \ingroup openmw /// \brief Workaround for non-OOP design of the record system /// \namespace MWInput /// \ingroup openmw /// \brief User input and character controls /// \namespace MWScript /// \ingroup openmw /// \brief MW-specific script extentions and integration of the script system into OpenMW openmw-openmw-0.38.0/apps/openmw/engine.cpp000066400000000000000000000643271264522266000206540ustar00rootroot00000000000000#include "engine.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mwinput/inputmanagerimp.hpp" #include "mwgui/windowmanagerimp.hpp" #include "mwscript/scriptmanagerimp.hpp" #include "mwscript/extensions.hpp" #include "mwscript/interpretercontext.hpp" #include "mwsound/soundmanagerimp.hpp" #include "mwworld/class.hpp" #include "mwworld/player.hpp" #include "mwworld/worldimp.hpp" #include "mwrender/vismask.hpp" #include "mwclass/classes.hpp" #include "mwdialogue/dialoguemanagerimp.hpp" #include "mwdialogue/journalimp.hpp" #include "mwdialogue/scripttest.hpp" #include "mwmechanics/mechanicsmanagerimp.hpp" #include "mwstate/statemanagerimp.hpp" namespace { void checkSDLError(int ret) { if (ret != 0) std::cerr << "SDL error: " << SDL_GetError() << std::endl; } } void OMW::Engine::executeLocalScripts() { MWWorld::LocalScripts& localScripts = mEnvironment.getWorld()->getLocalScripts(); localScripts.startIteration(); while (!localScripts.isFinished()) { std::pair script = localScripts.getNext(); MWScript::InterpreterContext interpreterContext ( &script.second.getRefData().getLocals(), script.second); mEnvironment.getScriptManager()->run (script.first, interpreterContext); } localScripts.setIgnore (MWWorld::Ptr()); } void OMW::Engine::frame(float frametime) { try { mStartTick = mViewer->getStartTick(); mEnvironment.setFrameDuration (frametime); // update input mEnvironment.getInputManager()->update(frametime, false); // When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug. // If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2), // and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21) if (!mEnvironment.getInputManager()->isWindowVisible()) return; // sound if (mUseSound) mEnvironment.getSoundManager()->update(frametime); // Main menu opened? Then scripts are also paused. bool paused = mEnvironment.getWindowManager()->containsMode(MWGui::GM_MainMenu); // update game state mEnvironment.getStateManager()->update (frametime); bool guiActive = mEnvironment.getWindowManager()->isGuiMode(); osg::Timer_t beforeScriptTick = osg::Timer::instance()->tick(); if (mEnvironment.getStateManager()->getState()== MWBase::StateManager::State_Running) { if (!paused) { if (mEnvironment.getWorld()->getScriptsEnabled()) { // local scripts executeLocalScripts(); // global scripts mEnvironment.getScriptManager()->getGlobalScripts().run(); } mEnvironment.getWorld()->markCellAsUnchanged(); } if (!guiActive) { double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0; mEnvironment.getWorld()->advanceTime(hours, true); } } osg::Timer_t afterScriptTick = osg::Timer::instance()->tick(); // update actors osg::Timer_t beforeMechanicsTick = osg::Timer::instance()->tick(); if (mEnvironment.getStateManager()->getState()!= MWBase::StateManager::State_NoGame) { mEnvironment.getMechanicsManager()->update(frametime, guiActive); } osg::Timer_t afterMechanicsTick = osg::Timer::instance()->tick(); if (mEnvironment.getStateManager()->getState()== MWBase::StateManager::State_Running) { MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr(); if(!guiActive && player.getClass().getCreatureStats(player).isDead()) mEnvironment.getStateManager()->endGame(); } // update world osg::Timer_t beforePhysicsTick = osg::Timer::instance()->tick();; if (mEnvironment.getStateManager()->getState()!= MWBase::StateManager::State_NoGame) { mEnvironment.getWorld()->update(frametime, guiActive); } osg::Timer_t afterPhysicsTick = osg::Timer::instance()->tick(); // update GUI mEnvironment.getWindowManager()->onFrame(frametime); if (mEnvironment.getStateManager()->getState()!= MWBase::StateManager::State_NoGame) { mEnvironment.getWindowManager()->update(); } int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); osg::Stats* stats = mViewer->getViewerStats(); stats->setAttribute(frameNumber, "script_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeScriptTick)); stats->setAttribute(frameNumber, "script_time_taken", osg::Timer::instance()->delta_s(beforeScriptTick, afterScriptTick)); stats->setAttribute(frameNumber, "script_time_end", osg::Timer::instance()->delta_s(mStartTick, afterScriptTick)); stats->setAttribute(frameNumber, "mechanics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeMechanicsTick)); stats->setAttribute(frameNumber, "mechanics_time_taken", osg::Timer::instance()->delta_s(beforeMechanicsTick, afterMechanicsTick)); stats->setAttribute(frameNumber, "mechanics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterMechanicsTick)); stats->setAttribute(frameNumber, "physics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforePhysicsTick)); stats->setAttribute(frameNumber, "physics_time_taken", osg::Timer::instance()->delta_s(beforePhysicsTick, afterPhysicsTick)); stats->setAttribute(frameNumber, "physics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterPhysicsTick)); } catch (const std::exception& e) { std::cerr << "Error in framelistener: " << e.what() << std::endl; } } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) : mWindow(NULL) , mEncoding(ToUTF8::WINDOWS_1252) , mEncoder(NULL) , mVerboseScripts (false) , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) , mCompileAllDialogue (false) , mWarningsMode (1) , mScriptConsoleMode (false) , mActivationDistanceOverride(-1) , mGrab(true) , mExportFonts(false) , mScriptContext (0) , mFSStrict (false) , mScriptBlacklistUse (true) , mNewGame (false) , mCfgMgr(configurationManager) { Misc::Rng::init(); MWClass::registerClasses(); Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE|SDL_INIT_GAMECONTROLLER|SDL_INIT_JOYSTICK; if(SDL_WasInit(flags) == 0) { SDL_SetMainReady(); if(SDL_Init(flags) != 0) { throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError())); } } mStartTick = osg::Timer::instance()->tick(); } OMW::Engine::~Engine() { mEnvironment.cleanup(); delete mScriptContext; mScriptContext = NULL; mResourceSystem.reset(); mViewer = NULL; if (mWindow) { SDL_DestroyWindow(mWindow); mWindow = NULL; } SDL_Quit(); } void OMW::Engine::enableFSStrict(bool fsStrict) { mFSStrict = fsStrict; } // Set data dir void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs) { mDataDirs = dataDirs; mFileCollections = Files::Collections (dataDirs, !mFSStrict); } // Add BSA archive void OMW::Engine::addArchive (const std::string& archive) { mArchives.push_back(archive); } // Set resource dir void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir) { mResDir = parResDir; } // Set start cell name (only interiors for now) void OMW::Engine::setCell (const std::string& cellName) { mCellName = cellName; } void OMW::Engine::addContentFile(const std::string& file) { mContentFiles.push_back(file); } void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) { mVerboseScripts = scriptsVerbosity; } void OMW::Engine::setSkipMenu (bool skipMenu, bool newGame) { mSkipMenu = skipMenu; mNewGame = newGame; } std::string OMW::Engine::loadSettings (Settings::Manager & settings) { // Create the settings manager and load default settings file const std::string localdefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string(); const std::string globaldefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string(); // prefer local if (boost::filesystem::exists(localdefault)) settings.loadDefault(localdefault); else if (boost::filesystem::exists(globaldefault)) settings.loadDefault(globaldefault); else throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); // load user settings if they exist const std::string settingspath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string(); if (boost::filesystem::exists(settingspath)) settings.loadUser(settingspath); return settingspath; } void OMW::Engine::createWindow(Settings::Manager& settings) { int screen = settings.getInt("screen", "Video"); int width = settings.getInt("resolution x", "Video"); int height = settings.getInt("resolution y", "Video"); bool fullscreen = settings.getBool("fullscreen", "Video"); bool windowBorder = settings.getBool("window border", "Video"); bool vsync = settings.getBool("vsync", "Video"); int antialiasing = settings.getInt("antialiasing", "Video"); int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(screen), pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(screen); if(fullscreen) { pos_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(screen); pos_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(screen); } Uint32 flags = SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE; if(fullscreen) flags |= SDL_WINDOW_FULLSCREEN; if (!windowBorder) flags |= SDL_WINDOW_BORDERLESS; SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, settings.getBool("minimize on focus loss", "Video") ? "1" : "0"); checkSDLError(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24)); if (antialiasing > 0) { checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing)); } while (!mWindow) { mWindow = SDL_CreateWindow("OpenMW", pos_x, pos_y, width, height, flags); if (!mWindow) { // Try with a lower AA if (antialiasing > 0) { std::cout << "Note: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2 << std::endl; antialiasing /= 2; Settings::Manager::setInt("antialiasing", "Video", antialiasing); checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing)); continue; } else { std::stringstream error; error << "Failed to create SDL window: " << SDL_GetError() << std::endl; throw std::runtime_error(error.str()); } } } setWindowIcon(); osg::ref_ptr traits = new osg::GraphicsContext::Traits; SDL_GetWindowPosition(mWindow, &traits->x, &traits->y); SDL_GetWindowSize(mWindow, &traits->width, &traits->height); traits->windowName = SDL_GetWindowTitle(mWindow); traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS); traits->screenNum = SDL_GetWindowDisplayIndex(mWindow); // FIXME: Some way to get these settings back from the SDL window? traits->red = 8; traits->green = 8; traits->blue = 8; traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel traits->depth = 24; traits->stencil = 8; traits->vsync = vsync; traits->doubleBuffer = true; traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow); osg::ref_ptr graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits); if(!graphicsWindow->valid()) throw std::runtime_error("Failed to create GraphicsContext"); osg::ref_ptr camera = mViewer->getCamera(); camera->setGraphicsContext(graphicsWindow); camera->setViewport(0, 0, width, height); mViewer->realize(); } void OMW::Engine::setWindowIcon() { boost::filesystem::ifstream windowIconStream; std::string windowIcon = (mResDir / "mygui" / "openmw.png").string(); windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary); if (windowIconStream.fail()) std::cerr << "Failed to open " << windowIcon << std::endl; osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!reader) { std::cerr << "Failed to read window icon, no png readerwriter found" << std::endl; return; } osgDB::ReaderWriter::ReadResult result = reader->readImage(windowIconStream); if (!result.success()) std::cerr << "Failed to read " << windowIcon << ": " << result.message() << " code " << result.status() << std::endl; else { osg::ref_ptr image = result.getImage(); SDL_Surface* surface = SDLUtil::imageToSurface(image, true); SDL_SetWindowIcon(mWindow, surface); SDL_FreeSurface(surface); } } void OMW::Engine::prepareEngine (Settings::Manager & settings) { mEnvironment.setStateManager ( new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0))); createWindow(settings); osg::ref_ptr rootNode (new osg::Group); mViewer->setSceneData(rootNode); mVFS.reset(new VFS::Manager(mFSStrict)); VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true); mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get())); mResourceSystem->getTextureManager()->setUnRefImageDataAfterApply(true); mResourceSystem->getTextureManager()->setFilterSettings( Settings::Manager::getString("texture mag filter", "General"), Settings::Manager::getString("texture min filter", "General"), Settings::Manager::getString("texture mipmap", "General"), Settings::Manager::getInt("anisotropy", "General"), NULL ); // Create input and UI first to set up a bootstrapping environment for // showing a loading screen and keeping the window responsive while doing so std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input_v3.xml").string(); bool keybinderUserExists = boost::filesystem::exists(keybinderUser); if(!keybinderUserExists) { std::string input2 = (mCfgMgr.getUserConfigPath() / "input_v2.xml").string(); if(boost::filesystem::exists(input2)) { boost::filesystem::copy_file(input2, keybinderUser); keybinderUserExists = boost::filesystem::exists(keybinderUser); } } // find correct path to the game controller bindings const std::string localdefault = mCfgMgr.getLocalPath().string() + "/gamecontrollerdb.txt"; const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/gamecontrollerdb.txt"; std::string gameControllerdb; if (boost::filesystem::exists(localdefault)) gameControllerdb = localdefault; else if (boost::filesystem::exists(globaldefault)) gameControllerdb = globaldefault; else gameControllerdb = ""; //if it doesn't exist, pass in an empty string MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, keybinderUser, keybinderUserExists, gameControllerdb, mGrab); mEnvironment.setInputManager (input); std::string myguiResources = (mResDir / "mygui").string(); osg::ref_ptr guiRoot = new osg::Group; guiRoot->setNodeMask(MWRender::Mask_GUI); rootNode->addChild(guiRoot); MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mCfgMgr.getLogPath().string() + std::string("/"), myguiResources, mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap, Version::getOpenmwVersionDescription(mResDir.string())); mEnvironment.setWindowManager (window); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound)); if (!mSkipMenu) { std::string logo = mFallbackMap["Movies_Company_Logo"]; if (!logo.empty()) window->playVideo(logo, true); } // Create the world mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mFileCollections, mContentFiles, mEncoder, mFallbackMap, mActivationDistanceOverride, mCellName, mStartupScript, mResDir.string())); mEnvironment.getWorld()->setupPlayer(); input->setPlayer(&mEnvironment.getWorld()->getPlayer()); window->setStore(mEnvironment.getWorld()->getStore()); window->initUI(); window->renderWorldMap(); //Load translation data mTranslationDataStorage.setEncoder(mEncoder); for (size_t i = 0; i < mContentFiles.size(); i++) mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]); Compiler::registerExtensions (mExtensions); // Create script system mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full); mScriptContext->setExtensions (&mExtensions); mEnvironment.setScriptManager (new MWScript::ScriptManager (mEnvironment.getWorld()->getStore(), mVerboseScripts, *mScriptContext, mWarningsMode, mScriptBlacklistUse ? mScriptBlacklist : std::vector())); // Create game mechanics system MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager; mEnvironment.setMechanicsManager (mechanics); // Create dialog system mEnvironment.setJournal (new MWDialogue::Journal); mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage)); // scripts if (mCompileAll) { std::pair result = mEnvironment.getScriptManager()->compileAll(); if (result.first) std::cout << "compiled " << result.second << " of " << result.first << " scripts (" << 100*static_cast (result.second)/result.first << "%)" << std::endl; } if (mCompileAllDialogue) { std::pair result = MWDialogue::ScriptTest::compileAll(&mExtensions, mWarningsMode); if (result.first) std::cout << "compiled " << result.second << " of " << result.first << " dialogue script/actor combinations a(" << 100*static_cast (result.second)/result.first << "%)" << std::endl; } } class WriteScreenshotToFileOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation { public: WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat) : mScreenshotPath(screenshotPath) , mScreenshotFormat(screenshotFormat) { } virtual void operator()(const osg::Image& image, const unsigned int context_id) { // Count screenshots. int shotCount = 0; // Find the first unused filename with a do-while std::ostringstream stream; do { // Reset the stream stream.str(""); stream.clear(); stream << mScreenshotPath << "/screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << "." << mScreenshotFormat; } while (boost::filesystem::exists(stream.str())); boost::filesystem::ofstream outStream; outStream.open(boost::filesystem::path(stream.str()), std::ios::binary); osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(mScreenshotFormat); if (!readerwriter) { std::cerr << "Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found" << std::endl; return; } osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream); if (!result.success()) { std::cerr << "Can't write screenshot: " << result.message() << " code " << result.status() << std::endl; } } private: std::string mScreenshotPath; std::string mScreenshotFormat; }; // Initialise and enter main loop. void OMW::Engine::go() { assert (!mContentFiles.empty()); mViewer = new osgViewer::Viewer; osg::ref_ptr statshandler = new osgViewer::StatsHandler; statshandler->setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3); statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), "script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000); statshandler->addUserStatsLine("Mechanics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), "mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000); statshandler->addUserStatsLine("Physics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), "physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000); mViewer->addEventHandler(statshandler); Settings::Manager settings; std::string settingspath; settingspath = loadSettings (settings); mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(), Settings::Manager::getString("screenshot format", "General"))); mViewer->addEventHandler(mScreenCaptureHandler); // Create encoder ToUTF8::Utf8Encoder encoder (mEncoding); mEncoder = &encoder; prepareEngine (settings); if (!mSaveGameFile.empty()) { mEnvironment.getStateManager()->loadGame(mSaveGameFile); } else if (!mSkipMenu) { // start in main menu mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); try { // Is there an ini setting for this filename or something? mEnvironment.getSoundManager()->streamMusic("Special/morrowind title.mp3"); std::string logo = mFallbackMap["Movies_Morrowind_Logo"]; if (!logo.empty()) mEnvironment.getWindowManager()->playVideo(logo, true); } catch (...) {} } else { mEnvironment.getStateManager()->newGame (!mNewGame); } // Start the main rendering loop osg::Timer frameTimer; double simulationTime = 0.0; float framerateLimit = Settings::Manager::getFloat("framerate limit", "Video"); while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest()) { double dt = frameTimer.time_s(); frameTimer.setStartTick(); dt = std::min(dt, 0.2); bool guiActive = mEnvironment.getWindowManager()->isGuiMode(); if (!guiActive) simulationTime += dt; mViewer->advance(simulationTime); frame(dt); if (!mEnvironment.getInputManager()->isWindowVisible()) { OpenThreads::Thread::microSleep(5000); continue; } else { mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); } if (framerateLimit > 0.f) { double thisFrameTime = frameTimer.time_s(); double minFrameTime = 1.0 / framerateLimit; if (thisFrameTime < minFrameTime) { OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime)); } } } // Save user settings settings.saveUser(settingspath); std::cout << "Quitting peacefully." << std::endl; } void OMW::Engine::setCompileAll (bool all) { mCompileAll = all; } void OMW::Engine::setCompileAllDialogue (bool all) { mCompileAllDialogue = all; } void OMW::Engine::setSoundUsage(bool soundUsage) { mUseSound = soundUsage; } void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding) { mEncoding = encoding; } void OMW::Engine::setFallbackValues(std::map fallbackMap) { mFallbackMap = fallbackMap; } void OMW::Engine::setScriptConsoleMode (bool enabled) { mScriptConsoleMode = enabled; } void OMW::Engine::setStartupScript (const std::string& path) { mStartupScript = path; } void OMW::Engine::setActivationDistanceOverride (int distance) { mActivationDistanceOverride = distance; } void OMW::Engine::setWarningsMode (int mode) { mWarningsMode = mode; } void OMW::Engine::setScriptBlacklist (const std::vector& list) { mScriptBlacklist = list; } void OMW::Engine::setScriptBlacklistUse (bool use) { mScriptBlacklistUse = use; } void OMW::Engine::enableFontExport(bool exportFonts) { mExportFonts = exportFonts; } void OMW::Engine::setSaveGameFile(const std::string &savegame) { mSaveGameFile = savegame; } openmw-openmw-0.38.0/apps/openmw/engine.hpp000066400000000000000000000134671264522266000206600ustar00rootroot00000000000000#ifndef ENGINE_H #define ENGINE_H #include #include #include #include #include #include "mwbase/environment.hpp" #include "mwworld/ptr.hpp" namespace Resource { class ResourceSystem; } namespace VFS { class Manager; } namespace Compiler { class Context; } namespace MWScript { class ScriptManager; } namespace MWSound { class SoundManager; } namespace MWWorld { class World; } namespace MWGui { class WindowManager; } namespace Files { struct ConfigurationManager; } namespace osgViewer { class ScreenCaptureHandler; } struct SDL_Window; namespace OMW { /// \brief Main engine class, that brings together all the components of OpenMW class Engine { SDL_Window* mWindow; std::auto_ptr mVFS; std::auto_ptr mResourceSystem; MWBase::Environment mEnvironment; ToUTF8::FromType mEncoding; ToUTF8::Utf8Encoder* mEncoder; Files::PathContainer mDataDirs; std::vector mArchives; boost::filesystem::path mResDir; osg::ref_ptr mViewer; osg::ref_ptr mScreenCaptureHandler; std::string mCellName; std::vector mContentFiles; bool mVerboseScripts; bool mSkipMenu; bool mUseSound; bool mCompileAll; bool mCompileAllDialogue; int mWarningsMode; std::string mFocusName; std::map mFallbackMap; bool mScriptConsoleMode; std::string mStartupScript; int mActivationDistanceOverride; std::string mSaveGameFile; // Grab mouse? bool mGrab; bool mExportFonts; Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; Files::Collections mFileCollections; bool mFSStrict; Translation::Storage mTranslationDataStorage; std::vector mScriptBlacklist; bool mScriptBlacklistUse; bool mNewGame; osg::Timer_t mStartTick; // not implemented Engine (const Engine&); Engine& operator= (const Engine&); void executeLocalScripts(); void frame (float dt); /// Load settings from various files, returns the path to the user settings file std::string loadSettings (Settings::Manager & settings); /// Prepare engine for game play void prepareEngine (Settings::Manager & settings); void createWindow(Settings::Manager& settings); void setWindowIcon(); public: Engine(Files::ConfigurationManager& configurationManager); virtual ~Engine(); /// Enable strict filesystem mode (do not fold case) /// /// \attention The strict mode must be specified before any path-related settings /// are given to the engine. void enableFSStrict(bool fsStrict); /// Set data dirs void setDataDirs(const Files::PathContainer& dataDirs); /// Add BSA archive void addArchive(const std::string& archive); /// Set resource dir void setResourceDir(const boost::filesystem::path& parResDir); /// Set start cell name (only interiors for now) void setCell(const std::string& cellName); /** * @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container. * @param file - filename (extension is required) */ void addContentFile(const std::string& file); /// Enable or disable verbose script output void setScriptsVerbosity(bool scriptsVerbosity); /// Disable or enable all sounds void setSoundUsage(bool soundUsage); /// Skip main menu and go directly into the game /// /// \param newGame Start a new game instead off dumping the player into the game /// (ignored if !skipMenu). void setSkipMenu (bool skipMenu, bool newGame); void setGrabMouse(bool grab) { mGrab = grab; } /// Initialise and enter main loop. void go(); /// Compile all scripts (excludign dialogue scripts) at startup? void setCompileAll (bool all); /// Compile all dialogue scripts at startup? void setCompileAllDialogue (bool all); /// Font encoding void setEncoding(const ToUTF8::FromType& encoding); void setFallbackValues(std::map map); /// Enable console-only script functionality void setScriptConsoleMode (bool enabled); /// Set path for a script that is run on startup in the console. void setStartupScript (const std::string& path); /// Override the game setting specified activation distance. void setActivationDistanceOverride (int distance); void setWarningsMode (int mode); void setScriptBlacklist (const std::vector& list); void setScriptBlacklistUse (bool use); void enableFontExport(bool exportFonts); /// Set the save game file to load after initialising the engine. void setSaveGameFile(const std::string& savegame); private: Files::ConfigurationManager& mCfgMgr; }; } #endif /* ENGINE_H */ openmw-openmw-0.38.0/apps/openmw/main.cpp000066400000000000000000000345411264522266000203260ustar00rootroot00000000000000#include #include #include #include #include #include #include "engine.hpp" #include #include #include #if defined(_WIN32) // For OutputDebugString #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include // makes __argc and __argv available on windows #include #endif #if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) || defined(__posix)) #define USE_CRASH_CATCHER 1 #else #define USE_CRASH_CATCHER 0 #endif #if USE_CRASH_CATCHER #include extern int cc_install_handlers(int argc, char **argv, int num_signals, int *sigs, const char *logfile, int (*user_info)(char*, char*)); extern int is_debugger_attached(void); #endif #include /** * Workaround for problems with whitespaces in paths in older versions of Boost library */ #if (BOOST_VERSION <= 104600) namespace boost { template<> inline boost::filesystem::path lexical_cast(const std::string& arg) { return boost::filesystem::path(arg); } } /* namespace boost */ #endif /* (BOOST_VERSION <= 104600) */ struct FallbackMap { std::map mMap; }; void validate(boost::any &v, std::vector const &tokens, FallbackMap*, int) { if(v.empty()) { v = boost::any(FallbackMap()); } FallbackMap *map = boost::any_cast(&v); for(std::vector::const_iterator it=tokens.begin(); it != tokens.end(); ++it) { int sep = it->find(","); if(sep < 1 || sep == (int)it->length()-1) #if (BOOST_VERSION < 104200) throw boost::program_options::validation_error("invalid value"); #else throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value); #endif std::string key(it->substr(0,sep)); std::string value(it->substr(sep+1)); if(map->mMap.find(key) == map->mMap.end()) { map->mMap.insert(std::make_pair (key,value)); } } } /** * \brief Parses application command line and calls \ref Cfg::ConfigurationManager * to parse configuration files. * * Results are directly written to \ref Engine class. * * \retval true - Everything goes OK * \retval false - Error */ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::ConfigurationManager& cfgMgr) { // Create a local alias for brevity namespace bpo = boost::program_options; typedef std::vector StringsVector; bpo::options_description desc("Syntax: openmw \nAllowed options"); desc.add_options() ("help", "print help message") ("version", "print version information and quit") ("data", bpo::value()->default_value(Files::PathContainer(), "data") ->multitoken()->composing(), "set data directories (later directories have higher priority)") ("data-local", bpo::value()->default_value(""), "set local data directory (highest priority)") ("fallback-archive", bpo::value()->default_value(StringsVector(), "fallback-archive") ->multitoken(), "set fallback BSA archives (later archives have higher priority)") ("resources", bpo::value()->default_value("resources"), "set resources directory") ("start", bpo::value()->default_value(""), "set initial cell") ("content", bpo::value()->default_value(StringsVector(), "") ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") ("no-sound", bpo::value()->implicit_value(true) ->default_value(false), "disable all sounds") ("script-verbose", bpo::value()->implicit_value(true) ->default_value(false), "verbose script output") ("script-all", bpo::value()->implicit_value(true) ->default_value(false), "compile all scripts (excluding dialogue scripts) at startup") ("script-all-dialogue", bpo::value()->implicit_value(true) ->default_value(false), "compile all dialogue scripts at startup") ("script-console", bpo::value()->implicit_value(true) ->default_value(false), "enable console-only script functionality") ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") ("script-warn", bpo::value()->implicit_value (1) ->default_value (1), "handling of warnings when compiling scripts\n" "\t0 - ignore warning\n" "\t1 - show warning but consider script as correctly compiled anyway\n" "\t2 - treat warnings as errors") ("script-blacklist", bpo::value()->default_value(StringsVector(), "") ->multitoken(), "ignore the specified script (if the use of the blacklist is enabled)") ("script-blacklist-use", bpo::value()->implicit_value(true) ->default_value(true), "enable script blacklisting") ("load-savegame", bpo::value()->default_value(""), "load a save game file on game startup (specify an absolute filename or a filename relative to the current working directory)") ("skip-menu", bpo::value()->implicit_value(true) ->default_value(false), "skip main menu on game startup") ("new-game", bpo::value()->implicit_value(true) ->default_value(false), "run new game sequence (ignored if skip-menu=0)") ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") ( "encoding", bpo::value()-> default_value("win1252"), "Character encoding used in OpenMW game messages:\n" "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" "\n\twin1252 - Western European (Latin) alphabet, used by default") ("fallback", bpo::value()->default_value(FallbackMap(), "") ->multitoken()->composing(), "fallback values") ("no-grab", "Don't grab mouse cursor") ("export-fonts", bpo::value()->implicit_value(true) ->default_value(false), "Export Morrowind .fnt fonts to PNG image and XML file in current directory") ("activate-dist", bpo::value ()->default_value (-1), "activation distance override"); bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) .options(desc).allow_unregistered().run(); bpo::variables_map variables; // Runtime options override settings from all configs bpo::store(valid_opts, variables); bpo::notify(variables); if (variables.count ("help")) { std::cout << desc << std::endl; return false; } if (variables.count ("version")) { cfgMgr.readConfiguration(variables, desc, true); Version::Version v = Version::getOpenmwVersion(variables["resources"].as()); std::cout << v.describe() << std::endl; return false; } cfgMgr.readConfiguration(variables, desc); Version::Version v = Version::getOpenmwVersion(variables["resources"].as()); std::cout << v.describe() << std::endl; engine.setGrabMouse(!variables.count("no-grab")); // Font encoding settings std::string encoding(variables["encoding"].as()); std::cout << ToUTF8::encodingUsingMessage(encoding) << std::endl; engine.setEncoding(ToUTF8::calculateEncoding(encoding)); // directory settings engine.enableFSStrict(variables["fs-strict"].as()); Files::PathContainer dataDirs(variables["data"].as()); std::string local(variables["data-local"].as()); if (!local.empty()) { dataDirs.push_back(Files::PathContainer::value_type(local)); } cfgMgr.processPaths(dataDirs); engine.setDataDirs(dataDirs); // fallback archives StringsVector archives = variables["fallback-archive"].as(); for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); ++it) { engine.addArchive(*it); } engine.setResourceDir(variables["resources"].as()); StringsVector content = variables["content"].as(); if (content.empty()) { std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl; return false; } StringsVector::const_iterator it(content.begin()); StringsVector::const_iterator end(content.end()); for (; it != end; ++it) { engine.addContentFile(*it); } // startup-settings engine.setCell(variables["start"].as()); engine.setSkipMenu (variables["skip-menu"].as(), variables["new-game"].as()); if (!variables["skip-menu"].as() && variables["new-game"].as()) std::cerr << "new-game used without skip-menu -> ignoring it" << std::endl; // scripts engine.setCompileAll(variables["script-all"].as()); engine.setCompileAllDialogue(variables["script-all-dialogue"].as()); engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setScriptConsoleMode (variables["script-console"].as()); engine.setStartupScript (variables["script-run"].as()); engine.setWarningsMode (variables["script-warn"].as()); engine.setScriptBlacklist (variables["script-blacklist"].as()); engine.setScriptBlacklistUse (variables["script-blacklist-use"].as()); engine.setSaveGameFile (variables["load-savegame"].as()); // other settings engine.setSoundUsage(!variables["no-sound"].as()); engine.setFallbackValues(variables["fallback"].as().mMap); engine.setActivationDistanceOverride (variables["activate-dist"].as()); engine.enableFontExport(variables["export-fonts"].as()); return true; } #if defined(_WIN32) && defined(_DEBUG) class DebugOutput : public boost::iostreams::sink { public: std::streamsize write(const char *str, std::streamsize size) { // Make a copy for null termination std::string tmp (str, static_cast(size)); // Write string to Visual Studio Debug output OutputDebugString (tmp.c_str ()); return size; } }; #else class Tee : public boost::iostreams::sink { public: Tee(std::ostream &stream, std::ostream &stream2) : out(stream), out2(stream2) { } std::streamsize write(const char *str, std::streamsize size) { out.write (str, size); out.flush(); out2.write (str, size); out2.flush(); return size; } private: std::ostream &out; std::ostream &out2; }; #endif int main(int argc, char**argv) { #if defined(__APPLE__) setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); #endif // Some objects used to redirect cout and cerr // Scope must be here, so this still works inside the catch block for logging exceptions std::streambuf* cout_rdbuf = std::cout.rdbuf (); std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); #if !(defined(_WIN32) && defined(_DEBUG)) boost::iostreams::stream_buffer coutsb; boost::iostreams::stream_buffer cerrsb; #endif std::ostream oldcout(cout_rdbuf); std::ostream oldcerr(cerr_rdbuf); boost::filesystem::ofstream logfile; std::auto_ptr engine; int ret = 0; try { Files::ConfigurationManager cfgMgr; #if defined(_WIN32) && defined(_DEBUG) // Redirect cout and cerr to VS debug output when running in debug mode boost::iostreams::stream_buffer sb; sb.open(DebugOutput()); std::cout.rdbuf (&sb); std::cerr.rdbuf (&sb); #else // Redirect cout and cerr to openmw.log logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/openmw.log")); coutsb.open (Tee(logfile, oldcout)); cerrsb.open (Tee(logfile, oldcerr)); std::cout.rdbuf (&coutsb); std::cerr.rdbuf (&cerrsb); #endif #if USE_CRASH_CATCHER // Unix crash catcher if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached()) { int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; cc_install_handlers(argc, argv, 5, s, (cfgMgr.getLogPath() / "crash.log").string().c_str(), NULL); std::cout << "Installing crash catcher" << std::endl; } else std::cout << "Running in a debugger, not installing crash catcher" << std::endl; #endif #ifdef __APPLE__ // FIXME: set current dir to bundle path //boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path(); //boost::filesystem::current_path(bundlePath); #endif engine.reset(new OMW::Engine(cfgMgr)); if (parseOptions(argc, argv, *engine, cfgMgr)) { engine->go(); } } catch (std::exception &e) { #if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) if (!isatty(fileno(stdin))) #endif SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL); std::cerr << "\nERROR: " << e.what() << std::endl; ret = 1; } // Restore cout and cerr std::cout.rdbuf(cout_rdbuf); std::cerr.rdbuf(cerr_rdbuf); return ret; } // Platform specific for Windows when there is no console built into the executable. // Windows will call the WinMain function instead of main in this case, the normal // main function is then called with the __argc and __argv parameters. #if defined(_WIN32) && !defined(_CONSOLE) int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { return main(__argc, __argv); } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/000077500000000000000000000000001264522266000201455ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwbase/dialoguemanager.hpp000066400000000000000000000050011264522266000237760ustar00rootroot00000000000000#ifndef GAME_MWBASE_DIALOGUEMANAGER_H #define GAME_MWBASE_DIALOGUEMANAGER_H #include #include namespace Loading { class Listener; } namespace ESM { class ESMReader; class ESMWriter; } namespace MWWorld { class Ptr; } namespace MWBase { /// \brief Interface for dialogue manager (implemented in MWDialogue) class DialogueManager { DialogueManager (const DialogueManager&); ///< not implemented DialogueManager& operator= (const DialogueManager&); ///< not implemented public: DialogueManager() {} virtual void clear() = 0; virtual ~DialogueManager() {} virtual bool isInChoice() const = 0; virtual void startDialogue (const MWWorld::Ptr& actor) = 0; virtual void addTopic (const std::string& topic) = 0; virtual void askQuestion (const std::string& question,int choice) = 0; virtual void goodbye() = 0; virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const = 0; //calbacks for the GUI virtual void keywordSelected (const std::string& keyword) = 0; virtual void goodbyeSelected() = 0; virtual void questionAnswered (int answer) = 0; virtual bool checkServiceRefused () = 0; virtual void persuade (int type) = 0; virtual int getTemporaryDispositionChange () const = 0; /// @note This change is temporary and gets discarded when dialogue ends. virtual void applyDispositionChange (int delta) = 0; virtual int countSavedGameRecords() const = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0; virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0; /// Changes faction1's opinion of faction2 by \a diff. virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff) = 0; virtual void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute) = 0; /// @return faction1's opinion of faction2 virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0; /// Removes the last added topic response for the given actor from the journal virtual void clearInfoActor (const MWWorld::Ptr& actor) const = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/environment.cpp000066400000000000000000000067421264522266000232260ustar00rootroot00000000000000#include "environment.hpp" #include #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" #include "journal.hpp" #include "soundmanager.hpp" #include "mechanicsmanager.hpp" #include "inputmanager.hpp" #include "windowmanager.hpp" #include "statemanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; MWBase::Environment::Environment() : mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0), mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mStateManager (0), mFrameDuration (0) { assert (!sThis); sThis = this; } MWBase::Environment::~Environment() { cleanup(); sThis = 0; } void MWBase::Environment::setWorld (World *world) { mWorld = world; } void MWBase::Environment::setSoundManager (SoundManager *soundManager) { mSoundManager = soundManager; } void MWBase::Environment::setScriptManager (ScriptManager *scriptManager) { mScriptManager = scriptManager; } void MWBase::Environment::setWindowManager (WindowManager *windowManager) { mWindowManager = windowManager; } void MWBase::Environment::setMechanicsManager (MechanicsManager *mechanicsManager) { mMechanicsManager = mechanicsManager; } void MWBase::Environment::setDialogueManager (DialogueManager *dialogueManager) { mDialogueManager = dialogueManager; } void MWBase::Environment::setJournal (Journal *journal) { mJournal = journal; } void MWBase::Environment::setInputManager (InputManager *inputManager) { mInputManager = inputManager; } void MWBase::Environment::setStateManager (StateManager *stateManager) { mStateManager = stateManager; } void MWBase::Environment::setFrameDuration (float duration) { mFrameDuration = duration; } MWBase::World *MWBase::Environment::getWorld() const { assert (mWorld); return mWorld; } MWBase::SoundManager *MWBase::Environment::getSoundManager() const { assert (mSoundManager); return mSoundManager; } MWBase::ScriptManager *MWBase::Environment::getScriptManager() const { assert (mScriptManager); return mScriptManager; } MWBase::WindowManager *MWBase::Environment::getWindowManager() const { assert (mWindowManager); return mWindowManager; } MWBase::MechanicsManager *MWBase::Environment::getMechanicsManager() const { assert (mMechanicsManager); return mMechanicsManager; } MWBase::DialogueManager *MWBase::Environment::getDialogueManager() const { assert (mDialogueManager); return mDialogueManager; } MWBase::Journal *MWBase::Environment::getJournal() const { assert (mJournal); return mJournal; } MWBase::InputManager *MWBase::Environment::getInputManager() const { assert (mInputManager); return mInputManager; } MWBase::StateManager *MWBase::Environment::getStateManager() const { assert (mStateManager); return mStateManager; } float MWBase::Environment::getFrameDuration() const { return mFrameDuration; } void MWBase::Environment::cleanup() { delete mMechanicsManager; mMechanicsManager = 0; delete mDialogueManager; mDialogueManager = 0; delete mJournal; mJournal = 0; delete mScriptManager; mScriptManager = 0; delete mWindowManager; mWindowManager = 0; delete mWorld; mWorld = 0; delete mSoundManager; mSoundManager = 0; delete mInputManager; mInputManager = 0; delete mStateManager; mStateManager = 0; } const MWBase::Environment& MWBase::Environment::get() { assert (sThis); return *sThis; } openmw-openmw-0.38.0/apps/openmw/mwbase/environment.hpp000066400000000000000000000051621264522266000232260ustar00rootroot00000000000000#ifndef GAME_BASE_ENVIRONMENT_H #define GAME_BASE_ENVIRONMENT_H namespace MWBase { class World; class ScriptManager; class DialogueManager; class Journal; class SoundManager; class MechanicsManager; class InputManager; class WindowManager; class StateManager; /// \brief Central hub for mw-subsystems /// /// This class allows each mw-subsystem to access any others subsystem's top-level manager class. /// /// \attention Environment takes ownership of the manager class instances it is handed over in /// the set* functions. class Environment { static Environment *sThis; World *mWorld; SoundManager *mSoundManager; ScriptManager *mScriptManager; WindowManager *mWindowManager; MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; InputManager *mInputManager; StateManager *mStateManager; float mFrameDuration; Environment (const Environment&); ///< not implemented Environment& operator= (const Environment&); ///< not implemented public: Environment(); ~Environment(); void setWorld (World *world); void setSoundManager (SoundManager *soundManager); void setScriptManager (MWBase::ScriptManager *scriptManager); void setWindowManager (WindowManager *windowManager); void setMechanicsManager (MechanicsManager *mechanicsManager); void setDialogueManager (DialogueManager *dialogueManager); void setJournal (Journal *journal); void setInputManager (InputManager *inputManager); void setStateManager (StateManager *stateManager); void setFrameDuration (float duration); ///< Set length of current frame in seconds. World *getWorld() const; SoundManager *getSoundManager() const; ScriptManager *getScriptManager() const; WindowManager *getWindowManager() const; MechanicsManager *getMechanicsManager() const; DialogueManager *getDialogueManager() const; Journal *getJournal() const; InputManager *getInputManager() const; StateManager *getStateManager() const; float getFrameDuration() const; void cleanup(); ///< Delete all mw*-subsystems. static const Environment& get(); ///< Return instance of this class. }; } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/inputmanager.hpp000066400000000000000000000045071264522266000233560ustar00rootroot00000000000000#ifndef GAME_MWBASE_INPUTMANAGER_H #define GAME_MWBASE_INPUTMANAGER_H #include #include #include namespace MWBase { /// \brief Interface for input manager (implemented in MWInput) class InputManager { InputManager (const InputManager&); ///< not implemented InputManager& operator= (const InputManager&); ///< not implemented public: InputManager() {} /// Clear all savegame-specific data virtual void clear() = 0; virtual ~InputManager() {} virtual bool isWindowVisible() = 0; virtual void update(float dt, bool disableControls, bool disableEvents=false) = 0; virtual void changeInputMode(bool guiMode) = 0; virtual void processChangedSettings(const std::set< std::pair >& changed) = 0; virtual void setDragDrop(bool dragDrop) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; virtual bool getControlSwitch (const std::string& sw) = 0; virtual std::string getActionDescription (int action) = 0; virtual std::string getActionKeyBindingName (int action) = 0; virtual std::string getActionControllerBindingName (int action) = 0; virtual std::string sdlControllerAxisToString(int axis) = 0; virtual std::string sdlControllerButtonToString(int button) = 0; ///Actions available for binding to keyboard buttons virtual std::vector getActionKeySorting() = 0; ///Actions available for binding to controller buttons virtual std::vector getActionControllerSorting() = 0; virtual int getNumActions() = 0; ///If keyboard is true, only pay attention to keyboard events. If false, only pay attention to controller events (excluding esc) virtual void enableDetectingBindingMode (int action, bool keyboard) = 0; virtual void resetToDefaultKeyBindings() = 0; virtual void resetToDefaultControllerBindings() = 0; /// Returns if the last used input device was a joystick or a keyboard /// @return true if joystick, false otherwise virtual bool joystickLastUsed() = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/journal.hpp000066400000000000000000000062151264522266000223340ustar00rootroot00000000000000#ifndef GAME_MWBASE_JOURNAL_H #define GAME_MWBASE_JOURNAL_H #include #include #include #include #include "../mwdialogue/journalentry.hpp" #include "../mwdialogue/topic.hpp" #include "../mwdialogue/quest.hpp" namespace Loading { class Listener; } namespace ESM { class ESMReader; class ESMWriter; } namespace MWBase { /// \brief Interface for the player's journal (implemented in MWDialogue) class Journal { Journal (const Journal&); ///< not implemented Journal& operator= (const Journal&); ///< not implemented public: typedef std::deque TEntryContainer; typedef TEntryContainer::const_iterator TEntryIter; typedef std::map TQuestContainer; // topic, quest typedef TQuestContainer::const_iterator TQuestIter; typedef std::map TTopicContainer; // topic-id, topic-content typedef TTopicContainer::const_iterator TTopicIter; public: Journal() {} virtual void clear() = 0; virtual ~Journal() {} virtual void addEntry (const std::string& id, int index) = 0; ///< Add a journal entry. virtual void setJournalIndex (const std::string& id, int index) = 0; ///< Set the journal index without adding an entry. virtual int getJournalIndex (const std::string& id) const = 0; ///< Get the journal index. virtual void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor) = 0; /// \note topicId must be lowercase virtual void removeLastAddedTopicResponse (const std::string& topicId, const std::string& actorName) = 0; ///< Removes the last topic response added for the given topicId and actor name. /// \note topicId must be lowercase virtual TEntryIter begin() const = 0; ///< Iterator pointing to the begin of the main journal. /// /// \note Iterators to main journal entries will never become invalid. virtual TEntryIter end() const = 0; ///< Iterator pointing past the end of the main journal. virtual TQuestIter questBegin() const = 0; ///< Iterator pointing to the first quest (sorted by topic ID) virtual TQuestIter questEnd() const = 0; ///< Iterator pointing past the last quest. virtual TTopicIter topicBegin() const = 0; ///< Iterator pointing to the first topic (sorted by topic ID) /// /// \note The topic ID is identical with the user-visible topic string. virtual TTopicIter topicEnd() const = 0; ///< Iterator pointing past the last topic. virtual int countSavedGameRecords() const = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0; virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/mechanicsmanager.hpp000066400000000000000000000246441264522266000241550ustar00rootroot00000000000000#ifndef GAME_MWBASE_MECHANICSMANAGER_H #define GAME_MWBASE_MECHANICSMANAGER_H #include #include #include #include namespace osg { class Vec3f; } namespace ESM { struct Class; class ESMReader; class ESMWriter; } namespace MWWorld { class Ptr; class CellStore; class CellRef; } namespace Loading { class Listener; } namespace MWBase { /// \brief Interface for game mechanics manager (implemented in MWMechanics) class MechanicsManager { MechanicsManager (const MechanicsManager&); ///< not implemented MechanicsManager& operator= (const MechanicsManager&); ///< not implemented public: MechanicsManager() {} virtual ~MechanicsManager() {} virtual void add (const MWWorld::Ptr& ptr) = 0; ///< Register an object for management virtual void remove (const MWWorld::Ptr& ptr) = 0; ///< Deregister an object for management virtual void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) = 0; ///< Moves an object to a new cell virtual void drop (const MWWorld::CellStore *cellStore) = 0; ///< Deregister all objects in the given cell. virtual void watchActor (const MWWorld::Ptr& ptr) = 0; ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. virtual void update (float duration, bool paused) = 0; ///< Update objects /// /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). virtual void advanceTime (float duration) = 0; virtual void setPlayerName (const std::string& name) = 0; ///< Set player name. virtual void setPlayerRace (const std::string& id, bool male, const std::string &head, const std::string &hair) = 0; ///< Set player race. virtual void setPlayerBirthsign (const std::string& id) = 0; ///< Set player birthsign. virtual void setPlayerClass (const std::string& id) = 0; ///< Set player class to stock class. virtual void setPlayerClass (const ESM::Class& class_) = 0; ///< Set player class to custom class. virtual void rest(bool sleep) = 0; ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? virtual int getHoursToRest() const = 0; ///< Calculate how many hours the player needs to rest in order to be fully healed virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) = 0; ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. virtual int getDerivedDisposition(const MWWorld::Ptr& ptr) = 0; ///< Calculate the diposition of an NPC toward the player. virtual int countDeaths (const std::string& id) const = 0; ///< Return the number of deaths for actors with the given ID. /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0; /// Makes \a ptr fight \a target. Also shouts a combat taunt. virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; enum OffenseType { OT_Theft, // Taking items owned by an NPC or a faction you are not a member of OT_Assault, // Attacking a peaceful NPC OT_Murder, // Murdering a peaceful NPC OT_Trespassing, // Picking the lock of an owned door/chest OT_SleepingInOwnedBed, // Sleeping in a bed owned by an NPC or a faction you are not a member of OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft) }; /** * @note victim may be empty * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @param victimAware Is the victim already aware of the crime? * If this parameter is false, it will be determined by a line-of-sight and awareness check. * @return was the crime seen? */ virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0, bool victimAware=false) = 0; /// @return false if the attack was considered a "friendly hit" and forgiven virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0; /// Notify that actor was killed, add a murder bounty if applicable /// @note No-op for non-player attackers virtual void actorKilled (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0; /// Utility to check if taking this item is illegal and calling commitCrime if so /// @param container The container the item is in; may be empty for an item in the world virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container, int count) = 0; /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0; /// Attempt sleeping in a bed. If this is illegal, call commitCrime. /// @return was it illegal, and someone saw you doing it? virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0; enum PersuasionType { PT_Admire, PT_Intimidate, PT_Taunt, PT_Bribe10, PT_Bribe100, PT_Bribe1000 }; virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; ///< Perform a persuasion action on NPC virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; ///< Forces an object to refresh its animation state. virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; ///< Run animation for a MW-reference. Calls to this function for references that are currently not /// in the scene should be ignored. /// /// \param mode 0 normal, 1 immediate start, 2 immediate loop /// \param count How many times the animation should be run /// \return Success or error virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0; ///< Skip the animation for the given MW-reference for one frame. Calls to this function for /// references that are currently not in the scene should be ignored. virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0; /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0; virtual bool toggleAI() = 0; virtual bool isAIActive() = 0; virtual void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector& objects) = 0; virtual void getActorsInRange(const osg::Vec3f &position, float radius, std::vector &objects) = 0; ///Returns the list of actors which are siding with the given actor in fights /**ie AiFollow or AiEscort is active and the target is the actor **/ virtual std::list getActorsSidingWith(const MWWorld::Ptr& actor) = 0; virtual std::list getActorsFollowing(const MWWorld::Ptr& actor) = 0; virtual std::list getActorsFollowingIndices(const MWWorld::Ptr& actor) = 0; ///Returns a list of actors who are fighting the given actor within the fAlarmDistance /** ie AiCombat is active and the target is the actor **/ virtual std::list getActorsFighting(const MWWorld::Ptr& actor) = 0; virtual void playerLoaded() = 0; virtual int countSavedGameRecords() const = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0; virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0; virtual void clear() = 0; virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; /// Resurrects the player if necessary virtual void keepPlayerAlive() = 0; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0; virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0; /// List the owners that the player has stolen this item from (the owner can be an NPC or a faction). /// virtual std::vector > getStolenItemOwners(const std::string& itemid) = 0; /// Has the player stolen this item from the given owner? virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0; virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim) = 0; /// Turn actor into werewolf or normal form. virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0; /// Sets the NPC's Acrobatics skill to match the fWerewolfAcrobatics GMST. /// It only applies to the current form the NPC is in. virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/scriptmanager.hpp000066400000000000000000000024451264522266000235220ustar00rootroot00000000000000#ifndef GAME_MWBASE_SCRIPTMANAGER_H #define GAME_MWBASE_SCRIPTMANAGER_H #include namespace Interpreter { class Context; } namespace Compiler { class Locals; } namespace MWScript { class GlobalScripts; } namespace MWBase { /// \brief Interface for script manager (implemented in MWScript) class ScriptManager { ScriptManager (const ScriptManager&); ///< not implemented ScriptManager& operator= (const ScriptManager&); ///< not implemented public: ScriptManager() {} virtual ~ScriptManager() {} virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0; ///< Run the script with the given name (compile first, if not compiled yet) virtual bool compile (const std::string& name) = 0; ///< Compile script with the given namen /// \return Success? virtual std::pair compileAll() = 0; ///< Compile all scripts /// \return count, success virtual const Compiler::Locals& getLocals (const std::string& name) = 0; ///< Return locals for script \a name. virtual MWScript::GlobalScripts& getGlobalScripts() = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/soundmanager.hpp000066400000000000000000000203151264522266000233420ustar00rootroot00000000000000#ifndef GAME_MWBASE_SOUNDMANAGER_H #define GAME_MWBASE_SOUNDMANAGER_H #include #include #include #include "../mwworld/ptr.hpp" namespace MWWorld { class CellStore; } namespace MWSound { class Sound; class Stream; struct Sound_Decoder; typedef boost::shared_ptr DecoderPtr; } namespace MWBase { typedef boost::shared_ptr SoundPtr; typedef boost::shared_ptr SoundStreamPtr; /// \brief Interface for sound manager (implemented in MWSound) class SoundManager { public: /* These must all fit together */ enum PlayMode { Play_Normal = 0, /* non-looping, affected by environment */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ Play_RemoveAtDistance = 1<<2, /* (3D only) If the listener gets further than 2000 units away from the sound source, the sound is removed. This is weird stuff but apparently how vanilla works for sounds played by the PlayLoopSound family of script functions. Perhaps we can make this cut off a more subtle fade later, but have to be careful to not change the overall volume of areas by too much. */ Play_NoPlayerLocal = 1<<3, /* (3D only) Don't play the sound local to the listener even if the player is making it. */ Play_LoopNoEnv = Play_Loop | Play_NoEnv, Play_LoopRemoveAtDistance = Play_Loop | Play_RemoveAtDistance }; enum PlayType { Play_TypeSfx = 1<<4, /* Normal SFX sound */ Play_TypeVoice = 1<<5, /* Voice sound */ Play_TypeFoot = 1<<6, /* Footstep sound */ Play_TypeMusic = 1<<7, /* Music track */ Play_TypeMovie = 1<<8, /* Movie audio track */ Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeFoot|Play_TypeMusic|Play_TypeMovie }; private: SoundManager (const SoundManager&); ///< not implemented SoundManager& operator= (const SoundManager&); ///< not implemented public: SoundManager() {} virtual ~SoundManager() {} virtual void processChangedSettings(const std::set< std::pair >& settings) = 0; virtual void stopMusic() = 0; ///< Stops music if it's playing virtual void streamMusic(const std::string& filename) = 0; ///< Play a soundifle /// \param filename name of a sound file in "Music/" in the data directory. virtual void startRandomTitle() = 0; ///< Starts a random track from the current playlist virtual bool isMusicPlaying() = 0; ///< Returns true if music is playing virtual void playPlaylist(const std::string &playlist) = 0; ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename) = 0; ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. virtual void say(const std::string& filename) = 0; ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. virtual bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const = 0; ///< Is actor not speaking? virtual void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) = 0; ///< Stop an actor speaking virtual float getSaySoundLoudness(const MWWorld::ConstPtr& reference) const = 0; ///< Check the currently playing say sound for this actor /// and get an average loudness value (scale [0,1]) at the current time position. /// If the actor is not saying anything, returns 0. virtual SoundStreamPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; ///< Play a 2D audio track, using a custom decoder virtual void stopTrack(SoundStreamPtr stream) = 0; ///< Stop the given audio track from playing virtual double getTrackTimeDelay(SoundStreamPtr stream) = 0; ///< Retives the time delay, in seconds, of the audio track (must be a sound /// returned by \ref playTrack). Only intended to be called by the track /// decoder's read method. virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0) = 0; ///< Play a sound, independently of 3D-position ///< @param offset Number of seconds into the sound to start playback. virtual MWBase::SoundPtr playSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0) = 0; ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified. ///< @param offset Number of seconds into the sound to start playback. virtual MWBase::SoundPtr playSound3D(const osg::Vec3f& initialPos, const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0) = 0; ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated using Sound::setPosition. virtual void stopSound(SoundPtr sound) = 0; ///< Stop the given sound from playing virtual void stopSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId) = 0; ///< Stop the given object from playing the given sound, virtual void stopSound3D(const MWWorld::ConstPtr &reference) = 0; ///< Stop the given object from playing all sounds. virtual void stopSound(const MWWorld::CellStore *cell) = 0; ///< Stop all sounds for the given cell. virtual void stopSound(const std::string& soundId) = 0; ///< Stop a non-3d looping sound virtual void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration) = 0; ///< Fade out given sound (that is already playing) of given object ///< @param reference Reference to object, whose sound is faded out ///< @param soundId ID of the sound to fade out. ///< @param duration Time until volume reaches 0. virtual bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const = 0; ///< Is the given sound currently playing on the given object? /// If you want to check if sound played with playSound is playing, use empty Ptr virtual void pauseSounds(int types=Play_TypeMask) = 0; ///< Pauses all currently playing sounds, including music. virtual void resumeSounds(int types=Play_TypeMask) = 0; ///< Resumes all previously paused sounds. virtual void update(float duration) = 0; virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater) = 0; virtual void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) = 0; virtual void clear() = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/statemanager.hpp000066400000000000000000000055631264522266000233420ustar00rootroot00000000000000#ifndef GAME_MWSTATE_STATEMANAGER_H #define GAME_MWSTATE_STATEMANAGER_H #include #include namespace MWState { struct Slot; class Character; } namespace MWBase { /// \brief Interface for game state manager (implemented in MWState) class StateManager { public: enum State { State_NoGame, State_Ended, State_Running }; typedef std::list::const_iterator CharacterIterator; private: StateManager (const StateManager&); ///< not implemented StateManager& operator= (const StateManager&); ///< not implemented public: StateManager() {} virtual ~StateManager() {} virtual void requestQuit() = 0; virtual bool hasQuitRequest() const = 0; virtual void askLoadRecent() = 0; virtual State getState() const = 0; virtual void newGame (bool bypass = false) = 0; ///< Start a new game. /// /// \param bypass Skip new game mechanics. virtual void endGame() = 0; virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot) = 0; virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0; ///< Write a saved game to \a slot or create a new slot if \a slot == 0. /// /// \note Slot must belong to the current character. virtual void loadGame (const std::string& filepath) = 0; ///< Load a saved game directly from the given file path. This will search the CharacterManager /// for a Character containing this save file, and set this Character current if one was found. /// Otherwise, a new Character will be created. virtual void loadGame (const MWState::Character *character, const std::string& filepath) = 0; ///< Load a saved game file belonging to the given character. ///Simple saver, writes over the file if already existing /** Used for quick save and autosave **/ virtual void quickSave(std::string = "Quicksave")=0; ///Simple loader, loads the last saved file /** Used for quickload **/ virtual void quickLoad()=0; virtual MWState::Character *getCurrentCharacter (bool create = true) = 0; ///< \param create Create a new character, if there is no current character. virtual CharacterIterator characterBegin() = 0; ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned /// iterator. virtual CharacterIterator characterEnd() = 0; virtual void update (float duration) = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/windowmanager.hpp000066400000000000000000000336701264522266000235310ustar00rootroot00000000000000#ifndef GAME_MWBASE_WINDOWMANAGER_H #define GAME_MWBASE_WINDOWMANAGER_H #include #include #include #include #include #include "../mwgui/mode.hpp" namespace Loading { class Listener; } namespace Translation { class Storage; } namespace MyGUI { class Gui; class Widget; class UString; } namespace ESM { struct Class; class ESMReader; class ESMWriter; struct CellId; } namespace MWMechanics { class AttributeValue; template class DynamicStat; class SkillValue; } namespace MWWorld { class CellStore; class Ptr; } namespace MWGui { class Layout; class Console; class SpellWindow; class TradeWindow; class TravelWindow; class SpellBuyingWindow; class ConfirmationDialog; class CountDialog; class ScrollWindow; class BookWindow; class InventoryWindow; class ContainerWindow; class DialogueWindow; class WindowModal; class JailScreen; enum ShowInDialogueMode { ShowInDialogueMode_IfPossible, ShowInDialogueMode_Only, ShowInDialogueMode_Never }; } namespace SFO { class CursorManager; } namespace MWBase { /// \brief Interface for widnow manager (implemented in MWGui) class WindowManager { WindowManager (const WindowManager&); ///< not implemented WindowManager& operator= (const WindowManager&); ///< not implemented public: typedef std::vector SkillList; WindowManager() {} virtual ~WindowManager() {} /** * Should be called each frame to update windows/gui elements. * This could mean updating sizes of gui elements or opening * new dialogs. */ virtual void update() = 0; /// @note This method will block until the video finishes playing /// (and will continually update the window while doing so) virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void setNewGame(bool newgame) = 0; virtual void pushGuiMode (MWGui::GuiMode mode) = 0; virtual void popGuiMode() = 0; virtual void removeGuiMode (MWGui::GuiMode mode) = 0; ///< can be anywhere in the stack virtual void goToJail(int days) = 0; virtual void updatePlayer() = 0; virtual MWGui::GuiMode getMode() const = 0; virtual bool containsMode(MWGui::GuiMode) const = 0; virtual bool isGuiMode() const = 0; virtual bool isConsoleMode() const = 0; virtual void toggleVisible (MWGui::GuiWindow wnd) = 0; virtual void forceHide(MWGui::GuiWindow wnd) = 0; virtual void unsetForceHide(MWGui::GuiWindow wnd) = 0; /// Disallow all inventory mode windows virtual void disallowAll() = 0; /// Allow one or more windows virtual void allow (MWGui::GuiWindow wnd) = 0; virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0; /// \todo investigate, if we really need to expose every single lousy UI element to the outside world virtual MWGui::DialogueWindow* getDialogueWindow() = 0; virtual MWGui::InventoryWindow* getInventoryWindow() = 0; virtual MWGui::CountDialog* getCountDialog() = 0; virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; virtual MWGui::TradeWindow* getTradeWindow() = 0; /// Make the player use an item, while updating GUI state accordingly virtual void useItem(const MWWorld::Ptr& item) = 0; virtual void updateSpellWindow() = 0; virtual void setConsoleSelectedObject(const MWWorld::Ptr& object) = 0; /// Set value for the given ID. virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value) = 0; virtual void setValue (int parSkill, const MWMechanics::SkillValue& value) = 0; virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; virtual void setValue (const std::string& id, const std::string& value) = 0; virtual void setValue (const std::string& id, int value) = 0; /// Set time left for the player to start drowning (update the drowning bar) /// @param time time left to start drowning /// @param maxTime how long we can be underwater (in total) until drowning starts virtual void setDrowningTimeLeft (float time, float maxTime) = 0; virtual void setPlayerClass (const ESM::Class &class_) = 0; ///< set current class of player virtual void configureSkills (const SkillList& major, const SkillList& minor) = 0; ///< configure skill groups, each set contains the skill ID for that group. virtual void updateSkillArea() = 0; ///< update display of skills, factions, birth sign, reputation and bounty virtual void changeCell(MWWorld::CellStore* cell) = 0; ///< change the active cell virtual void setFocusObject(const MWWorld::Ptr& focus) = 0; virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) = 0; virtual void setCursorVisible(bool visible) = 0; virtual void getMousePosition(int &x, int &y) = 0; virtual void getMousePosition(float &x, float &y) = 0; virtual void setDragDrop(bool dragDrop) = 0; virtual bool getWorldMouseOver() = 0; virtual bool toggleFogOfWar() = 0; virtual bool toggleFullHelp() = 0; ///< show extra info in item tooltips (owner, script) virtual bool getFullHelp() const = 0; virtual void setActiveMap(int x, int y, bool interior) = 0; ///< set the indices of the map texture that should be used /// sets the visibility of the drowning bar virtual void setDrowningBarVisibility(bool visible) = 0; /// sets the visibility of the hud health/magicka/stamina bars virtual void setHMSVisibility(bool visible) = 0; /// sets the visibility of the hud minimap virtual void setMinimapVisibility(bool visible) = 0; virtual void setWeaponVisibility(bool visible) = 0; virtual void setSpellVisibility(bool visible) = 0; virtual void setSneakVisibility(bool visible) = 0; virtual void activateQuickKey (int index) = 0; virtual std::string getSelectedSpell() = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; virtual void showCrosshair(bool show) = 0; virtual bool getSubtitlesEnabled() = 0; virtual bool toggleGui() = 0; virtual void disallowMouse() = 0; virtual void allowMouse() = 0; virtual void notifyInputActionBound() = 0; virtual void addVisitedLocation(const std::string& name, int x, int y) = 0; /// Hides dialog and schedules dialog to be deleted. virtual void removeDialog(MWGui::Layout* dialog) = 0; ///Gracefully attempts to exit the topmost GUI mode /** No guarentee of actually closing the window **/ virtual void exitCurrentGuiMode() = 0; virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; virtual void staticMessageBox(const std::string& message) = 0; virtual void removeStaticMessageBox() = 0; virtual void interactiveMessageBox (const std::string& message, const std::vector& buttons = std::vector(), bool block=false) = 0; /// returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual int readPressedButton() = 0; virtual void onFrame (float frameDuration) = 0; /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. virtual std::map getPlayerSkillValues() = 0; virtual std::map getPlayerAttributeValues() = 0; virtual SkillList getPlayerMinorSkills() = 0; virtual SkillList getPlayerMajorSkills() = 0; /** * Fetches a GMST string from the store, if there is no setting with the given * ID or it is not a string the default string is returned. * * @param id Identifier for the GMST setting, e.g. "aName" * @param default Default value if the GMST setting cannot be used. */ virtual std::string getGameSettingString(const std::string &id, const std::string &default_) = 0; virtual void processChangedSettings(const std::set< std::pair >& changed) = 0; virtual void windowResized(int x, int y) = 0; virtual void executeInConsole (const std::string& path) = 0; virtual void enableRest() = 0; virtual bool getRestEnabled() = 0; virtual bool getJournalAllowed() = 0; virtual bool getPlayerSleeping() = 0; virtual void wakeUpPlayer() = 0; virtual void showCompanionWindow(MWWorld::Ptr actor) = 0; virtual void startSpellMaking(MWWorld::Ptr actor) = 0; virtual void startEnchanting(MWWorld::Ptr actor) = 0; virtual void startRecharge(MWWorld::Ptr soulgem) = 0; virtual void startSelfEnchanting(MWWorld::Ptr soulgem) = 0; virtual void startTraining(MWWorld::Ptr actor) = 0; virtual void startRepair(MWWorld::Ptr actor) = 0; virtual void startRepairItem(MWWorld::Ptr item) = 0; virtual void startTravel(const MWWorld::Ptr& actor) = 0; virtual void startSpellBuying(const MWWorld::Ptr& actor) = 0; virtual void startTrade(const MWWorld::Ptr& actor) = 0; virtual void openContainer(const MWWorld::Ptr& container, bool loot) = 0; virtual void showBook(const MWWorld::Ptr& item, bool showTakeButton) = 0; virtual void showScroll(const MWWorld::Ptr& item, bool showTakeButton) = 0; virtual void showSoulgemDialog (MWWorld::Ptr item) = 0; virtual void changePointer (const std::string& name) = 0; virtual void setEnemy (const MWWorld::Ptr& enemy) = 0; virtual const Translation::Storage& getTranslationDataStorage() const = 0; /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; virtual Loading::Listener* getLoadingScreen() = 0; /// Should the cursor be visible? virtual bool getCursorVisible() = 0; /// Clear all savegame-specific data virtual void clear() = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) = 0; virtual void readRecord (ESM::ESMReader& reader, uint32_t type) = 0; virtual int countSavedGameRecords() const = 0; /// Does the current stack of GUI-windows permit saving? virtual bool isSavingAllowed() const = 0; /// Send exit command to active Modal window virtual void exitCurrentModal() = 0; /// Sets the current Modal /** Used to send exit command to active Modal when Esc is pressed **/ virtual void addCurrentModal(MWGui::WindowModal* input) = 0; /// Removes the top Modal /** Used when one Modal adds another Modal \param input Pointer to the current modal, to ensure proper modal is removed **/ virtual void removeCurrentModal(MWGui::WindowModal* input) = 0; virtual void pinWindow (MWGui::GuiWindow window) = 0; /// Fade the screen in, over \a time seconds virtual void fadeScreenIn(const float time, bool clearQueue=true) = 0; /// Fade the screen out to black, over \a time seconds virtual void fadeScreenOut(const float time, bool clearQueue=true) = 0; /// Fade the screen to a specified percentage of black, over \a time seconds virtual void fadeScreenTo(const int percent, const float time, bool clearQueue=true) = 0; /// Darken the screen to a specified percentage virtual void setBlindness(const int percent) = 0; virtual void activateHitOverlay(bool interrupt=true) = 0; virtual void setWerewolfOverlay(bool set) = 0; virtual void toggleDebugWindow() = 0; /// Cycle to next or previous spell virtual void cycleSpell(bool next) = 0; /// Cycle to next or previous weapon virtual void cycleWeapon(bool next) = 0; // In WindowManager for now since there isn't a VFS singleton virtual std::string correctIconPath(const std::string& path) = 0; virtual std::string correctBookartPath(const std::string& path, int width, int height) = 0; virtual std::string correctTexturePath(const std::string& path) = 0; virtual void requestMap(std::set cells) = 0; virtual void removeCell(MWWorld::CellStore* cell) = 0; virtual void writeFog(MWWorld::CellStore* cell) = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwbase/world.hpp000066400000000000000000000605021264522266000220100ustar00rootroot00000000000000#ifndef GAME_MWBASE_WORLD_H #define GAME_MWBASE_WORLD_H #include #include #include #include #include "../mwworld/ptr.hpp" #include "../mwrender/rendermode.hpp" namespace osg { class Vec3f; class Matrixf; class Quat; class Image; } namespace Loading { class Listener; } namespace ESM { class ESMReader; class ESMWriter; struct Position; struct Cell; struct Class; struct Potion; struct Spell; struct NPC; struct Armor; struct Weapon; struct Clothing; struct Enchantment; struct Book; struct EffectList; struct CreatureLevList; struct ItemLevList; } namespace MWRender { class Animation; } namespace MWMechanics { struct Movement; } namespace MWWorld { class Fallback; class CellStore; class Player; class LocalScripts; class TimeStamp; class ESMStore; class RefData; typedef std::vector > PtrMovementList; } namespace MWBase { /// \brief Interface for the World (implemented in MWWorld) class World { World (const World&); ///< not implemented World& operator= (const World&); ///< not implemented public: struct DoorMarker { std::string name; float x, y; // world position ESM::CellId dest; }; World() {} virtual ~World() {} virtual void startNewGame (bool bypass) = 0; ///< \param bypass Bypass regular game start. virtual void clear() = 0; virtual int countSavedGameRecords() const = 0; virtual int countSavedGameCells() const = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0; virtual void readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap) = 0; virtual MWWorld::CellStore *getExterior (int x, int y) = 0; virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0; virtual void useDeathCamera() = 0; virtual void setWaterHeight(const float height) = 0; virtual bool toggleWater() = 0; virtual bool toggleWorld() = 0; virtual void adjustSky() = 0; virtual const MWWorld::Fallback *getFallback () const = 0; virtual MWWorld::Player& getPlayer() = 0; virtual MWWorld::Ptr getPlayerPtr() = 0; virtual const MWWorld::ESMStore& getStore() const = 0; virtual std::vector& getEsmReader() = 0; virtual MWWorld::LocalScripts& getLocalScripts() = 0; virtual bool hasCellChanged() const = 0; ///< Has the set of active cells changed, since the last frame? virtual bool isCellExterior() const = 0; virtual bool isCellQuasiExterior() const = 0; virtual osg::Vec2f getNorthVector (MWWorld::CellStore* cell) = 0; ///< get north vector for given interior cell virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out) = 0; ///< get a list of teleport door markers for a given cell, to be displayed on the local map virtual void setGlobalInt (const std::string& name, int value) = 0; ///< Set value independently from real type. virtual void setGlobalFloat (const std::string& name, float value) = 0; ///< Set value independently from real type. virtual int getGlobalInt (const std::string& name) const = 0; ///< Get value independently from real type. virtual float getGlobalFloat (const std::string& name) const = 0; ///< Get value independently from real type. virtual char getGlobalVariableType (const std::string& name) const = 0; ///< Return ' ', if there is no global variable with this name. virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const = 0; ///< Return name of the cell. /// /// \note If cell==0, the cell the player is currently in will be used instead to /// generate a name. virtual void removeRefScript (MWWorld::RefData *ref) = 0; //< Remove the script attached to ref from mLocalScripts virtual MWWorld::Ptr getPtr (const std::string& name, bool activeOnly) = 0; ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. virtual MWWorld::Ptr searchPtr (const std::string& name, bool activeOnly) = 0; ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0; ///< Search is limited to the active cells. virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr) = 0; ///< Return a pointer to a liveCellRef which contains \a ptr. /// \note Search is limited to the active cells. virtual void enable (const MWWorld::Ptr& ptr) = 0; virtual void disable (const MWWorld::Ptr& ptr) = 0; virtual void advanceTime (double hours, bool incremental = false) = 0; ///< Advance in-game time. virtual void setHour (double hour) = 0; ///< Set in-game time hour. virtual void setMonth (int month) = 0; ///< Set in-game time month. virtual void setDay (int day) = 0; ///< Set in-game time day. virtual int getDay() const = 0; virtual int getMonth() const = 0; virtual int getYear() const = 0; virtual std::string getMonthName (int month = -1) const = 0; ///< Return name of month (-1: current month) virtual MWWorld::TimeStamp getTimeStamp() const = 0; ///< Return current in-game time stamp. virtual bool toggleSky() = 0; ///< \return Resulting mode virtual void changeWeather(const std::string& region, unsigned int id) = 0; virtual int getCurrentWeather() const = 0; virtual int getMasserPhase() const = 0; virtual int getSecundaPhase() const = 0; virtual void setMoonColour (bool red) = 0; virtual void modRegion(const std::string ®ionid, const std::vector &chances) = 0; virtual float getTimeScaleFactor() const = 0; virtual void changeToInteriorCell (const std::string& cellName, const ESM::Position& position) = 0; ///< Move to interior cell. virtual void changeToExteriorCell (const ESM::Position& position) = 0; ///< Move to exterior cell. virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true) = 0; ///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. virtual void markCellAsUnchanged() = 0; virtual MWWorld::Ptr getFacedObject() = 0; ///< Return pointer to the object the player is looking at, if it is within activation range virtual float getMaxActivationDistance() = 0; /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis. virtual std::pair getHitContact(const MWWorld::ConstPtr &ptr, float distance) = 0; virtual void adjustPosition (const MWWorld::Ptr& ptr, bool force) = 0; ///< Adjust position after load to be on ground. Must be called after model load. /// @param force do this even if the ptr is flying virtual void fixPosition (const MWWorld::Ptr& actor) = 0; ///< Attempt to fix position so that the Ptr is no longer inside collision geometry. /// @note No-op for items in containers. Use ContainerStore::removeItem instead. virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0; virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; ///< @return an updated Ptr in case the Ptr's cell changes virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z) = 0; ///< @return an updated Ptr virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; virtual MWWorld::Ptr safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0; ///< place an object in a "safe" location (ie not in the void, etc). virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const = 0; ///< Convert cell numbers to position. virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0; ///< Convert position to cell numbers virtual void queueMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity) = 0; ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; ///< cast a Ray and return true if there is an object in the ray path. virtual bool toggleCollisionMode() = 0; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. /// \return Resulting mode virtual bool toggleRenderMode (MWRender::RenderMode mode) = 0; ///< Toggle a render mode. ///< \return Resulting mode virtual const ESM::Potion *createRecord (const ESM::Potion& record) = 0; ///< Create a new record (of type potion) in the ESM store. /// \return pointer to created record virtual const ESM::Spell *createRecord (const ESM::Spell& record) = 0; ///< Create a new record (of type spell) in the ESM store. /// \return pointer to created record virtual const ESM::Class *createRecord (const ESM::Class& record) = 0; ///< Create a new record (of type class) in the ESM store. /// \return pointer to created record virtual const ESM::Cell *createRecord (const ESM::Cell& record) = 0; ///< Create a new record (of type cell) in the ESM store. /// \return pointer to created record virtual const ESM::NPC *createRecord(const ESM::NPC &record) = 0; ///< Create a new record (of type npc) in the ESM store. /// \return pointer to created record virtual const ESM::Armor *createRecord (const ESM::Armor& record) = 0; ///< Create a new record (of type armor) in the ESM store. /// \return pointer to created record virtual const ESM::Weapon *createRecord (const ESM::Weapon& record) = 0; ///< Create a new record (of type weapon) in the ESM store. /// \return pointer to created record virtual const ESM::Clothing *createRecord (const ESM::Clothing& record) = 0; ///< Create a new record (of type clothing) in the ESM store. /// \return pointer to created record virtual const ESM::Enchantment *createRecord (const ESM::Enchantment& record) = 0; ///< Create a new record (of type enchantment) in the ESM store. /// \return pointer to created record virtual const ESM::Book *createRecord (const ESM::Book& record) = 0; ///< Create a new record (of type book) in the ESM store. /// \return pointer to created record virtual const ESM::CreatureLevList *createOverrideRecord (const ESM::CreatureLevList& record) = 0; ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record virtual const ESM::ItemLevList *createOverrideRecord (const ESM::ItemLevList& record) = 0; ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record virtual void update (float duration, bool paused) = 0; virtual MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) = 0; ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @param number of objects to place virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount) = 0; ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position /// @param object /// @param number of objects to place virtual bool canPlaceObject (float cursorX, float cursorY) = 0; ///< @return true if it is possible to place on object at specified cursor location virtual void processChangedSettings (const std::set< std::pair >& settings) = 0; virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0; virtual bool isSwimming(const MWWorld::ConstPtr &object) const = 0; virtual bool isWading(const MWWorld::ConstPtr &object) const = 0; ///Is the head of the creature underwater? virtual bool isSubmerged(const MWWorld::ConstPtr &object) const = 0; virtual bool isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0; virtual void togglePOV() = 0; virtual bool isFirstPerson() const = 0; virtual void togglePreviewMode(bool enable) = 0; virtual bool toggleVanityMode(bool enable) = 0; virtual void allowVanityMode(bool allow) = 0; virtual void togglePlayerLooking(bool enable) = 0; virtual void changeVanityModeScale(float factor) = 0; virtual bool vanityRotateCamera(float * rot) = 0; virtual void setCameraDistance(float dist, bool adjust = false, bool override = true)=0; virtual void setupPlayer() = 0; virtual void renderPlayer() = 0; /// open or close a non-teleport door (depending on current state) virtual void activateDoor(const MWWorld::Ptr& door) = 0; /// update movement state of a non-teleport door as specified /// @param state see MWClass::setDoorState /// @note throws an exception when invoked on a teleport door virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0; virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \a object virtual bool getActorCollidingWith (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is colliding with \a object virtual void hurtStandingActors (const MWWorld::ConstPtr& object, float dmgPerSecond) = 0; ///< Apply a health difference to any actors standing on \a object. /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. virtual void hurtCollidingActors (const MWWorld::ConstPtr& object, float dmgPerSecond) = 0; ///< Apply a health difference to any actors colliding with \a object. /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. virtual float getWindSpeed() = 0; virtual void getContainersOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out) = 0; ///< get all containers in active cells owned by this Npc virtual void getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out) = 0; ///< get all items in active cells owned by this Npc virtual bool getLOS(const MWWorld::ConstPtr& actor,const MWWorld::ConstPtr& targetActor) = 0; ///< get Line of Sight (morrowind stupid implementation) virtual float getDistToNearestRayHit(const osg::Vec3f& from, const osg::Vec3f& dir, float maxDist) = 0; virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; virtual int canRest() = 0; ///< check if the player is allowed to rest \n /// 0 - yes \n /// 1 - only waiting \n /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; virtual const MWRender::Animation* getAnimation(const MWWorld::ConstPtr &ptr) const = 0; virtual void reattachPlayerCamera() = 0; /// \todo this does not belong here virtual void screenshot (osg::Image* image, int w, int h) = 0; /// Find default position inside exterior cell specified by name /// \return false if exterior with given name not exists, true otherwise virtual bool findExteriorPosition(const std::string &name, ESM::Position &pos) = 0; /// Find default position inside interior cell specified by name /// \return false if interior with given name not exists, true otherwise virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos) = 0; /// Enables or disables use of teleport spell effects (recall, intervention, etc). virtual void enableTeleporting(bool enable) = 0; /// Returns true if teleport spell effects are allowed. virtual bool isTeleportingEnabled() const = 0; /// Enables or disables use of levitation spell effect. virtual void enableLevitation(bool enable) = 0; /// Returns true if levitation spell effect is allowed. virtual bool isLevitationEnabled() const = 0; virtual bool getGodModeState() = 0; virtual bool toggleGodMode() = 0; virtual bool toggleScripts() = 0; virtual bool getScriptsEnabled() const = 0; /** * @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met. * @param actor * @return true if the spell can be casted (i.e. the animation should start) */ virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0; virtual void castSpell (const MWWorld::Ptr& actor) = 0; virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection) = 0; virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0; virtual const std::vector& getContentFiles() const = 0; virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; // Are we in an exterior or pseudo-exterior cell and it's night? virtual bool isDark() const = 0; virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, osg::Vec3f& result) = 0; /// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id) = 0; enum DetectionType { Detect_Enchantment, Detect_Key, Detect_Creature }; /// List all references (filtered by \a type) detected by \a ptr. The range /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. /// @note This also works for references in containers. virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, DetectionType type) = 0; /// Update the value of some globals according to the world state, which may be used by dialogue entries. /// This should be called when initiating a dialogue. virtual void updateDialogueGlobals() = 0; /// Moves all stolen items from \a ptr to the closest evidence chest. virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0; virtual void goToJail () = 0; /// Spawn a random creature from a levelled list next to the player virtual void spawnRandomCreature(const std::string& creatureList) = 0; /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0; virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) = 0; virtual void explodeSpell (const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName) = 0; virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; /// @see MWWorld::WeatherManager::isInStorm virtual bool isInStorm() const = 0; /// @see MWWorld::WeatherManager::getStormDirection virtual osg::Vec3f getStormDirection() const = 0; /// Resets all actors in the current active cells to their original location within that cell. virtual void resetActors() = 0; virtual bool isWalkingOnWater (const MWWorld::ConstPtr& actor) = 0; /// Return a vector aiming the actor's weapon towards a target. /// @note The length of the vector is the distance between actor and target. virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0; /// Return the distance between actor's weapon and target's collision box. virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0; virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/000077500000000000000000000000001264522266000203405ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwclass/activator.cpp000066400000000000000000000075221264522266000230460ustar00rootroot00000000000000#include "activator.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/action.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/nullaction.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" #include "../mwmechanics/npcstats.hpp" namespace MWClass { void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model, true); } } void Activator::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { if(!model.empty()) physics.addObject(ptr, model); MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Activator::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Activator::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } std::string Activator::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } void Activator::registerSelf() { boost::shared_ptr instance (new Activator); registerClass (typeid (ESM::Activator).name(), instance); } bool Activator::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Activator::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); std::string text; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } boost::shared_ptr Activator::activate(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor) const { if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Sound *sound = store.get().searchRandom("WolfActivator"); boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); if(sound) action->setSound(sound->mId); return action; } return boost::shared_ptr(new MWWorld::NullAction); } MWWorld::Ptr Activator::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } } openmw-openmw-0.38.0/apps/openmw/mwclass/activator.hpp000066400000000000000000000032651264522266000230530ustar00rootroot00000000000000#ifndef GAME_MWCLASS_ACTIVATOR_H #define GAME_MWCLASS_ACTIVATOR_H #include "../mwworld/class.hpp" namespace MWClass { class Activator : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation static void registerSelf(); virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/actor.cpp000066400000000000000000000054301264522266000221560ustar00rootroot00000000000000#include "actor.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/inventorystore.hpp" namespace MWClass { Actor::Actor() {} Actor::~Actor() {} void Actor::adjustPosition(const MWWorld::Ptr& ptr, bool force) const { MWBase::Environment::get().getWorld()->adjustPosition(ptr, force); } void Actor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { if (!model.empty()) { physics.addActor(ptr, model); if (getCreatureStats(ptr).isDead()) MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false); } MWBase::Environment::get().getMechanicsManager()->add(ptr); } void Actor::block(const MWWorld::Ptr &ptr) const { MWWorld::InventoryStore& inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if (shield == inv.end()) return; MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); switch (shield->getClass().getEquipmentSkill(*shield)) { case ESM::Skill::LightArmor: sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); break; case ESM::Skill::MediumArmor: sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); break; case ESM::Skill::HeavyArmor: sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); break; default: return; } } osg::Vec3f Actor::getRotationVector(const MWWorld::Ptr& ptr) const { MWMechanics::Movement &movement = getMovementSettings(ptr); osg::Vec3f vec(movement.mRotation[0], movement.mRotation[1], movement.mRotation[2]); movement.mRotation[0] = 0.0f; movement.mRotation[1] = 0.0f; movement.mRotation[2] = 0.0f; return vec; } float Actor::getEncumbrance(const MWWorld::Ptr& ptr) const { float weight = getContainerStore(ptr).getWeight(); const MWMechanics::MagicEffects& effects = getCreatureStats(ptr).getMagicEffects(); weight -= effects.get(MWMechanics::EffectKey(ESM::MagicEffect::Feather)).getMagnitude(); weight += effects.get(MWMechanics::EffectKey(ESM::MagicEffect::Burden)).getMagnitude(); return (weight < 0) ? 0.0f : weight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/actor.hpp000066400000000000000000000023471264522266000221670ustar00rootroot00000000000000#ifndef GAME_MWCLASS_MOBILE_H #define GAME_MWCLASS_MOBILE_H #include "../mwworld/class.hpp" namespace ESM { struct GameSetting; } namespace MWClass { /// \brief Class holding functionality common to Creature and NPC class Actor : public MWWorld::Class { protected: Actor(); public: virtual ~Actor(); virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const; ///< Adjust position to stand on ground. Must be called post model load /// @param force do this even if the ptr is flying virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void block(const MWWorld::Ptr &ptr) const; virtual osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const; ///< Return desired rotations, as euler angles. virtual float getEncumbrance(const MWWorld::Ptr& ptr) const; ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. // not implemented Actor(const Actor&); Actor& operator= (const Actor&); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/apparatus.cpp000066400000000000000000000112421264522266000230440ustar00rootroot00000000000000#include "apparatus.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionalchemy.hpp" #include "../mwworld/cellstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass { void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Apparatus::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Apparatus::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Apparatus::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Apparatus::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } std::string Apparatus::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } int Apparatus::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Apparatus::registerSelf() { boost::shared_ptr instance (new Apparatus); registerClass (typeid (ESM::Apparatus).name(), instance); } std::string Apparatus::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Apparatus Up"); } std::string Apparatus::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Apparatus Down"); } std::string Apparatus::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Apparatus::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Apparatus::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } boost::shared_ptr Apparatus::use (const MWWorld::Ptr& ptr) const { return boost::shared_ptr(new MWWorld::ActionAlchemy()); } MWWorld::Ptr Apparatus::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } bool Apparatus::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Apparatus) != 0; } float Apparatus::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/apparatus.hpp000066400000000000000000000051211264522266000230500ustar00rootroot00000000000000#ifndef GAME_MWCLASS_APPARATUS_H #define GAME_MWCLASS_APPARATUS_H #include "../mwworld/class.hpp" namespace MWClass { class Apparatus : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/armor.cpp000066400000000000000000000340521264522266000221700ustar00rootroot00000000000000#include "armor.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/containerstore.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass { void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Armor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Armor::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Armor::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Armor::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } bool Armor::hasItemHealth (const MWWorld::ConstPtr& ptr) const { return true; } int Armor::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mHealth; } std::string Armor::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } std::pair, bool> Armor::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots_; const int size = 11; static const int sMapping[size][2] = { { ESM::Armor::Helmet, MWWorld::InventoryStore::Slot_Helmet }, { ESM::Armor::Cuirass, MWWorld::InventoryStore::Slot_Cuirass }, { ESM::Armor::LPauldron, MWWorld::InventoryStore::Slot_LeftPauldron }, { ESM::Armor::RPauldron, MWWorld::InventoryStore::Slot_RightPauldron }, { ESM::Armor::Greaves, MWWorld::InventoryStore::Slot_Greaves }, { ESM::Armor::Boots, MWWorld::InventoryStore::Slot_Boots }, { ESM::Armor::LGauntlet, MWWorld::InventoryStore::Slot_LeftGauntlet }, { ESM::Armor::RGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet }, { ESM::Armor::Shield, MWWorld::InventoryStore::Slot_CarriedLeft }, { ESM::Armor::LBracer, MWWorld::InventoryStore::Slot_LeftGauntlet }, { ESM::Armor::RBracer, MWWorld::InventoryStore::Slot_RightGauntlet } }; for (int i=0; imBase->mData.mType) { slots_.push_back (int (sMapping[i][1])); break; } return std::make_pair (slots_, false); } int Armor::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); std::string typeGmst; switch (ref->mBase->mData.mType) { case ESM::Armor::Helmet: typeGmst = "iHelmWeight"; break; case ESM::Armor::Cuirass: typeGmst = "iCuirassWeight"; break; case ESM::Armor::LPauldron: case ESM::Armor::RPauldron: typeGmst = "iPauldronWeight"; break; case ESM::Armor::Greaves: typeGmst = "iGreavesWeight"; break; case ESM::Armor::Boots: typeGmst = "iBootsWeight"; break; case ESM::Armor::LGauntlet: case ESM::Armor::RGauntlet: typeGmst = "iGauntletWeight"; break; case ESM::Armor::Shield: typeGmst = "iShieldWeight"; break; case ESM::Armor::LBracer: case ESM::Armor::RBracer: typeGmst = "iGauntletWeight"; break; } if (typeGmst.empty()) return -1; const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); float iWeight = floor(gmst.find(typeGmst)->getFloat()); float epsilon = 0.0005f; if (ref->mBase->mData.mWeight == 0) return ESM::Skill::Unarmored; if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fLightMaxMod")->getFloat() + epsilon) return ESM::Skill::LightArmor; if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->getFloat() + epsilon) return ESM::Skill::MediumArmor; else return ESM::Skill::HeavyArmor; } int Armor::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Armor::registerSelf() { boost::shared_ptr instance (new Armor); registerClass (typeid (ESM::Armor).name(), instance); } std::string Armor::getUpSoundId (const MWWorld::ConstPtr& ptr) const { int es = getEquipmentSkill(ptr); if (es == ESM::Skill::LightArmor) return std::string("Item Armor Light Up"); else if (es == ESM::Skill::MediumArmor) return std::string("Item Armor Medium Up"); else return std::string("Item Armor Heavy Up"); } std::string Armor::getDownSoundId (const MWWorld::ConstPtr& ptr) const { int es = getEquipmentSkill(ptr); if (es == ESM::Skill::LightArmor) return std::string("Item Armor Light Down"); else if (es == ESM::Skill::MediumArmor) return std::string("Item Armor Medium Down"); else return std::string("Item Armor Heavy Down"); } std::string Armor::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Armor::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Armor::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; // get armor type string (light/medium/heavy) int armorType = getEquipmentSkill(ptr); std::string typeText; if (armorType == ESM::Skill::LightArmor) typeText = "#{sLight}"; else if (armorType == ESM::Skill::MediumArmor) typeText = "#{sMedium}"; else typeText = "#{sHeavy}"; text += "\n#{sArmorRating}: " + MWGui::ToolTips::toString(getEffectiveArmorRating(ptr, MWMechanics::getPlayer())); int remainingHealth = getItemHealth(ptr); text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/" + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight) + " (" + typeText + ")"; text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.enchant = ref->mBase->mEnchant; if (!info.enchant.empty()) info.remainingEnchantCharge = static_cast(ptr.getCellRef().getEnchantmentCharge()); info.text = text; return info; } std::string Armor::getEnchantment (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mEnchant; } std::string Armor::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { const MWWorld::LiveCellRef *ref = ptr.get(); ESM::Armor newItem = *ref->mBase; newItem.mId=""; newItem.mName=newName; newItem.mData.mEnchant=enchCharge; newItem.mEnchant=enchId; const ESM::Armor *record = MWBase::Environment::get().getWorld()->createRecord (newItem); return record->mId; } int Armor::getEffectiveArmorRating(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &actor) const { const MWWorld::LiveCellRef *ref = ptr.get(); int armorSkillType = getEquipmentSkill(ptr); int armorSkill = actor.getClass().getSkill(actor, armorSkillType); const MWBase::World *world = MWBase::Environment::get().getWorld(); int iBaseArmorSkill = world->getStore().get().find("iBaseArmorSkill")->getInt(); if(ref->mBase->mData.mWeight == 0) return ref->mBase->mData.mArmor; else return ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill; } std::pair Armor::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc); if (ptr.getCellRef().getCharge() == 0) return std::make_pair(0, "#{sInventoryMessage1}"); // slots that this item can be equipped in std::pair, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr); if (slots_.first.empty()) return std::make_pair(0, ""); if (npc.getClass().isNpc()) { std::string npcRace = npc.get()->mBase->mRace; // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); if(race->mData.mFlags & ESM::Race::Beast) { std::vector parts = ptr.get()->mBase->mParts.mParts; for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) { if((*itr).mPart == ESM::PRT_Head) return std::make_pair(0, "#{sNotifyMessage13}"); if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) return std::make_pair(0, "#{sNotifyMessage14}"); } } } for (std::vector::const_iterator slot=slots_.first.begin(); slot!=slots_.first.end(); ++slot) { // If equipping a shield, check if there's a twohanded weapon conflicting with it if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft) { MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if(weapon == invStore.end()) return std::make_pair(1,""); if(weapon->getTypeName() == typeid(ESM::Weapon).name() && (weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || weapon->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)) { return std::make_pair(3,""); } return std::make_pair(1,""); } } return std::make_pair(1,""); } boost::shared_ptr Armor::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); action->setSound(getUpSoundId(ptr)); return action; } MWWorld::Ptr Armor::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } int Armor::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mEnchant; } bool Armor::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Armor) || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } float Armor::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/armor.hpp000066400000000000000000000106511264522266000221740ustar00rootroot00000000000000#ifndef GAME_MWCLASS_ARMOR_H #define GAME_MWCLASS_ARMOR_H #include "../mwworld/class.hpp" namespace MWClass { class Armor : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const; ///< \return Item health data available? virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? virtual int getEquipmentSkill (const MWWorld::ConstPtr& ptr) const; /// Return the index of the skill this item corresponds to when equipped or -1, if there is /// no such skill. virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. virtual std::pair canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const; ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. \n /// Second item in the pair specifies the error message virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; /// Get the effective armor rating, factoring in the actor's skills, for the given armor. virtual int getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/bodypart.cpp000066400000000000000000000026431264522266000226750ustar00rootroot00000000000000#include "bodypart.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwrender/objects.hpp" #include "../mwworld/cellstore.hpp" namespace MWClass { MWWorld::Ptr BodyPart::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } void BodyPart::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string &model, MWRender::RenderingInterface &renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void BodyPart::insertObject(const MWWorld::Ptr &ptr, const std::string &model, MWPhysics::PhysicsSystem &physics) const { } std::string BodyPart::getName(const MWWorld::ConstPtr &ptr) const { return std::string(); } void BodyPart::registerSelf() { boost::shared_ptr instance (new BodyPart); registerClass (typeid (ESM::BodyPart).name(), instance); } std::string BodyPart::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } } openmw-openmw-0.38.0/apps/openmw/mwclass/bodypart.hpp000066400000000000000000000017021264522266000226750ustar00rootroot00000000000000#ifndef GAME_MWCLASS_BODYPART_H #define GAME_MWCLASS_BODYPART_H #include "../mwworld/class.hpp" namespace MWClass { class BodyPart : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. static void registerSelf(); virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/book.cpp000066400000000000000000000140311264522266000217750ustar00rootroot00000000000000#include "book.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" #include "../mwmechanics/npcstats.hpp" namespace MWClass { void Book::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Book::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Book::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Book::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Book::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Sound *sound = store.get().searchRandom("WolfItem"); boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); if(sound) action->setSound(sound->mId); return action; } return boost::shared_ptr(new MWWorld::ActionRead(ptr)); } std::string Book::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } int Book::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Book::registerSelf() { boost::shared_ptr instance (new Book); registerClass (typeid (ESM::Book).name(), instance); } std::string Book::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Book Up"); } std::string Book::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Book Down"); } std::string Book::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Book::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Book::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.enchant = ref->mBase->mEnchant; info.text = text; return info; } std::string Book::getEnchantment (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mEnchant; } std::string Book::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { const MWWorld::LiveCellRef *ref = ptr.get(); ESM::Book newItem = *ref->mBase; newItem.mId=""; newItem.mName=newName; newItem.mData.mIsScroll = 1; newItem.mData.mEnchant=enchCharge; newItem.mEnchant=enchId; const ESM::Book *record = MWBase::Environment::get().getWorld()->createRecord (newItem); return record->mId; } boost::shared_ptr Book::use (const MWWorld::Ptr& ptr) const { return boost::shared_ptr(new MWWorld::ActionRead(ptr)); } MWWorld::Ptr Book::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } int Book::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mEnchant; } bool Book::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Books) || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } float Book::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/book.hpp000066400000000000000000000061301264522266000220030ustar00rootroot00000000000000#ifndef GAME_MWCLASS_BOOK_H #define GAME_MWCLASS_BOOK_H #include "../mwworld/class.hpp" namespace MWClass { class Book : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/classes.cpp000066400000000000000000000023731264522266000225060ustar00rootroot00000000000000#include "classes.hpp" #include "activator.hpp" #include "creature.hpp" #include "npc.hpp" #include "weapon.hpp" #include "armor.hpp" #include "potion.hpp" #include "apparatus.hpp" #include "book.hpp" #include "clothing.hpp" #include "container.hpp" #include "door.hpp" #include "ingredient.hpp" #include "creaturelevlist.hpp" #include "itemlevlist.hpp" #include "light.hpp" #include "lockpick.hpp" #include "misc.hpp" #include "probe.hpp" #include "repair.hpp" #include "static.hpp" #include "bodypart.hpp" namespace MWClass { void registerClasses() { Activator::registerSelf(); Creature::registerSelf(); Npc::registerSelf(); Weapon::registerSelf(); Armor::registerSelf(); Potion::registerSelf(); Apparatus::registerSelf(); Book::registerSelf(); Clothing::registerSelf(); Container::registerSelf(); Door::registerSelf(); Ingredient::registerSelf(); CreatureLevList::registerSelf(); ItemLevList::registerSelf(); Light::registerSelf(); Lockpick::registerSelf(); Miscellaneous::registerSelf(); Probe::registerSelf(); Repair::registerSelf(); Static::registerSelf(); BodyPart::registerSelf(); } } openmw-openmw-0.38.0/apps/openmw/mwclass/classes.hpp000066400000000000000000000002351264522266000225060ustar00rootroot00000000000000#ifndef GAME_MWCLASS_CLASSES_H #define GAME_MWCLASS_CLASSES_H namespace MWClass { void registerClasses(); ///< register all known classes } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/clothing.cpp000066400000000000000000000227111264522266000226560ustar00rootroot00000000000000#include "clothing.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" namespace MWClass { void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Clothing::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Clothing::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Clothing::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } std::string Clothing::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } std::pair, bool> Clothing::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots_; if (ref->mBase->mData.mType==ESM::Clothing::Ring) { slots_.push_back (int (MWWorld::InventoryStore::Slot_LeftRing)); slots_.push_back (int (MWWorld::InventoryStore::Slot_RightRing)); } else { const int size = 9; static const int sMapping[size][2] = { { ESM::Clothing::Shirt, MWWorld::InventoryStore::Slot_Shirt }, { ESM::Clothing::Belt, MWWorld::InventoryStore::Slot_Belt }, { ESM::Clothing::Robe, MWWorld::InventoryStore::Slot_Robe }, { ESM::Clothing::Pants, MWWorld::InventoryStore::Slot_Pants }, { ESM::Clothing::Shoes, MWWorld::InventoryStore::Slot_Boots }, { ESM::Clothing::LGlove, MWWorld::InventoryStore::Slot_LeftGauntlet }, { ESM::Clothing::RGlove, MWWorld::InventoryStore::Slot_RightGauntlet }, { ESM::Clothing::Skirt, MWWorld::InventoryStore::Slot_Skirt }, { ESM::Clothing::Amulet, MWWorld::InventoryStore::Slot_Amulet } }; for (int i=0; imBase->mData.mType) { slots_.push_back (int (sMapping[i][1])); break; } } return std::make_pair (slots_, false); } int Clothing::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mData.mType==ESM::Clothing::Shoes) return ESM::Skill::Unarmored; return -1; } int Clothing::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Clothing::registerSelf() { boost::shared_ptr instance (new Clothing); registerClass (typeid (ESM::Clothing).name(), instance); } std::string Clothing::getUpSoundId (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mData.mType == 8) { return std::string("Item Ring Up"); } return std::string("Item Clothes Up"); } std::string Clothing::getDownSoundId (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mData.mType == 8) { return std::string("Item Ring Down"); } return std::string("Item Clothes Down"); } std::string Clothing::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Clothing::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Clothing::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.enchant = ref->mBase->mEnchant; if (!info.enchant.empty()) info.remainingEnchantCharge = static_cast(ptr.getCellRef().getEnchantmentCharge()); info.text = text; return info; } std::string Clothing::getEnchantment (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mEnchant; } std::string Clothing::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { const MWWorld::LiveCellRef *ref = ptr.get(); ESM::Clothing newItem = *ref->mBase; newItem.mId=""; newItem.mName=newName; newItem.mData.mEnchant=enchCharge; newItem.mEnchant=enchId; const ESM::Clothing *record = MWBase::Environment::get().getWorld()->createRecord (newItem); return record->mId; } std::pair Clothing::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { // slots that this item can be equipped in std::pair, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr); if (slots_.first.empty()) return std::make_pair(0, ""); if (npc.getClass().isNpc()) { std::string npcRace = npc.get()->mBase->mRace; // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); if(race->mData.mFlags & ESM::Race::Beast) { std::vector parts = ptr.get()->mBase->mParts.mParts; for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) { if((*itr).mPart == ESM::PRT_Head) return std::make_pair(0, "#{sNotifyMessage13}"); if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) return std::make_pair(0, "#{sNotifyMessage15}"); } } } return std::make_pair (1, ""); } boost::shared_ptr Clothing::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); action->setSound(getUpSoundId(ptr)); return action; } MWWorld::Ptr Clothing::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } int Clothing::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mEnchant; } bool Clothing::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Clothing) || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } float Clothing::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/clothing.hpp000066400000000000000000000076411264522266000226700ustar00rootroot00000000000000#ifndef GAME_MWCLASS_CLOTHING_H #define GAME_MWCLASS_CLOTHING_H #include "../mwworld/class.hpp" namespace MWClass { class Clothing : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? virtual int getEquipmentSkill (const MWWorld::ConstPtr& ptr) const; /// Return the index of the skill this item corresponds to when equipped or -1, if there is /// no such skill. virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. virtual std::pair canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const; ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. /// Second item in the pair specifies the error message virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/container.cpp000066400000000000000000000246711264522266000230400ustar00rootroot00000000000000#include "container.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/actionopen.hpp" #include "../mwworld/actiontrap.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwmechanics/npcstats.hpp" namespace MWClass { class ContainerCustomData : public MWWorld::CustomData { public: MWWorld::ContainerStore mContainerStore; virtual MWWorld::CustomData *clone() const; virtual ContainerCustomData& asContainerCustomData() { return *this; } }; MWWorld::CustomData *ContainerCustomData::clone() const { return new ContainerCustomData (*this); } void Container::ensureCustomData (const MWWorld::Ptr& ptr) const { if (!ptr.getRefData().getCustomData()) { std::auto_ptr data (new ContainerCustomData); MWWorld::LiveCellRef *ref = ptr.get(); // setting ownership not needed, since taking items from a container inherits the // container's owner automatically data->mContainerStore.fill( ref->mBase->mInventory, ""); // store ptr.getRefData().setCustomData (data.release()); } } void Container::respawn(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mFlags & ESM::Container::Respawn) { MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); ptr.getRefData().setCustomData(NULL); } } void Container::restock(const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; MWWorld::ContainerStore& store = getContainerStore(ptr); // setting ownership not needed, since taking items from a container inherits the // container's owner automatically store.restock(list, ptr, ""); } void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model, true); } } void Container::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { if(!model.empty()) physics.addObject(ptr, model); MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Container::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } boost::shared_ptr Container::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return boost::shared_ptr (new MWWorld::NullAction ()); if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Sound *sound = store.get().searchRandom("WolfContainer"); boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); if(sound) action->setSound(sound->mId); return action; } const std::string lockedSound = "LockedChest"; const std::string trapActivationSound = "Disarm Trap Fail"; MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::InventoryStore& invStore = player.getClass().getInventoryStore(player); bool needKey = ptr.getCellRef().getLockLevel() > 0; bool hasKey = false; std::string keyName; // make key id lowercase std::string keyId = ptr.getCellRef().getKey(); Misc::StringUtils::lowerCaseInPlace(keyId); for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { std::string refId = it->getCellRef().getRefId(); Misc::StringUtils::lowerCaseInPlace(refId); if (refId == keyId) { hasKey = true; keyName = it->getClass().getName(*it); } } if (needKey && hasKey) { MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}"); unlock(ptr); // using a key disarms the trap ptr.getCellRef().setTrap(""); } if (!needKey || hasKey) { if(ptr.getCellRef().getTrap().empty()) { boost::shared_ptr action (new MWWorld::ActionOpen(ptr)); return action; } else { // Activate trap boost::shared_ptr action(new MWWorld::ActionTrap(actor, ptr.getCellRef().getTrap(), ptr)); action->setSound(trapActivationSound); return action; } } else { boost::shared_ptr action(new MWWorld::FailedAction); action->setSound(lockedSound); return action; } } std::string Container::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); return ptr.getRefData().getCustomData()->asContainerCustomData().mContainerStore; } std::string Container::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } void Container::registerSelf() { boost::shared_ptr instance (new Container); registerClass (typeid (ESM::Container).name(), instance); } bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName; std::string text; if (ptr.getCellRef().getLockLevel() > 0) text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ptr.getCellRef().getLockLevel()); else if (ptr.getCellRef().getLockLevel() < 0) text += "\n#{sUnlocked}"; if (ptr.getCellRef().getTrap() != "") text += "\n#{sTrapped}"; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } float Container::getCapacity (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mWeight; } float Container::getEncumbrance (const MWWorld::Ptr& ptr) const { return getContainerStore (ptr).getWeight(); } void Container::lock (const MWWorld::Ptr& ptr, int lockLevel) const { if(lockLevel!=0) ptr.getCellRef().setLockLevel(abs(lockLevel)); //Changes lock to locklevel, in positive else ptr.getCellRef().setLockLevel(abs(ptr.getCellRef().getLockLevel())); //No locklevel given, just flip the original one } void Container::unlock (const MWWorld::Ptr& ptr) const { ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative } bool Container::canLock(const MWWorld::ConstPtr &ptr) const { return true; } MWWorld::Ptr Container::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { if (!state.mHasCustomState) return; const ESM::ContainerState& state2 = dynamic_cast (state); if (!ptr.getRefData().getCustomData()) { // Create a CustomData, but don't fill it from ESM records (not needed) std::auto_ptr data (new ContainerCustomData); ptr.getRefData().setCustomData (data.release()); } dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. readState (state2.mInventory); } void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { ESM::ContainerState& state2 = dynamic_cast (state); if (!ptr.getRefData().getCustomData()) { state.mHasCustomState = false; return; } dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. writeState (state2.mInventory); } } openmw-openmw-0.38.0/apps/openmw/mwclass/container.hpp000066400000000000000000000062201264522266000230330ustar00rootroot00000000000000#ifndef GAME_MWCLASS_CONTAINER_H #define GAME_MWCLASS_CONTAINER_H #include "../mwworld/class.hpp" namespace MWClass { class Container : public MWWorld::Class { void ensureCustomData (const MWWorld::Ptr& ptr) const; virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const; ///< Return container store virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual float getCapacity (const MWWorld::Ptr& ptr) const; ///< Return total weight that fits into the object. Throws an exception, if the object can't /// hold other objects. virtual float getEncumbrance (const MWWorld::Ptr& ptr) const; ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = 0) const; ///< Lock object virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object virtual bool canLock(const MWWorld::ConstPtr &ptr) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. static void registerSelf(); virtual void respawn (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/creature.cpp000066400000000000000000000737421264522266000226730ustar00rootroot00000000000000#include "creature.hpp" #include #include #include #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/disease.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/difficultyscaling.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" #include "../mwworld/actionopen.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/containerstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/cellstore.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwrender/objects.hpp" #include "../mwgui/tooltips.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/combat.hpp" #include "../mwmechanics/actorutil.hpp" namespace { bool isFlagBitSet(const MWWorld::ConstPtr &ptr, ESM::Creature::Flags bitMask) { return (ptr.get()->mBase->mFlags & bitMask) != 0; } } namespace MWClass { class CreatureCustomData : public MWWorld::CustomData { public: MWMechanics::CreatureStats mCreatureStats; MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures MWMechanics::Movement mMovement; virtual MWWorld::CustomData *clone() const; virtual CreatureCustomData& asCreatureCustomData() { return *this; } virtual const CreatureCustomData& asCreatureCustomData() const { return *this; } CreatureCustomData() : mContainerStore(0) {} virtual ~CreatureCustomData() { delete mContainerStore; } }; MWWorld::CustomData *CreatureCustomData::clone() const { CreatureCustomData* cloned = new CreatureCustomData (*this); cloned->mContainerStore = mContainerStore->clone(); return cloned; } const Creature::GMST& Creature::getGmst() { static GMST gmst; static bool inited = false; if (!inited) { const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature"); gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature"); gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect"); gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier"); gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus"); gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier"); gmst.fMinFlySpeed = store.find("fMinFlySpeed"); gmst.fMaxFlySpeed = store.find("fMaxFlySpeed"); gmst.fSwimRunBase = store.find("fSwimRunBase"); gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult"); gmst.fKnockDownMult = store.find("fKnockDownMult"); gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult"); gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); inited = true; } return gmst; } void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const { if (!ptr.getRefData().getCustomData()) { std::auto_ptr data (new CreatureCustomData); MWWorld::LiveCellRef *ref = ptr.get(); // creature stats data->mCreatureStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mData.mStrength); data->mCreatureStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mData.mIntelligence); data->mCreatureStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mData.mWillpower); data->mCreatureStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mData.mAgility); data->mCreatureStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mData.mSpeed); data->mCreatureStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mData.mEndurance); data->mCreatureStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mData.mPersonality); data->mCreatureStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mData.mLuck); data->mCreatureStats.setHealth(static_cast(ref->mBase->mData.mHealth)); data->mCreatureStats.setMagicka(static_cast(ref->mBase->mData.mMana)); data->mCreatureStats.setFatigue(static_cast(ref->mBase->mData.mFatigue)); data->mCreatureStats.setLevel(ref->mBase->mData.mLevel); data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage); data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello); data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, ref->mBase->mAiData.mFight); data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee); data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm); // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); iter!=ref->mBase->mSpells.mList.end(); ++iter) { if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mCreatureStats.getSpells().add (spell); else /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on creature '" << ref->mBase->mId << "'" << std::endl; } // inventory bool hasInventory = hasInventoryStore(ptr); if (hasInventory) data->mContainerStore = new MWWorld::InventoryStore(); else data->mContainerStore = new MWWorld::ContainerStore(); data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold); data->mCreatureStats.setNeedRecalcDynamicStats(false); // store ptr.getRefData().setCustomData(data.release()); getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId()); if (hasInventory) getInventoryStore(ptr).autoEquip(ptr); } } void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertCreature(ptr, model, hasInventoryStore(ptr)); } std::string Creature::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Creature::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } MWMechanics::CreatureStats& Creature::getCreatureStats (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); return ptr.getRefData().getCustomData()->asCreatureCustomData().mCreatureStats; } void Creature::hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const { MWWorld::LiveCellRef *ref = ptr.get(); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); MWMechanics::CreatureStats &stats = getCreatureStats(ptr); if (stats.getDrawState() != MWMechanics::DrawState_Weapon) return; // Get the weapon used (if hand-to-hand, weapon = inv.end()) MWWorld::Ptr weapon; if (ptr.getClass().hasInventoryStore(ptr)) { MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weaponslot != inv.end() && weaponslot->getTypeName() == typeid(ESM::Weapon).name()) weapon = *weaponslot; } MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength); // TODO: where is the distance defined? float dist = 200.f; if (!weapon.isEmpty()) { const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); dist = fCombatDistance * weapon.get()->mBase->mData.mReach; } std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist); if (result.first.isEmpty()) return; // Didn't hit anything MWWorld::Ptr victim = result.first; if (!victim.getClass().isActor()) return; // Can't hit non-actors osg::Vec3f hitPosition (result.second); float hitchance = MWMechanics::getHitChance(ptr, victim, ref->mBase->mData.mCombat); if(Misc::Rng::roll0to99() >= hitchance) { victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false); MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr); return; } int min,max; switch (type) { case 0: min = ref->mBase->mData.mAttack[0]; max = ref->mBase->mData.mAttack[1]; break; case 1: min = ref->mBase->mData.mAttack[2]; max = ref->mBase->mData.mAttack[3]; break; case 2: default: min = ref->mBase->mData.mAttack[4]; max = ref->mBase->mData.mAttack[5]; break; } float damage = min + (max - min) * attackStrength; bool healthdmg = true; if (!weapon.isEmpty()) { const unsigned char *attack = NULL; if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; else if(type == ESM::Weapon::AT_Slash) attack = weapon.get()->mBase->mData.mSlash; else if(type == ESM::Weapon::AT_Thrust) attack = weapon.get()->mBase->mData.mThrust; if(attack) { damage = attack[0] + ((attack[1]-attack[0])*attackStrength); MWMechanics::adjustWeaponDamage(damage, weapon, ptr); MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr); } // Apply "On hit" enchanted weapons std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : ""; if (!enchantmentName.empty()) { const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( enchantmentName); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { MWMechanics::CastSpell cast(ptr, victim); cast.mHitPosition = hitPosition; cast.cast(weapon); } } } else if (isBipedal(ptr)) { MWMechanics::getHandToHandDamage(ptr, victim, damage, healthdmg, attackStrength); } MWMechanics::applyElementalShields(ptr, victim); if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength)) damage = 0; if (damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); MWMechanics::diseaseContact(victim, ptr); victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, true); } void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const { // NOTE: 'object' and/or 'attacker' may be empty. if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)) getCreatureStats(ptr).setAttacked(true); // Self defense bool setOnPcHitMe = true; // Note OnPcHitMe is not set for friendly hits. // No retaliation for totally static creatures (they have no movement or attacks anyway) if (isMobile(ptr) && !attacker.isEmpty()) { setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); } if(!object.isEmpty()) getCreatureStats(ptr).setLastHitAttemptObject(object.getCellRef().getRefId()); if(setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) { const std::string &script = ptr.get()->mBase->mScript; /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ if(!script.empty()) ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } if(!successful) { // Missed MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f); return; } if(!object.isEmpty()) getCreatureStats(ptr).setLastHitObject(object.getCellRef().getRefId()); if (damage > 0.0f && !object.isEmpty()) MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); if (damage < 0.001f) damage = 0; if (damage > 0.f) { if (!attacker.isEmpty()) { // Check for knockdown float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat(); float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().iKnockDownOddsMult->getInt() * 0.01f + getGmst().iKnockDownOddsBase->getInt(); if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) { getCreatureStats(ptr).setKnockedDown(true); } else getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? } damage = std::max(1.f, damage); if(ishealth) { if (!attacker.isEmpty()) damage = scaleDamage(damage, attacker, ptr); MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); MWMechanics::DynamicStat health(getCreatureStats(ptr).getHealth()); health.setCurrent(health.getCurrent() - damage); getCreatureStats(ptr).setHealth(health); } else { MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } } } boost::shared_ptr Creature::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Sound *sound = store.get().searchRandom("WolfCreature"); boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); if(sound) action->setSound(sound->mId); return action; } if(getCreatureStats(ptr).isDead()) return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat()) return boost::shared_ptr(new MWWorld::FailedAction("")); return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); } MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); return *ptr.getRefData().getCustomData()->asCreatureCustomData().mContainerStore; } MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const { if (hasInventoryStore(ptr)) return dynamic_cast(getContainerStore(ptr)); else throw std::runtime_error("this creature has no inventory store"); } bool Creature::hasInventoryStore(const MWWorld::Ptr &ptr) const { return isFlagBitSet(ptr, ESM::Creature::Weapon); } std::string Creature::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } bool Creature::isEssential (const MWWorld::ConstPtr& ptr) const { return isFlagBitSet(ptr, ESM::Creature::Essential); } void Creature::registerSelf() { boost::shared_ptr instance (new Creature); registerClass (typeid (ESM::Creature).name(), instance); } float Creature::getSpeed(const MWWorld::Ptr &ptr) const { MWMechanics::CreatureStats& stats = getCreatureStats(ptr); const GMST& gmst = getGmst(); float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() * (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat()); const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); // The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp) float runSpeed = walkSpeed; float moveSpeed; if(getEncumbrance(ptr) > getCapacity(ptr)) moveSpeed = 0.0f; else if(canFly(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).getMagnitude() > 0 && world->isLevitationEnabled())) { float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).getMagnitude()); flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } else if(world->isSwimming(ptr)) { float swimSpeed = walkSpeed; if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->getFloat(); moveSpeed = swimSpeed; } else if(running) moveSpeed = runSpeed; else moveSpeed = walkSpeed; if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) moveSpeed *= 0.75f; return moveSpeed; } MWMechanics::Movement& Creature::getMovementSettings (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); return ptr.getRefData().getCustomData()->asCreatureCustomData().mMovement; } bool Creature::hasToolTip(const MWWorld::ConstPtr& ptr) const { if (!ptr.getRefData().getCustomData()) return true; const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); return !customData.mCreatureStats.getAiSequence().isInCombat() || customData.mCreatureStats.isDead(); } MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName; std::string text; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); info.text = text; return info; } float Creature::getArmorRating (const MWWorld::Ptr& ptr) const { // Note this is currently unused. Creatures do not use armor mitigation. return getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).getMagnitude(); } float Creature::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); return static_cast(stats.getAttribute(0).getModified() * 5); } int Creature::getServices(const MWWorld::ConstPtr &actor) const { const MWWorld::LiveCellRef* ref = actor.get(); if (ref->mBase->mHasAI) return ref->mBase->mAiData.mServices; else return 0; } bool Creature::isPersistent(const MWWorld::ConstPtr &actor) const { const MWWorld::LiveCellRef* ref = actor.get(); return ref->mBase->mPersistent; } std::string Creature::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const { const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); int type = getSndGenTypeFromName(ptr, name); if(type >= 0) { std::vector sounds; MWWorld::LiveCellRef* ref = ptr.get(); const std::string& ourId = (ref->mBase->mOriginal.empty()) ? ptr.getCellRef().getRefId() : ref->mBase->mOriginal; MWWorld::Store::iterator sound = store.begin(); while(sound != store.end()) { if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(ourId, sound->mCreature))) sounds.push_back(&*sound); ++sound; } if(!sounds.empty()) return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; } if (type == ESM::SoundGenerator::Land) return "Body Fall Large"; return ""; } MWWorld::Ptr Creature::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } bool Creature::isBipedal(const MWWorld::ConstPtr &ptr) const { return isFlagBitSet(ptr, ESM::Creature::Bipedal); } bool Creature::canFly(const MWWorld::ConstPtr &ptr) const { return isFlagBitSet(ptr, ESM::Creature::Flies); } bool Creature::canSwim(const MWWorld::ConstPtr &ptr) const { return isFlagBitSet(ptr, static_cast(ESM::Creature::Swims | ESM::Creature::Bipedal)); } bool Creature::canWalk(const MWWorld::ConstPtr &ptr) const { return isFlagBitSet(ptr, static_cast(ESM::Creature::Walks | ESM::Creature::Bipedal)); } int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) { if(name == "left") { MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) return 2; if(world->isOnGround(ptr)) return 0; return -1; } if(name == "right") { MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) return 3; if(world->isOnGround(ptr)) return 1; return -1; } if(name == "swimleft") return 2; if(name == "swimright") return 3; if(name == "moan") return 4; if(name == "roar") return 5; if(name == "scream") return 6; if(name == "land") return 7; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const { MWWorld::LiveCellRef *ref = ptr.get(); const ESM::Skill* skillRecord = MWBase::Environment::get().getWorld()->getStore().get().find(skill); switch (skillRecord->mData.mSpecialization) { case ESM::Class::Combat: return ref->mBase->mData.mCombat; case ESM::Class::Magic: return ref->mBase->mData.mMagic; case ESM::Class::Stealth: return ref->mBase->mData.mStealth; default: throw std::runtime_error("invalid specialisation"); } } int Creature::getBloodTexture(const MWWorld::ConstPtr &ptr) const { int flags = ptr.get()->mBase->mFlags; if (flags & ESM::Creature::Skeleton) return 1; if (flags & ESM::Creature::Metal) return 2; return 0; } void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { if (!state.mHasCustomState) return; const ESM::CreatureState& state2 = dynamic_cast (state); if (state.mVersion > 0) { if (!ptr.getRefData().getCustomData()) { // Create a CustomData, but don't fill it from ESM records (not needed) std::auto_ptr data (new CreatureCustomData); if (hasInventoryStore(ptr)) data->mContainerStore = new MWWorld::InventoryStore(); else data->mContainerStore = new MWWorld::ContainerStore(); ptr.getRefData().setCustomData (data.release()); } } else ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless. CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); customData.mContainerStore->readState (state2.mInventory); customData.mCreatureStats.readState (state2.mCreatureStats); } void Creature::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { ESM::CreatureState& state2 = dynamic_cast (state); if (!ptr.getRefData().getCustomData()) { state.mHasCustomState = false; return; } const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); customData.mContainerStore->writeState (state2.mInventory); customData.mCreatureStats.writeState (state2.mCreatureStats); } int Creature::getBaseGold(const MWWorld::ConstPtr& ptr) const { return ptr.get()->mBase->mData.mGold; } void Creature::respawn(const MWWorld::Ptr &ptr) const { if (isFlagBitSet(ptr, ESM::Creature::Respawn)) { // Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell. // This also means we cannot respawn dynamically placed references with no content file connection. if (ptr.getCellRef().hasContentFile()) { if (ptr.getRefData().getCount() == 0) ptr.getRefData().setCount(1); // Reset to original position ptr.getRefData().setPosition(ptr.getCellRef().getPosition()); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); ptr.getRefData().setCustomData(NULL); } } } void Creature::restock(const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; MWWorld::ContainerStore& store = getContainerStore(ptr); store.restock(list, ptr, ptr.getCellRef().getRefId()); } int Creature::getBaseFightRating(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mAiData.mFight; } void Creature::adjustScale(const MWWorld::ConstPtr &ptr, osg::Vec3f &scale, bool /* rendering */) const { const MWWorld::LiveCellRef *ref = ptr.get(); scale *= ref->mBase->mScale; } } openmw-openmw-0.38.0/apps/openmw/mwclass/creature.hpp000066400000000000000000000134621264522266000226710ustar00rootroot00000000000000#ifndef GAME_MWCLASS_CREATURE_H #define GAME_MWCLASS_CREATURE_H #include "actor.hpp" namespace ESM { struct GameSetting; } namespace MWClass { class Creature : public Actor { void ensureCustomData (const MWWorld::Ptr& ptr) const; virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name); // cached GMSTs struct GMST { const ESM::GameSetting *fMinWalkSpeedCreature; const ESM::GameSetting *fMaxWalkSpeedCreature; const ESM::GameSetting *fEncumberedMoveEffect; const ESM::GameSetting *fSneakSpeedMultiplier; const ESM::GameSetting *fAthleticsRunBonus; const ESM::GameSetting *fBaseRunMultiplier; const ESM::GameSetting *fMinFlySpeed; const ESM::GameSetting *fMaxFlySpeed; const ESM::GameSetting *fSwimRunBase; const ESM::GameSetting *fSwimRunAthleticsMult; const ESM::GameSetting *fKnockDownMult; const ESM::GameSetting *iKnockDownOddsMult; const ESM::GameSetting *iKnockDownOddsBase; }; static const GMST& getGmst(); public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual bool hasToolTip(const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const; ///< Return creature stats virtual void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const; virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual MWWorld::ContainerStore& getContainerStore ( const MWWorld::Ptr& ptr) const; ///< Return container store virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; ///< Return inventory store virtual bool hasInventoryStore (const MWWorld::Ptr &ptr) const; virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual float getCapacity (const MWWorld::Ptr& ptr) const; ///< Return total weight that fits into the object. Throws an exception, if the object can't /// hold other objects. virtual float getArmorRating (const MWWorld::Ptr& ptr) const; ///< @return combined armor rating of this actor virtual bool isEssential (const MWWorld::ConstPtr& ptr) const; ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) virtual int getServices (const MWWorld::ConstPtr& actor) const; virtual bool isPersistent (const MWWorld::ConstPtr& ptr) const; virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; ///< Return desired movement. float getSpeed (const MWWorld::Ptr& ptr) const; static void registerSelf(); virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual bool isActor() const { return true; } virtual bool isBipedal (const MWWorld::ConstPtr &ptr) const; virtual bool canFly (const MWWorld::ConstPtr &ptr) const; virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; virtual bool canWalk (const MWWorld::ConstPtr &ptr) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. virtual int getBaseGold(const MWWorld::ConstPtr& ptr) const; virtual void respawn (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr &ptr) const; virtual int getBaseFightRating(const MWWorld::ConstPtr &ptr) const; virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const; /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/creaturelevlist.cpp000066400000000000000000000106351264522266000242660ustar00rootroot00000000000000#include "creaturelevlist.hpp" #include #include #include "../mwmechanics/levelledlist.hpp" #include "../mwworld/customdata.hpp" namespace MWClass { class CreatureLevListCustomData : public MWWorld::CustomData { public: // actorId of the creature we spawned int mSpawnActorId; bool mSpawn; // Should a new creature be spawned? virtual MWWorld::CustomData *clone() const; virtual CreatureLevListCustomData& asCreatureLevListCustomData() { return *this; } virtual const CreatureLevListCustomData& asCreatureLevListCustomData() const { return *this; } }; MWWorld::CustomData *CreatureLevListCustomData::clone() const { return new CreatureLevListCustomData (*this); } std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const { return ""; } void CreatureLevList::respawn(const MWWorld::Ptr &ptr) const { ensureCustomData(ptr); CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); customData.mSpawn = true; } void CreatureLevList::registerSelf() { boost::shared_ptr instance (new CreatureLevList); registerClass (typeid (ESM::CreatureLevList).name(), instance); } void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string& model, MWRender::RenderingInterface &renderingInterface) const { ensureCustomData(ptr); CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); if (!customData.mSpawn) return; MWWorld::LiveCellRef *ref = ptr.get(); std::string id = MWMechanics::getLevelledItem(ref->mBase, true); if (!id.empty()) { // Delete the previous creature if (customData.mSpawnActorId != -1) { MWWorld::Ptr creature = MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId); if (!creature.isEmpty()) MWBase::Environment::get().getWorld()->deleteObject(creature); customData.mSpawnActorId = -1; } const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); MWWorld::ManualRef ref(store, id); ref.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition()); MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition()); customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); customData.mSpawn = false; } else customData.mSpawn = false; } void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const { if (!ptr.getRefData().getCustomData()) { std::auto_ptr data (new CreatureLevListCustomData); data->mSpawnActorId = -1; data->mSpawn = true; ptr.getRefData().setCustomData(data.release()); } } void CreatureLevList::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { if (!state.mHasCustomState) return; const ESM::CreatureLevListState& state2 = dynamic_cast (state); ensureCustomData(ptr); CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); customData.mSpawnActorId = state2.mSpawnActorId; customData.mSpawn = state2.mSpawn; } void CreatureLevList::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { ESM::CreatureLevListState& state2 = dynamic_cast (state); if (!ptr.getRefData().getCustomData()) { state.mHasCustomState = false; return; } const CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); state2.mSpawnActorId = customData.mSpawnActorId; state2.mSpawn = customData.mSpawn; } } openmw-openmw-0.38.0/apps/openmw/mwclass/creaturelevlist.hpp000066400000000000000000000022361264522266000242710ustar00rootroot00000000000000#ifndef GAME_MWCLASS_CREATURELEVLIST_H #define GAME_MWCLASS_CREATURELEVLIST_H #include "../mwworld/class.hpp" namespace MWClass { class CreatureLevList : public MWWorld::Class { void ensureCustomData (const MWWorld::Ptr& ptr) const; public: virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. static void registerSelf(); virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. virtual void respawn (const MWWorld::Ptr& ptr) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/door.cpp000066400000000000000000000276651264522266000220270ustar00rootroot00000000000000#include "door.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/actionteleport.hpp" #include "../mwworld/actiondoor.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actiontrap.hpp" #include "../mwworld/customdata.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWClass { class DoorCustomData : public MWWorld::CustomData { public: int mDoorState; // 0 = nothing, 1 = opening, 2 = closing virtual MWWorld::CustomData *clone() const; virtual DoorCustomData& asDoorCustomData() { return *this; } virtual const DoorCustomData& asDoorCustomData() const { return *this; } }; MWWorld::CustomData *DoorCustomData::clone() const { return new DoorCustomData (*this); } void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model, true); } } void Door::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { if(!model.empty()) physics.addObject(ptr, model, MWPhysics::CollisionType_Door); // Resume the door's opening/closing animation if it wasn't finished if (ptr.getRefData().getCustomData()) { const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); if (customData.mDoorState > 0) { MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState); } } MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Door::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Door::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Door::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { MWWorld::LiveCellRef *ref = ptr.get(); const std::string &openSound = ref->mBase->mOpenSound; const std::string &closeSound = ref->mBase->mCloseSound; const std::string lockedSound = "LockedDoor"; const std::string trapActivationSound = "Disarm Trap Fail"; MWWorld::ContainerStore &invStore = actor.getClass().getContainerStore(actor); bool needKey = ptr.getCellRef().getLockLevel() > 0; bool hasKey = false; std::string keyName; // make key id lowercase std::string keyId = ptr.getCellRef().getKey(); Misc::StringUtils::lowerCaseInPlace(keyId); for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { std::string refId = it->getCellRef().getRefId(); Misc::StringUtils::lowerCaseInPlace(refId); if (refId == keyId) { hasKey = true; keyName = it->getClass().getName(*it); } } if (needKey && hasKey) { if(actor == MWMechanics::getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}"); unlock(ptr); //Call the function here. because that makes sense. // using a key disarms the trap ptr.getCellRef().setTrap(""); } if (!needKey || hasKey) { if(!ptr.getCellRef().getTrap().empty()) { // Trap activation boost::shared_ptr action(new MWWorld::ActionTrap(actor, ptr.getCellRef().getTrap(), ptr)); action->setSound(trapActivationSound); return action; } if (ptr.getCellRef().getTeleport()) { boost::shared_ptr action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true)); action->setSound(openSound); return action; } else { // animated door boost::shared_ptr action(new MWWorld::ActionDoor(ptr)); int doorstate = getDoorState(ptr); bool opening = true; float doorRot = ptr.getRefData().getPosition().rot[2] - ptr.getCellRef().getPosition().rot[2]; if (doorstate == 1) opening = false; if (doorstate == 0 && doorRot != 0) opening = false; if (opening) { MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, closeSound, 0.5f); // Doors rotate at 90 degrees per second, so start the sound at // where it would be at the current rotation. float offset = doorRot/(3.14159265f * 0.5f); action->setSoundOffset(offset); action->setSound(openSound); } else { MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, openSound, 0.5f); float offset = 1.0f - doorRot/(3.14159265f * 0.5f); action->setSoundOffset(std::max(offset, 0.0f)); action->setSound(closeSound); } return action; } } else { // locked, and we can't open. boost::shared_ptr action(new MWWorld::FailedAction); action->setSound(lockedSound); return action; } } void Door::lock (const MWWorld::Ptr& ptr, int lockLevel) const { if(lockLevel!=0) ptr.getCellRef().setLockLevel(abs(lockLevel)); //Changes lock to locklevel, in positive else ptr.getCellRef().setLockLevel(abs(ptr.getCellRef().getLockLevel())); //No locklevel given, just flip the origional one } void Door::unlock (const MWWorld::Ptr& ptr) const { ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative } bool Door::canLock(const MWWorld::ConstPtr &ptr) const { return true; } std::string Door::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } void Door::registerSelf() { boost::shared_ptr instance (new Door); registerClass (typeid (ESM::Door).name(), instance); } bool Door::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName; std::string text; if (ptr.getCellRef().getTeleport()) { text += "\n#{sTo}"; text += "\n" + getDestination(*ref); } if (ptr.getCellRef().getLockLevel() > 0) text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ptr.getCellRef().getLockLevel()); else if (ptr.getCellRef().getLockLevel() < 0) text += "\n#{sUnlocked}"; if (ptr.getCellRef().getTrap() != "") text += "\n#{sTrapped}"; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } std::string Door::getDestination (const MWWorld::LiveCellRef& door) { const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); std::string dest; if (door.mRef.getDestCell() != "") { // door leads to an interior, use interior name as tooltip dest = door.mRef.getDestCell(); } else { // door leads to exterior, use cell name (if any), otherwise translated region name int x,y; MWBase::Environment::get().getWorld()->positionToIndex (door.mRef.getDoorDest().pos[0], door.mRef.getDoorDest().pos[1], x, y); const ESM::Cell* cell = store.get().find(x,y); if (cell->mName != "") dest = cell->mName; else { const ESM::Region* region = store.get().find(cell->mRegion); //name as is, not a token return region->mName; } } return "#{sCell=" + dest + "}"; } MWWorld::Ptr Door::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } void Door::ensureCustomData(const MWWorld::Ptr &ptr) const { if (!ptr.getRefData().getCustomData()) { std::auto_ptr data(new DoorCustomData); data->mDoorState = 0; ptr.getRefData().setCustomData(data.release()); } } int Door::getDoorState (const MWWorld::ConstPtr &ptr) const { if (!ptr.getRefData().getCustomData()) return 0; const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); return customData.mDoorState; } void Door::setDoorState (const MWWorld::Ptr &ptr, int state) const { if (ptr.getCellRef().getTeleport()) throw std::runtime_error("load doors can't be moved"); ensureCustomData(ptr); DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); customData.mDoorState = state; } void Door::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { if (!state.mHasCustomState) return; ensureCustomData(ptr); DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); const ESM::DoorState& state2 = dynamic_cast(state); customData.mDoorState = state2.mDoorState; } void Door::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { if (!ptr.getRefData().getCustomData()) { state.mHasCustomState = false; return; } const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); ESM::DoorState& state2 = dynamic_cast(state); state2.mDoorState = customData.mDoorState; } } openmw-openmw-0.38.0/apps/openmw/mwclass/door.hpp000066400000000000000000000056051264522266000220220ustar00rootroot00000000000000#ifndef GAME_MWCLASS_DOOR_H #define GAME_MWCLASS_DOOR_H #include #include "../mwworld/class.hpp" namespace MWClass { class Door : public MWWorld::Class { void ensureCustomData (const MWWorld::Ptr& ptr) const; virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. static std::string getDestination (const MWWorld::LiveCellRef& door); ///< @return destination cell name or token virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = 0) const; ///< Lock object virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object virtual bool canLock(const MWWorld::ConstPtr &ptr) const; virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr static void registerSelf(); virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; /// 0 = nothing, 1 = opening, 2 = closing virtual int getDoorState (const MWWorld::ConstPtr &ptr) const; /// This does not actually cause the door to move. Use World::activateDoor instead. virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/ingredient.cpp000066400000000000000000000136341264522266000232030ustar00rootroot00000000000000#include "ingredient.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/actioneat.hpp" #include "../mwworld/nullaction.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" namespace MWClass { void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Ingredient::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Ingredient::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Ingredient::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Ingredient::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } std::string Ingredient::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } int Ingredient::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } boost::shared_ptr Ingredient::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action (new MWWorld::ActionEat (ptr)); action->setSound ("Swallow"); return action; } void Ingredient::registerSelf() { boost::shared_ptr instance (new Ingredient); registerClass (typeid (ESM::Ingredient).name(), instance); } std::string Ingredient::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Ingredient Up"); } std::string Ingredient::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Ingredient Down"); } std::string Ingredient::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Ingredient::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Ingredient::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats (player); int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->getFloat(); MWGui::Widgets::SpellEffectList list; for (int i=0; i<4; ++i) { if (ref->mBase->mData.mEffectID[i] < 0) continue; MWGui::Widgets::SpellEffectParams params; params.mEffectID = ref->mBase->mData.mEffectID[i]; params.mAttribute = ref->mBase->mData.mAttributes[i]; params.mSkill = ref->mBase->mData.mSkills[i]; params.mKnown = ( (i == 0 && alchemySkill >= fWortChanceValue) || (i == 1 && alchemySkill >= fWortChanceValue*2) || (i == 2 && alchemySkill >= fWortChanceValue*3) || (i == 3 && alchemySkill >= fWortChanceValue*4)); list.push_back(params); } info.effects = list; info.text = text; return info; } MWWorld::Ptr Ingredient::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } bool Ingredient::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Ingredients) != 0; } float Ingredient::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/ingredient.hpp000066400000000000000000000051371264522266000232070ustar00rootroot00000000000000#ifndef GAME_MWCLASS_INGREDIENT_H #define GAME_MWCLASS_INGREDIENT_H #include "../mwworld/class.hpp" namespace MWClass { class Ingredient : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/itemlevlist.cpp000066400000000000000000000005731264522266000234120ustar00rootroot00000000000000#include "itemlevlist.hpp" #include namespace MWClass { std::string ItemLevList::getName (const MWWorld::ConstPtr& ptr) const { return ""; } void ItemLevList::registerSelf() { boost::shared_ptr instance (new ItemLevList); registerClass (typeid (ESM::ItemLevList).name(), instance); } } openmw-openmw-0.38.0/apps/openmw/mwclass/itemlevlist.hpp000066400000000000000000000007301264522266000234120ustar00rootroot00000000000000#ifndef GAME_MWCLASS_ITEMLEVLIST_H #define GAME_MWCLASS_ITEMLEVLIST_H #include "../mwworld/class.hpp" namespace MWClass { class ItemLevList : public MWWorld::Class { public: virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. static void registerSelf(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/light.cpp000066400000000000000000000213721264522266000221600ustar00rootroot00000000000000#include "light.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/customdata.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" namespace MWClass { void Light::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { MWWorld::LiveCellRef *ref = ptr.get(); // Insert even if model is empty, so that the light is added renderingInterface.getObjects().insertModel(ptr, model, true, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)); } void Light::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->mBase != NULL); // TODO: add option somewhere to enable collision for placeable objects if (!model.empty() && (ref->mBase->mData.mFlags & ESM::Light::Carry) == 0) physics.addObject(ptr, model); if (!ref->mBase->mSound.empty() && !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)) MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Light::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Light::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mModel.empty()) return ""; return ref->mBase->mName; } boost::shared_ptr Light::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { if(!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return boost::shared_ptr(new MWWorld::NullAction()); MWWorld::LiveCellRef *ref = ptr.get(); if(!(ref->mBase->mData.mFlags&ESM::Light::Carry)) return boost::shared_ptr(new MWWorld::FailedAction()); return defaultItemActivate(ptr, actor); } std::string Light::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } std::pair, bool> Light::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots_; if (ref->mBase->mData.mFlags & ESM::Light::Carry) slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedLeft)); return std::make_pair (slots_, false); } int Light::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Light::registerSelf() { boost::shared_ptr instance (new Light); registerClass (typeid (ESM::Light).name(), instance); } std::string Light::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Misc Up"); } std::string Light::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Misc Down"); } std::string Light::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Light::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Light::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; if (Settings::Manager::getBool("show effect duration","Game")) text += "\n#{sDuration}: " + MWGui::ToolTips::toString(ptr.getClass().getRemainingUsageTime(ptr)); if (ref->mBase->mData.mWeight != 0) { text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); } if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } boost::shared_ptr Light::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); action->setSound(getUpSoundId(ptr)); return action; } void Light::setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const { ptr.getCellRef().setChargeFloat(duration); } float Light::getRemainingUsageTime (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); if (ptr.getCellRef().getCharge() == -1) return static_cast(ref->mBase->mData.mTime); else return ptr.getCellRef().getChargeFloat(); } MWWorld::Ptr Light::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } bool Light::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Lights) != 0; } float Light::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } std::pair Light::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { const MWWorld::LiveCellRef *ref = ptr.get(); if (!(ref->mBase->mData.mFlags & ESM::Light::Carry)) return std::make_pair(0,""); MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc); MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if(weapon == invStore.end()) return std::make_pair(1,""); /// \todo the 2h check is repeated many times; put it in a function if(weapon->getTypeName() == typeid(ESM::Weapon).name() && (weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || weapon->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)) { return std::make_pair(3,""); } return std::make_pair(1,""); } std::string Light::getSound(const MWWorld::ConstPtr& ptr) const { return ptr.get()->mBase->mSound; } } openmw-openmw-0.38.0/apps/openmw/mwclass/light.hpp000066400000000000000000000065001264522266000221610ustar00rootroot00000000000000#ifndef GAME_MWCLASS_LIGHT_H #define GAME_MWCLASS_LIGHT_H #include "../mwworld/class.hpp" namespace MWClass { class Light : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const; ///< Sets the remaining duration of the object. virtual float getRemainingUsageTime (const MWWorld::ConstPtr& ptr) const; ///< Returns the remaining duration of the object. virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; std::pair canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const; virtual std::string getSound(const MWWorld::ConstPtr& ptr) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/lockpick.cpp000066400000000000000000000125051264522266000226460ustar00rootroot00000000000000#include "lockpick.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" namespace MWClass { void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Lockpick::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Lockpick::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Lockpick::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Lockpick::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } std::string Lockpick::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } std::pair, bool> Lockpick::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { std::vector slots_; slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); return std::make_pair (slots_, false); } int Lockpick::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Lockpick::registerSelf() { boost::shared_ptr instance (new Lockpick); registerClass (typeid (ESM::Lockpick).name(), instance); } std::string Lockpick::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Lockpick Up"); } std::string Lockpick::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Lockpick Down"); } std::string Lockpick::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Lockpick::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; int remainingUses = getItemHealth(ptr); text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } boost::shared_ptr Lockpick::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); action->setSound(getUpSoundId(ptr)); return action; } MWWorld::Ptr Lockpick::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } bool Lockpick::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Picks) != 0; } int Lockpick::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mUses; } float Lockpick::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/lockpick.hpp000066400000000000000000000062671264522266000226630ustar00rootroot00000000000000#ifndef GAME_MWCLASS_LOCKPICK_H #define GAME_MWCLASS_LOCKPICK_H #include "../mwworld/class.hpp" namespace MWClass { class Lockpick : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const { return true; } ///< \return Item health data available? (default implementation: false) }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/misc.cpp000066400000000000000000000204161264522266000220020ustar00rootroot00000000000000#include "misc.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/actionsoulgem.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include namespace MWClass { bool Miscellaneous::isGold (const MWWorld::ConstPtr& ptr) const { return Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "gold_001") || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "gold_005") || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "gold_010") || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "gold_100"); } void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Miscellaneous::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Miscellaneous::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } std::string Miscellaneous::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } int Miscellaneous::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); int value = ref->mBase->mData.mValue; if (ptr.getCellRef().getGoldValue() > 1 && ptr.getRefData().getCount() == 1) value = ptr.getCellRef().getGoldValue(); if (ptr.getCellRef().getSoul() != "") { const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mRef.getSoul()); value *= creature->mData.mSoul; } return value; } void Miscellaneous::registerSelf() { boost::shared_ptr instance (new Miscellaneous); registerClass (typeid (ESM::Miscellaneous).name(), instance); } std::string Miscellaneous::getUpSoundId (const MWWorld::ConstPtr& ptr) const { if (isGold(ptr)) return std::string("Item Gold Up"); return std::string("Item Misc Up"); } std::string Miscellaneous::getDownSoundId (const MWWorld::ConstPtr& ptr) const { if (isGold(ptr)) return std::string("Item Gold Down"); return std::string("Item Misc Down"); } std::string Miscellaneous::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Miscellaneous::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Miscellaneous::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); bool gold = isGold(ptr); if (gold) count *= getValue(ptr); std::string countString; if (!gold) countString = MWGui::ToolTips::getCountString(count); else // gold displays its count also if it's 1. countString = " (" + boost::lexical_cast(count) + ")"; info.caption = ref->mBase->mName + countString; info.icon = ref->mBase->mIcon; if (ref->mRef.getSoul() != "") { const ESM::Creature *creature = store.get().find(ref->mRef.getSoul()); info.caption += " (" + creature->mName + ")"; } std::string text; if (!gold && !ref->mBase->mData.mIsKey) { text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); } if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } MWWorld::Ptr Miscellaneous::copyToCell(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell, int count) const { MWWorld::Ptr newPtr; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); if (isGold(ptr)) { int goldAmount = getValue(ptr) * count; std::string base = "Gold_001"; if (goldAmount >= 100) base = "Gold_100"; else if (goldAmount >= 25) base = "Gold_025"; else if (goldAmount >= 10) base = "Gold_010"; else if (goldAmount >= 5) base = "Gold_005"; // Really, I have no idea why moving ref out of conditional // scope causes list::push_back throwing std::bad_alloc MWWorld::ManualRef newRef(store, base); const MWWorld::LiveCellRef *ref = newRef.getPtr().get(); newPtr = MWWorld::Ptr(cell.insert(ref), &cell); newPtr.getCellRef().setGoldValue(goldAmount); newPtr.getRefData().setCount(1); } else { const MWWorld::LiveCellRef *ref = ptr.get(); newPtr = MWWorld::Ptr(cell.insert(ref), &cell); newPtr.getRefData().setCount(count); } newPtr.getCellRef().unsetRefNum(); return newPtr; } boost::shared_ptr Miscellaneous::use (const MWWorld::Ptr& ptr) const { if (ptr.getCellRef().getSoul().empty()) return boost::shared_ptr(new MWWorld::NullAction()); else return boost::shared_ptr(new MWWorld::ActionSoulgem(ptr)); } bool Miscellaneous::canSell (const MWWorld::ConstPtr& item, int npcServices) const { const MWWorld::LiveCellRef *ref = item.get(); return !ref->mBase->mData.mIsKey && (npcServices & ESM::NPC::Misc) && !isGold(item); } float Miscellaneous::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } bool Miscellaneous::isKey(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mIsKey != 0; } } openmw-openmw-0.38.0/apps/openmw/mwclass/misc.hpp000066400000000000000000000053361264522266000220130ustar00rootroot00000000000000#ifndef GAME_MWCLASS_MISC_H #define GAME_MWCLASS_MISC_H #include "../mwworld/class.hpp" namespace MWClass { class Miscellaneous : public MWWorld::Class { public: virtual MWWorld::Ptr copyToCell(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell, int count) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; virtual bool isKey (const MWWorld::ConstPtr &ptr) const; virtual bool isGold (const MWWorld::ConstPtr& ptr) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/npc.cpp000066400000000000000000001460611264522266000216340ustar00rootroot00000000000000#include "npc.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/disease.hpp" #include "../mwmechanics/combat.hpp" #include "../mwmechanics/autocalcspell.hpp" #include "../mwmechanics/difficultyscaling.hpp" #include "../mwmechanics/character.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" #include "../mwworld/actionopen.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/customdata.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/cellstore.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" namespace { int is_even(double d) { double int_part; modf(d / 2.0, &int_part); return 2.0 * int_part == d; } int round_ieee_754(double d) { double i = floor(d); d -= i; if(d < 0.5) return static_cast(i); if(d > 0.5) return static_cast(i) + 1; if(is_even(i)) return static_cast(i); return static_cast(i) + 1; } void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats) { // race bonus const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get().find(npc->mRace); bool male = (npc->mFlags & ESM::NPC::Female) == 0; int level = creatureStats.getLevel(); for (int i=0; imData.mAttributeValues[i]; creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); } // class bonus const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().get().find(npc->mClass); for (int i=0; i<2; ++i) { int attribute = class_->mData.mAttribute[i]; if (attribute>=0 && attribute<8) { creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); } } // skill bonus for (int attribute=0; attribute < ESM::Attribute::Length; ++attribute) { float modifierSum = 0; for (int j=0; jgetStore().get().find(j); if (skill->mData.mAttribute != attribute) continue; // is this a minor or major skill? float add=0.2f; for (int k=0; k<5; ++k) { if (class_->mData.mSkills[k][0] == j) add=0.5; } for (int k=0; k<5; ++k) { if (class_->mData.mSkills[k][1] == j) add=1.0; } modifierSum += add; } creatureStats.setAttribute(attribute, std::min( round_ieee_754(creatureStats.getAttribute(attribute).getBase() + (level-1) * modifierSum), 100) ); } // initial health int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); int multiplier = 3; if (class_->mData.mSpecialization == ESM::Class::Combat) multiplier += 2; else if (class_->mData.mSpecialization == ESM::Class::Stealth) multiplier += 1; if (class_->mData.mAttribute[0] == ESM::Attribute::Endurance || class_->mData.mAttribute[1] == ESM::Attribute::Endurance) multiplier += 1; creatureStats.setHealth(floor(0.5f * (strength + endurance)) + multiplier * (creatureStats.getLevel() - 1)); } /** * @brief autoCalculateSkills * * Skills are calculated with following formulae ( http://www.uesp.net/wiki/Morrowind:NPCs#Skills ): * * Skills: (Level - 1) × (Majority Multiplier + Specialization Multiplier) * * The Majority Multiplier is 1.0 for a Major or Minor Skill, or 0.1 for a Miscellaneous Skill. * * The Specialization Multiplier is 0.5 for a Skill in the same Specialization as the class, * zero for other Skills. * * and by adding class, race, specialization bonus. */ void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats, const MWWorld::Ptr& ptr) { const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().get().find(npc->mClass); unsigned int level = npcStats.getLevel(); const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get().find(npc->mRace); for (int i = 0; i < 2; ++i) { int bonus = (i==0) ? 10 : 25; for (int i2 = 0; i2 < 5; ++i2) { int index = class_->mData.mSkills[i2][i]; if (index >= 0 && index < ESM::Skill::Length) { npcStats.getSkill(index).setBase (npcStats.getSkill(index).getBase() + bonus); } } } for (int skillIndex = 0; skillIndex < ESM::Skill::Length; ++skillIndex) { float majorMultiplier = 0.1f; float specMultiplier = 0.0f; int raceBonus = 0; int specBonus = 0; for (int raceSkillIndex = 0; raceSkillIndex < 7; ++raceSkillIndex) { if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex) { raceBonus = race->mData.mBonus[raceSkillIndex].mBonus; break; } } for (int k = 0; k < 5; ++k) { // is this a minor or major skill? if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex)) { majorMultiplier = 1.0f; break; } } // is this skill in the same Specialization as the class? const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get().find(skillIndex); if (skill->mData.mSpecialization == class_->mData.mSpecialization) { specMultiplier = 0.5f; specBonus = 5; } npcStats.getSkill(skillIndex).setBase( std::min( round_ieee_754( npcStats.getSkill(skillIndex).getBase() + 5 + raceBonus + specBonus +(int(level)-1) * (majorMultiplier + specMultiplier)), 100)); // Must gracefully handle level 0 } int skills[ESM::Skill::Length]; for (int i=0; i spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race); for (std::vector::iterator it = spells.begin(); it != spells.end(); ++it) npcStats.getSpells().add(*it); } } namespace MWClass { class NpcCustomData : public MWWorld::CustomData { public: MWMechanics::NpcStats mNpcStats; MWMechanics::Movement mMovement; MWWorld::InventoryStore mInventoryStore; virtual MWWorld::CustomData *clone() const; virtual NpcCustomData& asNpcCustomData() { return *this; } virtual const NpcCustomData& asNpcCustomData() const { return *this; } }; MWWorld::CustomData *NpcCustomData::clone() const { return new NpcCustomData (*this); } const Npc::GMST& Npc::getGmst() { static GMST gmst; static bool inited = false; if(!inited) { const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); gmst.fMinWalkSpeed = store.find("fMinWalkSpeed"); gmst.fMaxWalkSpeed = store.find("fMaxWalkSpeed"); gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect"); gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier"); gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus"); gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier"); gmst.fMinFlySpeed = store.find("fMinFlySpeed"); gmst.fMaxFlySpeed = store.find("fMaxFlySpeed"); gmst.fSwimRunBase = store.find("fSwimRunBase"); gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult"); gmst.fJumpEncumbranceBase = store.find("fJumpEncumbranceBase"); gmst.fJumpEncumbranceMultiplier = store.find("fJumpEncumbranceMultiplier"); gmst.fJumpAcrobaticsBase = store.find("fJumpAcrobaticsBase"); gmst.fJumpAcroMultiplier = store.find("fJumpAcroMultiplier"); gmst.fJumpRunMultiplier = store.find("fJumpRunMultiplier"); gmst.fWereWolfRunMult = store.find("fWereWolfRunMult"); gmst.fKnockDownMult = store.find("fKnockDownMult"); gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult"); gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); gmst.fCombatArmorMinMult = store.find("fCombatArmorMinMult"); inited = true; } return gmst; } void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const { if (!ptr.getRefData().getCustomData()) { std::auto_ptr data(new NpcCustomData); MWWorld::LiveCellRef *ref = ptr.get(); // creature stats int gold=0; if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { gold = ref->mBase->mNpdt52.mGold; for (unsigned int i=0; i< ESM::Skill::Length; ++i) data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt52.mSkills[i]); data->mNpcStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mNpdt52.mStrength); data->mNpcStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mNpdt52.mIntelligence); data->mNpcStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mNpdt52.mWillpower); data->mNpcStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mNpdt52.mAgility); data->mNpcStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mNpdt52.mSpeed); data->mNpcStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mNpdt52.mEndurance); data->mNpcStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mNpdt52.mPersonality); data->mNpcStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mNpdt52.mLuck); data->mNpcStats.setHealth (ref->mBase->mNpdt52.mHealth); data->mNpcStats.setMagicka (ref->mBase->mNpdt52.mMana); data->mNpcStats.setFatigue (ref->mBase->mNpdt52.mFatigue); data->mNpcStats.setLevel(ref->mBase->mNpdt52.mLevel); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt52.mDisposition); data->mNpcStats.setReputation(ref->mBase->mNpdt52.mReputation); data->mNpcStats.setNeedRecalcDynamicStats(false); } else { gold = ref->mBase->mNpdt12.mGold; for (int i=0; i<3; ++i) data->mNpcStats.setDynamic (i, 10); data->mNpcStats.setLevel(ref->mBase->mNpdt12.mLevel); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition); data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); autoCalculateAttributes(ref->mBase, data->mNpcStats); autoCalculateSkills(ref->mBase, data->mNpcStats, ptr); data->mNpcStats.setNeedRecalcDynamicStats(true); } // race powers const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); for (std::vector::const_iterator iter (race->mPowers.mList.begin()); iter!=race->mPowers.mList.end(); ++iter) { if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mNpcStats.getSpells().add (spell); else std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; } if (!ref->mBase->mFaction.empty()) { static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get() .find("iAutoRepFacMod")->getInt(); static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get() .find("iAutoRepLevMod")->getInt(); int rank = ref->mBase->getFactionRank(); data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1)); } data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage); data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello); data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, ref->mBase->mAiData.mFight); data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee); data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm); // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); iter!=ref->mBase->mSpells.mList.end(); ++iter) { if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mNpcStats.getSpells().add (spell); else { /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; } } // inventory // setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items data->mInventoryStore.fill(ref->mBase->mInventory, ptr.getCellRef().getRefId()); data->mNpcStats.setGoldPool(gold); // store ptr.getRefData().setCustomData (data.release()); getInventoryStore(ptr).autoEquip(ptr); } } void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { renderingInterface.getObjects().insertNPC(ptr); } bool Npc::isPersistent(const MWWorld::ConstPtr &actor) const { const MWWorld::LiveCellRef* ref = actor.get(); return ref->mBase->mPersistent; } std::string Npc::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); std::string model = "meshes\\base_anim.nif"; const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); if(race->mData.mFlags & ESM::Race::Beast) model = "meshes\\base_animkna.nif"; return model; } std::string Npc::getName (const MWWorld::ConstPtr& ptr) const { if(ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf()) { const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); return store.find("sWerewolfPopup")->getString(); } const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } MWMechanics::CreatureStats& Npc::getCreatureStats (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); return ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats; } MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); return ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats; } void Npc::hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const { MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); // Get the weapon used (if hand-to-hand, weapon = inv.end()) MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr()); if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name()) weapon = MWWorld::Ptr(); MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength); const float fCombatDistance = store.find("fCombatDistance")->getFloat(); float dist = fCombatDistance * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : store.find("fHandToHandReach")->getFloat()); // TODO: Use second to work out the hit angle std::pair result = world->getHitContact(ptr, dist); MWWorld::Ptr victim = result.first; osg::Vec3f hitPosition (result.second); if(victim.isEmpty()) // Didn't hit anything return; const MWWorld::Class &othercls = victim.getClass(); if(!othercls.isActor()) // Can't hit non-actors return; MWMechanics::CreatureStats &otherstats = othercls.getCreatureStats(victim); if(otherstats.isDead()) // Can't hit dead actors return; if(ptr == MWMechanics::getPlayer()) MWBase::Environment::get().getWindowManager()->setEnemy(victim); int weapskill = ESM::Skill::HandToHand; if(!weapon.isEmpty()) weapskill = weapon.getClass().getEquipmentSkill(weapon); float hitchance = MWMechanics::getHitChance(ptr, victim, ptr.getClass().getSkill(ptr, weapskill)); if (Misc::Rng::roll0to99() >= hitchance) { othercls.onHit(victim, 0.0f, false, weapon, ptr, false); MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr); return; } bool healthdmg; float damage = 0.0f; if(!weapon.isEmpty()) { const unsigned char *attack = NULL; if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; else if(type == ESM::Weapon::AT_Slash) attack = weapon.get()->mBase->mData.mSlash; else if(type == ESM::Weapon::AT_Thrust) attack = weapon.get()->mBase->mData.mThrust; if(attack) { damage = attack[0] + ((attack[1]-attack[0])*attackStrength); } MWMechanics::adjustWeaponDamage(damage, weapon, ptr); MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr); healthdmg = true; } else { MWMechanics::getHandToHandDamage(ptr, victim, damage, healthdmg, attackStrength); } if(ptr == MWMechanics::getPlayer()) { skillUsageSucceeded(ptr, weapskill, 0); const MWMechanics::AiSequence& seq = victim.getClass().getCreatureStats(victim).getAiSequence(); bool unaware = !seq.isInCombat() && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); if(unaware) { damage *= store.find("fCombatCriticalStrikeMult")->getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } } if (othercls.getCreatureStats(victim).getKnockedDown()) damage *= store.find("fCombatKODamageMult")->getFloat(); // Apply "On hit" enchanted weapons std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : ""; if (!enchantmentName.empty()) { const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( enchantmentName); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { MWMechanics::CastSpell cast(ptr, victim); cast.mHitPosition = hitPosition; cast.cast(weapon); } } MWMechanics::applyElementalShields(ptr, victim); if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength)) damage = 0; if (healthdmg && damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); MWMechanics::diseaseContact(victim, ptr); othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); } void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); // NOTE: 'object' and/or 'attacker' may be empty. bool wasDead = getCreatureStats(ptr).isDead(); // Note OnPcHitMe is not set for friendly hits. bool setOnPcHitMe = true; if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)) { getCreatureStats(ptr).setAttacked(true); setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); } if(!object.isEmpty()) getCreatureStats(ptr).setLastHitAttemptObject(object.getCellRef().getRefId()); if(setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) { const std::string &script = ptr.getClass().getScript(ptr); /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ if(!script.empty()) ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } if(!successful) { // Missed sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f); return; } if(!object.isEmpty()) getCreatureStats(ptr).setLastHitObject(object.getCellRef().getRefId()); if (damage > 0.0f && !object.isEmpty()) MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); if (damage < 0.001f) damage = 0; if(damage > 0.0f && !attacker.isEmpty()) { // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const GMST& gmst = getGmst(); int chance = store.get().find("iVoiceHitOdds")->getInt(); if (Misc::Rng::roll0to99() < chance) { MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); } // Check for knockdown float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat(); float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * gmst.iKnockDownOddsMult->getInt() * 0.01f + gmst.iKnockDownOddsBase->getInt(); if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) { getCreatureStats(ptr).setKnockedDown(true); } else getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? if(damage > 0 && ishealth) { // Hit percentages: // cuirass = 30% // shield, helmet, greaves, boots, pauldrons = 10% each // guantlets = 5% each static const int hitslots[20] = { MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet }; int hitslot = hitslots[Misc::Rng::rollDice(20)]; float unmitigatedDamage = damage; float x = damage / (damage + getArmorRating(ptr)); damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x); int damageDiff = static_cast(unmitigatedDamage - damage); if (damage < 1) damage = 1; MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr()); if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name()) { int armorhealth = armor.getClass().getItemHealth(armor); armorhealth -= std::min(std::max(1, damageDiff), armorhealth); armor.getCellRef().setCharge(armorhealth); // Armor broken? unequip it if (armorhealth == 0) armor = *inv.unequipItem(armor, ptr); if (ptr == MWMechanics::getPlayer()) skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0); switch(armor.getClass().getEquipmentSkill(armor)) { case ESM::Skill::LightArmor: sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); break; case ESM::Skill::MediumArmor: sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); break; case ESM::Skill::HeavyArmor: sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); break; } } else if(ptr == MWMechanics::getPlayer()) skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0); } } if(ishealth) { if (!attacker.isEmpty()) damage = scaleDamage(damage, attacker, ptr); if(damage > 0.0f) { sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); if (ptr == MWMechanics::getPlayer()) MWBase::Environment::get().getWindowManager()->activateHitOverlay(); } MWMechanics::DynamicStat health(getCreatureStats(ptr).getHealth()); health.setCurrent(health.getCurrent() - damage); getCreatureStats(ptr).setHealth(health); } else { MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } if (!wasDead && getCreatureStats(ptr).isDead()) { // NPC was killed if (!attacker.isEmpty() && attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()) { attacker.getClass().getNpcStats(attacker).addWerewolfKill(); } MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, attacker); } } boost::shared_ptr Npc::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { // player got activated by another NPC if(ptr == MWMechanics::getPlayer()) return boost::shared_ptr(new MWWorld::ActionTalk(actor)); // Werewolfs can't activate NPCs if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Sound *sound = store.get().searchRandom("WolfNPC"); boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); if(sound) action->setSound(sound->mId); return action; } if(getCreatureStats(ptr).isDead()) return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat()) return boost::shared_ptr(new MWWorld::FailedAction("#{sActorInCombat}")); if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) || ptr.getClass().getCreatureStats(ptr).getKnockedDown()) return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing // Can't talk to werewolfs if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf()) return boost::shared_ptr (new MWWorld::FailedAction("")); return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); } MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); return ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore; } MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); return ptr.getRefData().getCustomData()->asNpcCustomData().mInventoryStore; } std::string Npc::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } float Npc::getSpeed(const MWWorld::Ptr& ptr) const { const MWBase::World *world = MWBase::Environment::get().getWorld(); const GMST& gmst = getGmst(); const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* (gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat()); walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; walkSpeed = std::max(0.0f, walkSpeed); if(sneaking) walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat(); float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); float moveSpeed; if(getEncumbrance(ptr) > getCapacity(ptr)) moveSpeed = 0.0f; else if(mageffects.get(ESM::MagicEffect::Levitate).getMagnitude() > 0 && world->isLevitationEnabled()) { float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).getMagnitude()); flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } else if(world->isSwimming(ptr)) { float swimSpeed = walkSpeed; if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* gmst.fSwimRunAthleticsMult->getFloat(); moveSpeed = swimSpeed; } else if(running && !sneaking) moveSpeed = runSpeed; else moveSpeed = walkSpeed; if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) moveSpeed *= 0.75f; if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing) moveSpeed *= gmst.fWereWolfRunMult->getFloat(); return moveSpeed; } float Npc::getJump(const MWWorld::Ptr &ptr) const { if(getEncumbrance(ptr) > getCapacity(ptr)) return 0.f; const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const GMST& gmst = getGmst(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() + gmst.fJumpEncumbranceMultiplier->getFloat() * (1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr)); float a = static_cast(npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified()); float b = 0.0f; if(a > 50.0f) { b = a - 50.0f; a = 50.0f; } float x = gmst.fJumpAcrobaticsBase->getFloat() + std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat()); x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat(); x += mageffects.get(ESM::MagicEffect::Jump).getMagnitude() * 64; x *= encumbranceTerm; if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) x *= gmst.fJumpRunMultiplier->getFloat(); x *= npcdata->mNpcStats.getFatigueTerm(); x -= -627.2f;/*gravity constant*/ x /= 3.0f; return x; } MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); return ptr.getRefData().getCustomData()->asNpcCustomData().mMovement; } bool Npc::isEssential (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mFlags & ESM::NPC::Essential) != 0; } void Npc::registerSelf() { boost::shared_ptr instance (new Npc); registerClass (typeid (ESM::NPC).name(), instance); } bool Npc::hasToolTip(const MWWorld::ConstPtr& ptr) const { if (!ptr.getRefData().getCustomData()) return true; const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); return !customData.mNpcStats.getAiSequence().isInCombat() || customData.mNpcStats.isDead(); } MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); bool fullHelp = MWBase::Environment::get().getWindowManager()->getFullHelp(); MWGui::ToolTipInfo info; info.caption = getName(ptr); if(fullHelp && ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf()) { info.caption += " ("; info.caption += ref->mBase->mName; info.caption += ")"; } if(fullHelp) info.text = MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); return info; } float Npc::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEncumbranceStrMult")->getFloat(); return stats.getAttribute(0).getModified()*fEncumbranceStrMult; } float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const { // According to UESP, inventory weight is ignored in werewolf form. Does that include // feather and burden effects? return getNpcStats(ptr).isWerewolf() ? 0.0f : Actor::getEncumbrance(ptr); } bool Npc::apply (const MWWorld::Ptr& ptr, const std::string& id, const MWWorld::Ptr& actor) const { MWMechanics::CastSpell cast(ptr, ptr); return cast.cast(id); } void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor) const { MWMechanics::NpcStats& stats = getNpcStats (ptr); if (stats.isWerewolf()) return; MWWorld::LiveCellRef *ref = ptr.get(); const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().get().find ( ref->mBase->mClass ); stats.useSkill (skill, *class_, usageType, extraFactor); } float Npc::getArmorRating (const MWWorld::Ptr& ptr) const { const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); MWMechanics::NpcStats &stats = getNpcStats(ptr); MWWorld::InventoryStore &invStore = getInventoryStore(ptr); float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat(); int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); int ratings[MWWorld::InventoryStore::Slots]; for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) { MWWorld::ContainerStoreIterator it = invStore.getSlot(i); if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name()) { // unarmored ratings[i] = static_cast((fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill)); } else { ratings[i] = it->getClass().getEffectiveArmorRating(*it, ptr); } } float shield = stats.getMagicEffects().get(ESM::MagicEffect::Shield).getMagnitude(); return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3f + (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet] + ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots] + ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron] ) * 0.1f + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + ratings[MWWorld::InventoryStore::Slot_RightGauntlet]) * 0.05f + shield; } void Npc::adjustScale(const MWWorld::ConstPtr &ptr, osg::Vec3f&scale, bool rendering) const { if (!rendering) return; // collision meshes are not scaled based on race height // having the same collision extents for all races makes the environments easier to test const MWWorld::LiveCellRef *ref = ptr.get(); const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); if (ref->mBase->isMale()) { scale.x() *= race->mData.mWeight.mMale; scale.y() *= race->mData.mWeight.mMale; scale.z() *= race->mData.mHeight.mMale; } else { scale.x() *= race->mData.mWeight.mFemale; scale.y() *= race->mData.mWeight.mFemale; scale.z() *= race->mData.mHeight.mFemale; } } int Npc::getServices(const MWWorld::ConstPtr &actor) const { const MWWorld::LiveCellRef* ref = actor.get(); if (ref->mBase->mHasAI) return ref->mBase->mAiData.mServices; else return 0; } std::string Npc::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const { if(name == "left" || name == "right") { MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isSwimming(ptr)) return (name == "left") ? "Swim Left" : "Swim Right"; if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) return (name == "left") ? "FootWaterLeft" : "FootWaterRight"; if(world->isOnGround(ptr)) { if (ptr.getClass().getNpcStats(ptr).isWerewolf() && ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) { MWMechanics::WeaponType weaponType = MWMechanics::WeapType_None; MWMechanics::getActiveWeapon(ptr.getClass().getCreatureStats(ptr), ptr.getClass().getInventoryStore(ptr), &weaponType); if (weaponType == MWMechanics::WeapType_None) return ""; } MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr); MWWorld::ContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name()) return (name == "left") ? "FootBareLeft" : "FootBareRight"; switch(boots->getClass().getEquipmentSkill(*boots)) { case ESM::Skill::LightArmor: return (name == "left") ? "FootLightLeft" : "FootLightRight"; case ESM::Skill::MediumArmor: return (name == "left") ? "FootMedLeft" : "FootMedRight"; case ESM::Skill::HeavyArmor: return (name == "left") ? "FootHeavyLeft" : "FootHeavyRight"; } } return ""; } if(name == "land") { MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) return "DefaultLandWater"; if(world->isOnGround(ptr)) return "DefaultLand"; return ""; } if(name == "swimleft") return "Swim Left"; if(name == "swimright") return "Swim Right"; // TODO: I have no idea what these are supposed to do for NPCs since they use // voiced dialog for various conditions like health loss and combat taunts. Maybe // only for biped creatures? if(name == "moan") return ""; if(name == "roar") return ""; if(name == "scream") return ""; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } MWWorld::Ptr Npc::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const { return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified(); } int Npc::getBloodTexture(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); if (ref->mBase->mFlags & ESM::NPC::Skeleton) return 1; if (ref->mBase->mFlags & ESM::NPC::Metal) return 2; return 0; } void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { if (!state.mHasCustomState) return; const ESM::NpcState& state2 = dynamic_cast (state); if (state.mVersion > 0) { if (!ptr.getRefData().getCustomData()) { // Create a CustomData, but don't fill it from ESM records (not needed) std::auto_ptr data (new NpcCustomData); ptr.getRefData().setCustomData (data.release()); } } else ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless. NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); customData.mInventoryStore.readState (state2.mInventory); customData.mNpcStats.readState (state2.mNpcStats); static_cast (customData.mNpcStats).readState (state2.mCreatureStats); } void Npc::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { ESM::NpcState& state2 = dynamic_cast (state); if (!ptr.getRefData().getCustomData()) { state.mHasCustomState = false; return; } const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); customData.mInventoryStore.writeState (state2.mInventory); customData.mNpcStats.writeState (state2.mNpcStats); static_cast (customData.mNpcStats).writeState (state2.mCreatureStats); } int Npc::getBaseGold(const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) return ref->mBase->mNpdt52.mGold; else return ref->mBase->mNpdt12.mGold; } bool Npc::isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const { return Misc::StringUtils::ciEqual(ptr.get()->mBase->mClass, className); } bool Npc::canSwim(const MWWorld::ConstPtr &ptr) const { return true; } bool Npc::canWalk(const MWWorld::ConstPtr &ptr) const { return true; } void Npc::respawn(const MWWorld::Ptr &ptr) const { if (ptr.get()->mBase->mFlags & ESM::NPC::Respawn) { // Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell. // This also means we cannot respawn dynamically placed references with no content file connection. if (ptr.getCellRef().hasContentFile()) { if (ptr.getRefData().getCount() == 0) ptr.getRefData().setCount(1); // Reset to original position ptr.getRefData().setPosition(ptr.getCellRef().getPosition()); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); ptr.getRefData().setCustomData(NULL); } } } void Npc::restock(const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; MWWorld::ContainerStore& store = getContainerStore(ptr); store.restock(list, ptr, ptr.getCellRef().getRefId()); } int Npc::getBaseFightRating (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mAiData.mFight; } bool Npc::isBipedal(const MWWorld::ConstPtr &ptr) const { return true; } std::string Npc::getPrimaryFaction (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mFaction; } int Npc::getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->getFactionRank(); } } openmw-openmw-0.38.0/apps/openmw/mwclass/npc.hpp000066400000000000000000000166431264522266000216430ustar00rootroot00000000000000#ifndef GAME_MWCLASS_NPC_H #define GAME_MWCLASS_NPC_H #include "actor.hpp" namespace ESM { struct GameSetting; } namespace MWClass { class Npc : public Actor { void ensureCustomData (const MWWorld::Ptr& ptr) const; virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; struct GMST { const ESM::GameSetting *fMinWalkSpeed; const ESM::GameSetting *fMaxWalkSpeed; const ESM::GameSetting *fEncumberedMoveEffect; const ESM::GameSetting *fSneakSpeedMultiplier; const ESM::GameSetting *fAthleticsRunBonus; const ESM::GameSetting *fBaseRunMultiplier; const ESM::GameSetting *fMinFlySpeed; const ESM::GameSetting *fMaxFlySpeed; const ESM::GameSetting *fSwimRunBase; const ESM::GameSetting *fSwimRunAthleticsMult; const ESM::GameSetting *fJumpEncumbranceBase; const ESM::GameSetting *fJumpEncumbranceMultiplier; const ESM::GameSetting *fJumpAcrobaticsBase; const ESM::GameSetting *fJumpAcroMultiplier; const ESM::GameSetting *fJumpRunMultiplier; const ESM::GameSetting *fWereWolfRunMult; const ESM::GameSetting *fKnockDownMult; const ESM::GameSetting *iKnockDownOddsMult; const ESM::GameSetting *iKnockDownOddsBase; const ESM::GameSetting *fCombatArmorMinMult; }; static const GMST& getGmst(); public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const; ///< Return creature stats virtual MWMechanics::NpcStats& getNpcStats (const MWWorld::Ptr& ptr) const; ///< Return NPC stats virtual MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const; ///< Return container store virtual bool hasToolTip(const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; ///< Return inventory store virtual bool hasInventoryStore(const MWWorld::Ptr &ptr) const { return true; } virtual void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const; virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual float getSpeed (const MWWorld::Ptr& ptr) const; ///< Return movement speed. virtual float getJump(const MWWorld::Ptr &ptr) const; ///< Return jump velocity (not accounting for movement) virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; ///< Return desired movement. virtual float getCapacity (const MWWorld::Ptr& ptr) const; ///< Return total weight that fits into the object. Throws an exception, if the object can't /// hold other objects. virtual float getEncumbrance (const MWWorld::Ptr& ptr) const; ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. virtual float getArmorRating (const MWWorld::Ptr& ptr) const; ///< @return combined armor rating of this actor virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id, const MWWorld::Ptr& actor) const; ///< Apply \a id on \a ptr. /// \param actor Actor that is resposible for the ID being applied to \a ptr. /// \return Any effect? virtual void adjustScale (const MWWorld::ConstPtr &ptr, osg::Vec3f &scale, bool rendering) const; /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh virtual void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor=1.f) const; ///< Inform actor \a ptr that a skill use has succeeded. virtual bool isEssential (const MWWorld::ConstPtr& ptr) const; ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) virtual int getServices (const MWWorld::ConstPtr& actor) const; virtual bool isPersistent (const MWWorld::ConstPtr& ptr) const; virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; static void registerSelf(); virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; virtual bool isActor() const { return true; } virtual bool isNpc() const { return true; } virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. virtual int getBaseGold(const MWWorld::ConstPtr& ptr) const; virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const; virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; virtual bool canWalk (const MWWorld::ConstPtr &ptr) const; virtual bool isBipedal (const MWWorld::ConstPtr &ptr) const; virtual void respawn (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr& ptr) const; virtual int getBaseFightRating (const MWWorld::ConstPtr& ptr) const; virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const; virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/potion.cpp000066400000000000000000000132131264522266000223540ustar00rootroot00000000000000#include "potion.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionapply.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/containerstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwmechanics/npcstats.hpp" namespace MWClass { void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Potion::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Potion::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Potion::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Potion::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } std::string Potion::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } int Potion::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Potion::registerSelf() { boost::shared_ptr instance (new Potion); registerClass (typeid (ESM::Potion).name(), instance); } std::string Potion::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Potion Up"); } std::string Potion::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Potion Down"); } std::string Potion::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Potion::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Potion::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects); // hide effects the player doesnt know about MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats (player); int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); int i=0; static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->getFloat(); for (MWGui::Widgets::SpellEffectList::iterator it = info.effects.begin(); it != info.effects.end(); ++it) { it->mKnown = (i <= 1 && alchemySkill >= fWortChanceValue) || (i <= 3 && alchemySkill >= fWortChanceValue*2); ++i; } info.isPotion = true; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } boost::shared_ptr Potion::use (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); boost::shared_ptr action ( new MWWorld::ActionApply (ptr, ref->mBase->mId)); action->setSound ("Drink"); return action; } MWWorld::Ptr Potion::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } bool Potion::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Potions) != 0; } float Potion::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/potion.hpp000066400000000000000000000050671264522266000223710ustar00rootroot00000000000000#ifndef GAME_MWCLASS_POTION_H #define GAME_MWCLASS_POTION_H #include "../mwworld/class.hpp" namespace MWClass { class Potion : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/probe.cpp000066400000000000000000000123151264522266000221550ustar00rootroot00000000000000#include "probe.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" namespace MWClass { void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Probe::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Probe::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Probe::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Probe::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } std::string Probe::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } std::pair, bool> Probe::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { std::vector slots_; slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); return std::make_pair (slots_, false); } int Probe::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Probe::registerSelf() { boost::shared_ptr instance (new Probe); registerClass (typeid (ESM::Probe).name(), instance); } std::string Probe::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Probe Up"); } std::string Probe::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Probe Down"); } std::string Probe::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Probe::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Probe::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; int remainingUses = getItemHealth(ptr); text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } boost::shared_ptr Probe::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); action->setSound(getUpSoundId(ptr)); return action; } MWWorld::Ptr Probe::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } bool Probe::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Probes) != 0; } int Probe::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mUses; } float Probe::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/probe.hpp000066400000000000000000000062561264522266000221710ustar00rootroot00000000000000#ifndef GAME_MWCLASS_PROBE_H #define GAME_MWCLASS_PROBE_H #include "../mwworld/class.hpp" namespace MWClass { class Probe : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const { return true; } ///< \return Item health data available? (default implementation: false) }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/repair.cpp000066400000000000000000000117461264522266000223370ustar00rootroot00000000000000#include "repair.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/actionrepair.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" namespace MWClass { void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Repair::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Repair::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Repair::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Repair::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } std::string Repair::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } int Repair::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Repair::registerSelf() { boost::shared_ptr instance (new Repair); registerClass (typeid (ESM::Repair).name(), instance); } std::string Repair::getUpSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Repair Up"); } std::string Repair::getDownSoundId (const MWWorld::ConstPtr& ptr) const { return std::string("Item Repair Down"); } std::string Repair::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Repair::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } bool Repair::hasItemHealth (const MWWorld::ConstPtr& ptr) const { return true; } int Repair::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mUses; } MWGui::ToolTipInfo Repair::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; int remainingUses = getItemHealth(ptr); text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } MWWorld::Ptr Repair::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } boost::shared_ptr Repair::use (const MWWorld::Ptr& ptr) const { return boost::shared_ptr(new MWWorld::ActionRepair(ptr)); } bool Repair::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::RepairItem) != 0; } float Repair::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/repair.hpp000066400000000000000000000060311264522266000223330ustar00rootroot00000000000000#ifndef GAME_MWCLASS_REPAIR_H #define GAME_MWCLASS_REPAIR_H #include "../mwworld/class.hpp" namespace MWClass { class Repair : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu (default implementation: return a /// null action). virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const; ///< \return Item health data available? (default implementation: false) virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exception) virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/static.cpp000066400000000000000000000030541264522266000223350ustar00rootroot00000000000000#include "static.hpp" #include #include "../mwworld/ptr.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/cellstore.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" namespace MWClass { void Static::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Static::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { if(!model.empty()) physics.addObject(ptr, model); } std::string Static::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Static::getName (const MWWorld::ConstPtr& ptr) const { return ""; } void Static::registerSelf() { boost::shared_ptr instance (new Static); registerClass (typeid (ESM::Static).name(), instance); } MWWorld::Ptr Static::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } } openmw-openmw-0.38.0/apps/openmw/mwclass/static.hpp000066400000000000000000000017421264522266000223440ustar00rootroot00000000000000#ifndef GAME_MWCLASS_STATIC_H #define GAME_MWCLASS_STATIC_H #include "../mwworld/class.hpp" namespace MWClass { class Static : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. static void registerSelf(); virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwclass/weapon.cpp000066400000000000000000000355471264522266000223530ustar00rootroot00000000000000#include "weapon.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" namespace MWClass { void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); } } void Weapon::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects } std::string Weapon::getModel(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->mBase->mModel; if (!model.empty()) { return "meshes\\" + model; } return ""; } std::string Weapon::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mName; } boost::shared_ptr Weapon::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { return defaultItemActivate(ptr, actor); } bool Weapon::hasItemHealth (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mData.mType < 11); // thrown weapons and arrows/bolts don't have health, only quantity } int Weapon::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mHealth; } std::string Weapon::getScript (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } std::pair, bool> Weapon::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots_; bool stack = false; if (ref->mBase->mData.mType==ESM::Weapon::Arrow || ref->mBase->mData.mType==ESM::Weapon::Bolt) { slots_.push_back (int (MWWorld::InventoryStore::Slot_Ammunition)); stack = true; } else if (ref->mBase->mData.mType==ESM::Weapon::MarksmanThrown) { slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); stack = true; } else slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); return std::make_pair (slots_, stack); } int Weapon::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); const int size = 12; static const int sMapping[size][2] = { { ESM::Weapon::ShortBladeOneHand, ESM::Skill::ShortBlade }, { ESM::Weapon::LongBladeOneHand, ESM::Skill::LongBlade }, { ESM::Weapon::LongBladeTwoHand, ESM::Skill::LongBlade }, { ESM::Weapon::BluntOneHand, ESM::Skill::BluntWeapon }, { ESM::Weapon::BluntTwoClose, ESM::Skill::BluntWeapon }, { ESM::Weapon::BluntTwoWide, ESM::Skill::BluntWeapon }, { ESM::Weapon::SpearTwoWide, ESM::Skill::Spear }, { ESM::Weapon::AxeOneHand, ESM::Skill::Axe }, { ESM::Weapon::AxeTwoHand, ESM::Skill::Axe }, { ESM::Weapon::MarksmanBow, ESM::Skill::Marksman }, { ESM::Weapon::MarksmanCrossbow, ESM::Skill::Marksman }, { ESM::Weapon::MarksmanThrown, ESM::Skill::Marksman } }; for (int i=0; imBase->mData.mType) return sMapping[i][1]; return -1; } int Weapon::getValue (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mValue; } void Weapon::registerSelf() { boost::shared_ptr instance (new Weapon); registerClass (typeid (ESM::Weapon).name(), instance); } std::string Weapon::getUpSoundId (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); int type = ref->mBase->mData.mType; // Ammo if (type == 12 || type == 13) { return std::string("Item Ammo Up"); } // Bow if (type == 9) { return std::string("Item Weapon Bow Up"); } // Crossbow if (type == 10) { return std::string("Item Weapon Crossbow Up"); } // Longblades, One hand and Two if (type == 1 || type == 2) { return std::string("Item Weapon Longblade Up"); } // Shortblade if (type == 0) { return std::string("Item Weapon Shortblade Up"); } // Spear if (type == 6) { return std::string("Item Weapon Spear Up"); } // Blunts, Axes and Thrown weapons if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8 || type == 11) { return std::string("Item Weapon Blunt Up"); } return std::string("Item Misc Up"); } std::string Weapon::getDownSoundId (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); int type = ref->mBase->mData.mType; // Ammo if (type == 12 || type == 13) { return std::string("Item Ammo Down"); } // Bow if (type == 9) { return std::string("Item Weapon Bow Down"); } // Crossbow if (type == 10) { return std::string("Item Weapon Crossbow Down"); } // Longblades, One hand and Two if (type == 1 || type == 2) { return std::string("Item Weapon Longblade Down"); } // Shortblade if (type == 0) { return std::string("Item Weapon Shortblade Down"); } // Spear if (type == 6) { return std::string("Item Weapon Spear Down"); } // Blunts, Axes and Thrown weapons if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8 || type == 11) { return std::string("Item Weapon Blunt Down"); } return std::string("Item Misc Down"); } std::string Weapon::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mIcon; } bool Weapon::hasToolTip (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); std::string text; // weapon type & damage. arrows / bolts don't have his info. if (ref->mBase->mData.mType < 12) { text += "\n#{sType} "; std::map > mapping; mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded"); mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded"); mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded"); mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded"); mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded"); mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded"); mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded"); mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", ""); mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", ""); mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", ""); std::string type = mapping[ref->mBase->mData.mType].first; std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second; text += store.get().find(type)->getString() + ((oneOrTwoHanded != "") ? ", " + store.get().find(oneOrTwoHanded)->getString() : ""); // weapon damage if (ref->mBase->mData.mType >= 9) { // marksman text += "\n#{sAttack}: " + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mChop[0])) + " - " + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mChop[1])); } else { // Chop text += "\n#{sChop}: " + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mChop[0])) + " - " + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mChop[1])); // Slash text += "\n#{sSlash}: " + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mSlash[0])) + " - " + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mSlash[1])); // Thrust text += "\n#{sThrust}: " + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mThrust[0])) + " - " + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mThrust[1])); } } if (ref->mBase->mData.mType < 11) // thrown weapons and arrows/bolts don't have health, only quantity { int remainingHealth = getItemHealth(ptr); text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/" + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); } text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); info.enchant = ref->mBase->mEnchant; if (!info.enchant.empty()) info.remainingEnchantCharge = static_cast(ptr.getCellRef().getEnchantmentCharge()); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; return info; } std::string Weapon::getEnchantment (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mEnchant; } std::string Weapon::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { const MWWorld::LiveCellRef *ref = ptr.get(); ESM::Weapon newItem = *ref->mBase; newItem.mId=""; newItem.mName=newName; newItem.mData.mEnchant=enchCharge; newItem.mEnchant=enchId; newItem.mData.mFlags |= ESM::Weapon::Magical; const ESM::Weapon *record = MWBase::Environment::get().getWorld()->createRecord (newItem); return record->mId; } std::pair Weapon::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0) return std::make_pair(0, "#{sInventoryMessage1}"); std::pair, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr); if (slots_.first.empty()) return std::make_pair (0, ""); if(ptr.get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || ptr.get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || ptr.get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { return std::make_pair (2, ""); } return std::make_pair(1, ""); } boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); action->setSound(getUpSoundId(ptr)); return action; } MWWorld::Ptr Weapon::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const { const MWWorld::LiveCellRef *ref = ptr.get(); return MWWorld::Ptr(cell.insert(ref), &cell); } int Weapon::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mEnchant; } bool Weapon::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return (npcServices & ESM::NPC::Weapon) || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } float Weapon::getWeight(const MWWorld::ConstPtr &ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mData.mWeight; } } openmw-openmw-0.38.0/apps/openmw/mwclass/weapon.hpp000066400000000000000000000103341264522266000223430ustar00rootroot00000000000000#ifndef GAME_MWCLASS_WEAPON_H #define GAME_MWCLASS_WEAPON_H #include "../mwworld/class.hpp" namespace MWClass { class Weapon : public MWWorld::Class { virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual bool hasItemHealth (const MWWorld::ConstPtr& ptr) const; ///< \return Item health data available? virtual int getItemMaxHealth (const MWWorld::ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr virtual std::pair, bool> getEquipmentSlots (const MWWorld::ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? virtual int getEquipmentSkill (const MWWorld::ConstPtr& ptr) const; /// Return the index of the skill this item corresponds to when equipped or -1, if there is /// no such skill. virtual int getValue (const MWWorld::ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the pick up sound Id virtual std::string getDownSoundId (const MWWorld::ConstPtr& ptr) const; ///< Return the put down sound Id virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. virtual std::pair canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const; ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. /// Second item in the pair specifies the error message virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/000077500000000000000000000000001264522266000210245ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwdialogue/dialoguemanagerimp.cpp000066400000000000000000000645761264522266000254040ustar00rootroot00000000000000#include "dialoguemanagerimp.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwgui/dialogue.hpp" #include "../mwscript/compilercontext.hpp" #include "../mwscript/interpretercontext.hpp" #include "../mwscript/extensions.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "filter.hpp" #include "hypertextparser.hpp" namespace MWDialogue { DialogueManager::DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage) : mTranslationDataStorage(translationDataStorage) , mCompilerContext (MWScript::CompilerContext::Type_Dialogue) , mErrorStream(std::cout.rdbuf()) , mErrorHandler(mErrorStream) , mTalkedTo(false) , mTemporaryDispositionChange(0.f) , mPermanentDispositionChange(0.f) { mChoice = -1; mIsInChoice = false; mCompilerContext.setExtensions (&extensions); const MWWorld::Store &dialogs = MWBase::Environment::get().getWorld()->getStore().get(); MWWorld::Store::iterator it = dialogs.begin(); for (; it != dialogs.end(); ++it) { mDialogueMap[Misc::StringUtils::lowerCase(it->mId)] = *it; } } void DialogueManager::clear() { mKnownTopics.clear(); mTalkedTo = false; mTemporaryDispositionChange = 0; mPermanentDispositionChange = 0; } void DialogueManager::addTopic (const std::string& topic) { mKnownTopics.insert( Misc::StringUtils::lowerCase(topic) ); } void DialogueManager::parseText (const std::string& text) { std::vector hypertext = HyperTextParser::parseHyperText(text); for (std::vector::iterator tok = hypertext.begin(); tok != hypertext.end(); ++tok) { std::string topicId = Misc::StringUtils::lowerCase(tok->mText); if (tok->isExplicitLink()) { // calculation of standard form for all hyperlinks size_t asterisk_count = HyperTextParser::removePseudoAsterisks(topicId); for(; asterisk_count > 0; --asterisk_count) topicId.append("*"); topicId = mTranslationDataStorage.topicStandardForm(topicId); } if (tok->isImplicitKeyword() && mTranslationDataStorage.hasTranslation()) continue; if (mActorKnownTopics.count( topicId )) mKnownTopics.insert( topicId ); } updateTopics(); } void DialogueManager::startDialogue (const MWWorld::Ptr& actor) { updateGlobals(); // Dialogue with dead actor (e.g. through script) should not be allowed. if (actor.getClass().getCreatureStats(actor).isDead()) return; mLastTopic = ""; mPermanentDispositionChange = 0; mTemporaryDispositionChange = 0; mChoice = -1; mIsInChoice = false; mActor = actor; MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats (actor); mTalkedTo = creatureStats.hasTalkedToPlayer(); mActorKnownTopics.clear(); MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); // If the dialogue window was already open, keep the existing history bool resetHistory = (!MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue)); win->startDialogue(actor, actor.getClass().getName (actor), resetHistory); //greeting const MWWorld::Store &dialogs = MWBase::Environment::get().getWorld()->getStore().get(); Filter filter (actor, mChoice, mTalkedTo); for (MWWorld::Store::iterator it = dialogs.begin(); it != dialogs.end(); ++it) { if(it->mType == ESM::Dialogue::Greeting) { // Search a response (we do not accept a fallback to "Info refusal" here) if (const ESM::DialInfo *info = filter.search (*it, false)) { //initialise the GUI MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); creatureStats.talkedToPlayer(); if (!info->mSound.empty()) { // TODO play sound } // first topics update so that parseText knows the keywords to highlight updateTopics(); parseText (info->mResponse); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript); mLastTopic = Misc::StringUtils::lowerCase(it->mId); // update topics again to accomodate changes resulting from executeScript updateTopics(); return; } } } // No greetings found. The dialogue window should not be shown. // If this is a companion, we must show the companion window directly (used by BM_bear_be_unique). bool isCompanion = !mActor.getClass().getScript(mActor).empty() && mActor.getRefData().getLocals().getIntVar(mActor.getClass().getScript(mActor), "companion"); if (isCompanion) MWBase::Environment::get().getWindowManager()->showCompanionWindow(mActor); } bool DialogueManager::compile (const std::string& cmd,std::vector& code) { bool success = true; try { mErrorHandler.reset(); std::istringstream input (cmd + "\n"); Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions()); Compiler::Locals locals; std::string actorScript = mActor.getClass().getScript (mActor); if (!actorScript.empty()) { // grab local variables from actor's script, if available. locals = MWBase::Environment::get().getScriptManager()->getLocals (actorScript); } Compiler::ScriptParser parser(mErrorHandler,mCompilerContext, locals, false); scanner.scan (parser); if (!mErrorHandler.isGood()) success = false; if (success) parser.getCode (code); } catch (const Compiler::SourceException& /* error */) { // error has already been reported via error handler success = false; } catch (const std::exception& error) { std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; success = false; } if (!success) { std::cerr << "compiling failed (dialogue script)" << std::endl << cmd << std::endl << std::endl; } return success; } void DialogueManager::executeScript (const std::string& script) { std::vector code; if(compile(script,code)) { try { MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); Interpreter::Interpreter interpreter; MWScript::installOpcodes (interpreter); interpreter.run (&code[0], code.size(), interpreterContext); } catch (const std::exception& error) { std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; } } } void DialogueManager::executeTopic (const std::string& topic) { Filter filter (mActor, mChoice, mTalkedTo); const MWWorld::Store &dialogues = MWBase::Environment::get().getWorld()->getStore().get(); const ESM::Dialogue& dialogue = *dialogues.find (topic); MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); const ESM::DialInfo* info = filter.search(dialogue, true); if (info) { parseText (info->mResponse); std::string title; if (dialogue.mType==ESM::Dialogue::Persuasion) { // Determine GMST from dialogue topic. GMSTs are: // sAdmireSuccess, sAdmireFail, sIntimidateSuccess, sIntimidateFail, // sTauntSuccess, sTauntFail, sBribeSuccess, sBribeFail std::string modifiedTopic = "s" + topic; modifiedTopic.erase (std::remove (modifiedTopic.begin(), modifiedTopic.end(), ' '), modifiedTopic.end()); const MWWorld::Store& gmsts = MWBase::Environment::get().getWorld()->getStore().get(); title = gmsts.find (modifiedTopic)->getString(); } else title = topic; MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); if (dialogue.mType == ESM::Dialogue::Topic) { // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group, // in which case it should not be added to the journal. for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) { if (iter->mId == info->mId) { MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor); break; } } } executeScript (info->mResultScript); mLastTopic = topic; } else { // no response found, print a fallback text win->addResponse ("…", topic); } } void DialogueManager::updateGlobals() { MWBase::Environment::get().getWorld()->updateDialogueGlobals(); } void DialogueManager::updateTopics() { updateGlobals(); std::list keywordList; int choice = mChoice; mChoice = -1; mActorKnownTopics.clear(); const MWWorld::Store &dialogs = MWBase::Environment::get().getWorld()->getStore().get(); Filter filter (mActor, mChoice, mTalkedTo); for (MWWorld::Store::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter) { if (iter->mType == ESM::Dialogue::Topic) { if (filter.responseAvailable (*iter)) { std::string lower = Misc::StringUtils::lowerCase(iter->mId); mActorKnownTopics.insert (lower); //does the player know the topic? if (mKnownTopics.count(lower)) { keywordList.push_back (iter->mId); } } } } // check the available services of this actor int services = 0; if (mActor.getTypeName() == typeid(ESM::NPC).name()) { MWWorld::LiveCellRef* ref = mActor.get(); if (ref->mBase->mHasAI) services = ref->mBase->mAiData.mServices; } else if (mActor.getTypeName() == typeid(ESM::Creature).name()) { MWWorld::LiveCellRef* ref = mActor.get(); if (ref->mBase->mHasAI) services = ref->mBase->mAiData.mServices; } int windowServices = 0; if (services & ESM::NPC::Weapon || services & ESM::NPC::Armor || services & ESM::NPC::Clothing || services & ESM::NPC::Books || services & ESM::NPC::Ingredients || services & ESM::NPC::Picks || services & ESM::NPC::Probes || services & ESM::NPC::Lights || services & ESM::NPC::Apparatus || services & ESM::NPC::RepairItem || services & ESM::NPC::Misc) windowServices |= MWGui::DialogueWindow::Service_Trade; if((mActor.getTypeName() == typeid(ESM::NPC).name() && !mActor.get()->mBase->getTransport().empty()) || (mActor.getTypeName() == typeid(ESM::Creature).name() && !mActor.get()->mBase->getTransport().empty())) windowServices |= MWGui::DialogueWindow::Service_Travel; if (services & ESM::NPC::Spells) windowServices |= MWGui::DialogueWindow::Service_BuySpells; if (services & ESM::NPC::Spellmaking) windowServices |= MWGui::DialogueWindow::Service_CreateSpells; if (services & ESM::NPC::Training) windowServices |= MWGui::DialogueWindow::Service_Training; if (services & ESM::NPC::Enchanting) windowServices |= MWGui::DialogueWindow::Service_Enchant; if (services & ESM::NPC::Repair) windowServices |= MWGui::DialogueWindow::Service_Repair; MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->setServices (windowServices); // sort again, because the previous sort was case-sensitive keywordList.sort(Misc::StringUtils::ciLess); win->setKeywords(keywordList); mChoice = choice; } void DialogueManager::keywordSelected (const std::string& keyword) { if(!mIsInChoice) { if(mDialogueMap.find(keyword) != mDialogueMap.end()) { if (mDialogueMap[keyword].mType == ESM::Dialogue::Topic) { executeTopic (keyword); } } } updateTopics(); } bool DialogueManager::isInChoice() const { return mIsInChoice; } void DialogueManager::goodbyeSelected() { MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); // Apply disposition change to NPC's base disposition if (mActor.getClass().isNpc()) { MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor); npcStats.setBaseDisposition(static_cast(npcStats.getBaseDisposition() + mPermanentDispositionChange)); } mPermanentDispositionChange = 0; mTemporaryDispositionChange = 0; } void DialogueManager::questionAnswered (int answer) { mChoice = answer; if (mDialogueMap.find(mLastTopic) != mDialogueMap.end()) { Filter filter (mActor, mChoice, mTalkedTo); if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic || mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting) { if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true)) { std::string text = info->mResponse; parseText (text); mChoice = -1; mIsInChoice = false; MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext)); // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group, // in which case it should not be added to the journal. for (ESM::Dialogue::InfoContainer::const_iterator iter = mDialogueMap[mLastTopic].mInfo.begin(); iter!=mDialogueMap[mLastTopic].mInfo.end(); ++iter) { if (iter->mId == info->mId) { MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor); break; } } executeScript (info->mResultScript); } else { mChoice = -1; mIsInChoice = false; MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); } } } updateTopics(); } void DialogueManager::askQuestion (const std::string& question, int choice) { mIsInChoice = true; MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->addChoice(question, choice); } void DialogueManager::goodbye() { mIsInChoice = true; MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->goodbye(); } void DialogueManager::persuade(int type) { bool success; float temp, perm; MWBase::Environment::get().getMechanicsManager()->getPersuasionDispositionChange( mActor, MWBase::MechanicsManager::PersuasionType(type), mTemporaryDispositionChange, success, temp, perm); mTemporaryDispositionChange += temp; mPermanentDispositionChange += perm; // change temp disposition so that final disposition is between 0...100 float curDisp = static_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor)); if (curDisp + mTemporaryDispositionChange < 0) mTemporaryDispositionChange = -curDisp; else if (curDisp + mTemporaryDispositionChange > 100) mTemporaryDispositionChange = 100 - curDisp; MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1); if (success) { int gold=0; if (type == MWBase::MechanicsManager::PT_Bribe10) gold = 10; else if (type == MWBase::MechanicsManager::PT_Bribe100) gold = 100; else if (type == MWBase::MechanicsManager::PT_Bribe1000) gold = 1000; if (gold) { player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, gold, player); mActor.getClass().getContainerStore(mActor).add(MWWorld::ContainerStore::sGoldId, gold, mActor); } } std::string text; if (type == MWBase::MechanicsManager::PT_Admire) text = "Admire"; else if (type == MWBase::MechanicsManager::PT_Taunt) text = "Taunt"; else if (type == MWBase::MechanicsManager::PT_Intimidate) text = "Intimidate"; else{ text = "Bribe"; } executeTopic (text + (success ? " Success" : " Fail")); } int DialogueManager::getTemporaryDispositionChange() const { return static_cast(mTemporaryDispositionChange); } void DialogueManager::applyDispositionChange(int delta) { mTemporaryDispositionChange += delta; } bool DialogueManager::checkServiceRefused() { Filter filter (mActor, mChoice, mTalkedTo); const MWWorld::Store &dialogues = MWBase::Environment::get().getWorld()->getStore().get(); const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal"); MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); std::vector infos = filter.list (dialogue, false, false, true); if (!infos.empty()) { const ESM::DialInfo* info = infos[0]; parseText (info->mResponse); const MWWorld::Store& gmsts = MWBase::Environment::get().getWorld()->getStore().get(); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), gmsts.find ("sServiceRefusal")->getString()); executeScript (info->mResultScript); return true; } return false; } void DialogueManager::say(const MWWorld::Ptr &actor, const std::string &topic) const { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(!sndMgr->sayDone(actor)) { // Actor is already saying something. return; } const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Dialogue *dial = store.get().find(topic); Filter filter(actor, 0, false); const ESM::DialInfo *info = filter.search(*dial, false); if(info != NULL) { MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); if(winMgr->getSubtitlesEnabled()) winMgr->messageBox(info->mResponse); if (!info->mSound.empty()) sndMgr->say(actor, info->mSound); } } int DialogueManager::countSavedGameRecords() const { return 1; // known topics } void DialogueManager::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { ESM::DialogueState state; for (std::set::const_iterator iter (mKnownTopics.begin()); iter!=mKnownTopics.end(); ++iter) { state.mKnownTopics.push_back (*iter); } state.mChangedFactionReaction = mChangedFactionReaction; writer.startRecord (ESM::REC_DIAS); state.save (writer); writer.endRecord (ESM::REC_DIAS); } void DialogueManager::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type==ESM::REC_DIAS) { const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); ESM::DialogueState state; state.load (reader); for (std::vector::const_iterator iter (state.mKnownTopics.begin()); iter!=state.mKnownTopics.end(); ++iter) if (store.get().search (*iter)) mKnownTopics.insert (*iter); mChangedFactionReaction = state.mChangedFactionReaction; } } void DialogueManager::modFactionReaction(const std::string &faction1, const std::string &faction2, int diff) { std::string fact1 = Misc::StringUtils::lowerCase(faction1); std::string fact2 = Misc::StringUtils::lowerCase(faction2); // Make sure the factions exist MWBase::Environment::get().getWorld()->getStore().get().find(fact1); MWBase::Environment::get().getWorld()->getStore().get().find(fact2); int newValue = getFactionReaction(faction1, faction2) + diff; std::map& map = mChangedFactionReaction[fact1]; map[fact2] = newValue; } void DialogueManager::setFactionReaction(const std::string &faction1, const std::string &faction2, int absolute) { std::string fact1 = Misc::StringUtils::lowerCase(faction1); std::string fact2 = Misc::StringUtils::lowerCase(faction2); // Make sure the factions exist MWBase::Environment::get().getWorld()->getStore().get().find(fact1); MWBase::Environment::get().getWorld()->getStore().get().find(fact2); std::map& map = mChangedFactionReaction[fact1]; map[fact2] = absolute; } int DialogueManager::getFactionReaction(const std::string &faction1, const std::string &faction2) const { std::string fact1 = Misc::StringUtils::lowerCase(faction1); std::string fact2 = Misc::StringUtils::lowerCase(faction2); ModFactionReactionMap::const_iterator map = mChangedFactionReaction.find(fact1); if (map != mChangedFactionReaction.end() && map->second.find(fact2) != map->second.end()) return map->second.at(fact2); const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get().find(fact1); std::map::const_iterator it = faction->mReactions.begin(); for (; it != faction->mReactions.end(); ++it) { if (Misc::StringUtils::ciEqual(it->first, fact2)) return it->second; } return 0; } void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const { if (actor == mActor && !mLastTopic.empty()) { MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse( mLastTopic, actor.getClass().getName(actor)); } } } openmw-openmw-0.38.0/apps/openmw/mwdialogue/dialoguemanagerimp.hpp000066400000000000000000000071621264522266000253750ustar00rootroot00000000000000#ifndef GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #define GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #include "../mwbase/dialoguemanager.hpp" #include #include #include #include #include "../mwworld/ptr.hpp" #include "../mwscript/compilercontext.hpp" namespace ESM { struct Dialogue; } namespace MWDialogue { class DialogueManager : public MWBase::DialogueManager { std::map mDialogueMap; std::set mKnownTopics;// Those are the topics the player knows. // Modified faction reactions. > typedef std::map > ModFactionReactionMap; ModFactionReactionMap mChangedFactionReaction; std::set mActorKnownTopics; Translation::Storage& mTranslationDataStorage; MWScript::CompilerContext mCompilerContext; std::ostream mErrorStream; Compiler::StreamErrorHandler mErrorHandler; MWWorld::Ptr mActor; bool mTalkedTo; int mChoice; std::string mLastTopic; // last topic ID, lowercase bool mIsInChoice; float mTemporaryDispositionChange; float mPermanentDispositionChange; void parseText (const std::string& text); void updateTopics(); void updateGlobals(); bool compile (const std::string& cmd,std::vector& code); void executeScript (const std::string& script); void executeTopic (const std::string& topic); public: DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage); virtual void clear(); virtual bool isInChoice() const; virtual void startDialogue (const MWWorld::Ptr& actor); virtual void addTopic (const std::string& topic); virtual void askQuestion (const std::string& question,int choice); virtual void goodbye(); virtual bool checkServiceRefused (); virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const; //calbacks for the GUI virtual void keywordSelected (const std::string& keyword); virtual void goodbyeSelected(); virtual void questionAnswered (int answer); virtual void persuade (int type); virtual int getTemporaryDispositionChange () const; /// @note This change is temporary and gets discarded when dialogue ends. virtual void applyDispositionChange (int delta); virtual int countSavedGameRecords() const; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; virtual void readRecord (ESM::ESMReader& reader, uint32_t type); /// Changes faction1's opinion of faction2 by \a diff. virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff); virtual void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute); /// @return faction1's opinion of faction2 virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const; /// Removes the last added topic response for the given actor from the journal virtual void clearInfoActor (const MWWorld::Ptr& actor) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/filter.cpp000066400000000000000000000542721264522266000230270ustar00rootroot00000000000000#include "filter.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/actorutil.hpp" #include "selectwrapper.hpp" bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const { bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); // actor id if (!info.mActor.empty()) { if ( !Misc::StringUtils::ciEqual(info.mActor, mActor.getCellRef().getRefId())) return false; } else if (isCreature) { // Creatures must not have topics aside of those specific to their id return false; } // NPC race if (!info.mRace.empty()) { if (isCreature) return true; MWWorld::LiveCellRef *cellRef = mActor.get(); if (!Misc::StringUtils::ciEqual(info.mRace, cellRef->mBase->mRace)) return false; } // NPC class if (!info.mClass.empty()) { if (isCreature) return true; MWWorld::LiveCellRef *cellRef = mActor.get(); if ( !Misc::StringUtils::ciEqual(info.mClass, cellRef->mBase->mClass)) return false; } // NPC faction if (!info.mFaction.empty()) { if (isCreature) return true; if (!Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), info.mFaction)) return false; // check rank if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank) return false; } else if (info.mData.mRank != -1) { if (isCreature) return true; // Rank requirement, but no faction given. Use the actor's faction, if there is one. // check rank if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank) return false; } // Gender if (!isCreature) { MWWorld::LiveCellRef* npc = mActor.get(); if (info.mData.mGender==(npc->mBase->mFlags & npc->mBase->Female ? 0 : 1)) return false; } return true; } bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const { const MWWorld::Ptr player = MWMechanics::getPlayer(); // check player faction if (!info.mPcFaction.empty()) { MWMechanics::NpcStats& stats = player.getClass().getNpcStats (player); std::map::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction)); if(iter==stats.getFactionRanks().end()) return false; // check rank if (iter->second < info.mData.mPCrank) return false; } // check cell if (!info.mCell.empty()) { // supports partial matches, just like getPcCell const std::string& playerCell = MWBase::Environment::get().getWorld()->getCellName(player.getCell()); bool match = playerCell.length()>=info.mCell.length() && Misc::StringUtils::ciEqual(playerCell.substr (0, info.mCell.length()), info.mCell); if (!match) return false; } return true; } bool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const { for (std::vector::const_iterator iter (info.mSelects.begin()); iter != info.mSelects.end(); ++iter) if (!testSelectStruct (*iter)) return false; return true; } bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert) const { bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); if (isCreature) return true; int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(); // For service refusal, the disposition check is inverted. However, a value of 0 still means "always succeed". return invert ? (info.mData.mDisposition == 0 || actorDisposition < info.mData.mDisposition) : (actorDisposition >= info.mData.mDisposition); } bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const { if (select.isNpcOnly() && (mActor.getTypeName() != typeid (ESM::NPC).name())) // If the actor is a creature, we pass all conditions only applicable to NPCs. return true; if (select.getFunction() == SelectWrapper::Function_Choice && mChoice == -1) // If not currently in a choice, we reject all conditions that test against choices. return false; switch (select.getType()) { case SelectWrapper::Type_None: return true; case SelectWrapper::Type_Integer: return select.selectCompare (getSelectStructInteger (select)); case SelectWrapper::Type_Numeric: return testSelectStructNumeric (select); case SelectWrapper::Type_Boolean: return select.selectCompare (getSelectStructBoolean (select)); // We must not do the comparison for inverted functions (eg. Function_NotClass) case SelectWrapper::Type_Inverted: return getSelectStructBoolean (select); } return true; } bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) const { switch (select.getFunction()) { case SelectWrapper::Function_Global: // internally all globals are float :( return select.selectCompare ( MWBase::Environment::get().getWorld()->getGlobalFloat (select.getName())); case SelectWrapper::Function_Local: { std::string scriptName = mActor.getClass().getScript (mActor); if (scriptName.empty()) return false; // no script std::string name = Misc::StringUtils::lowerCase (select.getName()); const Compiler::Locals& localDefs = MWBase::Environment::get().getScriptManager()->getLocals (scriptName); char type = localDefs.getType (name); if (type==' ') return false; // script does not have a variable of this name. int index = localDefs.getIndex (name); if (index < 0) return false; // shouldn't happen, we checked that variable has a type above, so must exist const MWScript::Locals& locals = mActor.getRefData().getLocals(); switch (type) { case 's': return select.selectCompare (static_cast (locals.mShorts[index])); case 'l': return select.selectCompare (locals.mLongs[index]); case 'f': return select.selectCompare (locals.mFloats[index]); } throw std::logic_error ("unknown local variable type in dialogue filter"); } case SelectWrapper::Function_PcHealthPercent: { MWWorld::Ptr player = MWMechanics::getPlayer(); float ratio = player.getClass().getCreatureStats (player).getHealth().getCurrent() / player.getClass().getCreatureStats (player).getHealth().getModified(); return select.selectCompare (static_cast(ratio*100)); } case SelectWrapper::Function_PcDynamicStat: { MWWorld::Ptr player = MWMechanics::getPlayer(); float value = player.getClass().getCreatureStats (player). getDynamic (select.getArgument()).getCurrent(); return select.selectCompare (value); } case SelectWrapper::Function_HealthPercent: { float ratio = mActor.getClass().getCreatureStats (mActor).getHealth().getCurrent() / mActor.getClass().getCreatureStats (mActor).getHealth().getModified(); return select.selectCompare (static_cast(ratio*100)); } default: throw std::runtime_error ("unknown numeric select function"); } } int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) const { MWWorld::Ptr player = MWMechanics::getPlayer(); switch (select.getFunction()) { case SelectWrapper::Function_Journal: return MWBase::Environment::get().getJournal()->getJournalIndex (select.getName()); case SelectWrapper::Function_Item: { MWWorld::ContainerStore& store = player.getClass().getContainerStore (player); return store.count(select.getName()); } case SelectWrapper::Function_Dead: return MWBase::Environment::get().getMechanicsManager()->countDeaths (select.getName()); case SelectWrapper::Function_Choice: return mChoice; case SelectWrapper::Function_AiSetting: return mActor.getClass().getCreatureStats (mActor).getAiSetting ( (MWMechanics::CreatureStats::AiSetting)select.getArgument()).getModified(); case SelectWrapper::Function_PcAttribute: return player.getClass().getCreatureStats (player). getAttribute (select.getArgument()).getModified(); case SelectWrapper::Function_PcSkill: return static_cast (player.getClass(). getNpcStats (player).getSkill (select.getArgument()).getModified()); case SelectWrapper::Function_FriendlyHit: { int hits = mActor.getClass().getCreatureStats (mActor).getFriendlyHits(); return hits>4 ? 4 : hits; } case SelectWrapper::Function_PcLevel: return player.getClass().getCreatureStats (player).getLevel(); case SelectWrapper::Function_PcGender: return player.get()->mBase->isMale() ? 0 : 1; case SelectWrapper::Function_PcClothingModifier: { MWWorld::InventoryStore& store = player.getClass().getInventoryStore (player); int value = 0; for (int i=0; i<=15; ++i) // everything except things held in hands and ammunition { MWWorld::ContainerStoreIterator slot = store.getSlot (i); if (slot!=store.end()) value += slot->getClass().getValue (*slot); } return value; } case SelectWrapper::Function_PcCrimeLevel: return player.getClass().getNpcStats (player).getBounty(); case SelectWrapper::Function_RankRequirement: { std::string faction = mActor.getClass().getPrimaryFaction(mActor); if (faction.empty()) return 0; int rank = getFactionRank (player, faction); if (rank>=9) return 0; // max rank int result = 0; if (hasFactionRankSkillRequirements (player, faction, rank+1)) result += 1; if (hasFactionRankReputationRequirements (player, faction, rank+1)) result += 2; return result; } case SelectWrapper::Function_Level: return mActor.getClass().getCreatureStats (mActor).getLevel(); case SelectWrapper::Function_PCReputation: return player.getClass().getNpcStats (player).getReputation(); case SelectWrapper::Function_Weather: return MWBase::Environment::get().getWorld()->getCurrentWeather(); case SelectWrapper::Function_Reputation: return mActor.getClass().getNpcStats (mActor).getReputation(); case SelectWrapper::Function_FactionRankDiff: { std::string faction = mActor.getClass().getPrimaryFaction(mActor); if (faction.empty()) return 0; int rank = getFactionRank (player, faction); int npcRank = mActor.getClass().getPrimaryFactionRank(mActor); return rank-npcRank; } case SelectWrapper::Function_WerewolfKills: return player.getClass().getNpcStats (player).getWerewolfKills(); case SelectWrapper::Function_RankLow: case SelectWrapper::Function_RankHigh: { bool low = select.getFunction()==SelectWrapper::Function_RankLow; std::string factionId = mActor.getClass().getPrimaryFaction(mActor); if (factionId.empty()) return 0; int value = 0; MWMechanics::NpcStats& playerStats = player.getClass().getNpcStats (player); std::map::const_iterator playerFactionIt = playerStats.getFactionRanks().begin(); for (; playerFactionIt != playerStats.getFactionRanks().end(); ++playerFactionIt) { int reaction = MWBase::Environment::get().getDialogueManager()->getFactionReaction(factionId, playerFactionIt->first); if (low ? reaction < value : reaction > value) value = reaction; } return value; } case SelectWrapper::Function_CreatureTargetted: { MWWorld::Ptr target; mActor.getClass().getCreatureStats(mActor).getAiSequence().getCombatTarget(target); if (target) { if (target.getClass().isNpc() && target.getClass().getNpcStats(target).isWerewolf()) return 2; if (target.getTypeName() == typeid(ESM::Creature).name()) return 1; } } return 0; default: throw std::runtime_error ("unknown integer select function"); } } bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) const { MWWorld::Ptr player = MWMechanics::getPlayer(); switch (select.getFunction()) { case SelectWrapper::Function_False: return false; case SelectWrapper::Function_NotId: return !Misc::StringUtils::ciEqual(mActor.getCellRef().getRefId(), select.getName()); case SelectWrapper::Function_NotFaction: return !Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), select.getName()); case SelectWrapper::Function_NotClass: return !Misc::StringUtils::ciEqual(mActor.get()->mBase->mClass, select.getName()); case SelectWrapper::Function_NotRace: return !Misc::StringUtils::ciEqual(mActor.get()->mBase->mRace, select.getName()); case SelectWrapper::Function_NotCell: return !Misc::StringUtils::ciEqual(MWBase::Environment::get().getWorld()->getCellName(mActor.getCell()) , select.getName()); case SelectWrapper::Function_NotLocal: { std::string scriptName = mActor.getClass().getScript (mActor); if (scriptName.empty()) // This actor has no attached script, so there is no local variable return true; const Compiler::Locals& localDefs = MWBase::Environment::get().getScriptManager()->getLocals (scriptName); return localDefs.getIndex (Misc::StringUtils::lowerCase (select.getName()))==-1; } case SelectWrapper::Function_SameGender: return (player.get()->mBase->mFlags & ESM::NPC::Female)== (mActor.get()->mBase->mFlags & ESM::NPC::Female); case SelectWrapper::Function_SameRace: return Misc::StringUtils::ciEqual(mActor.get()->mBase->mRace, player.get()->mBase->mRace); case SelectWrapper::Function_SameFaction: return player.getClass().getNpcStats (player).isInFaction(mActor.getClass().getPrimaryFaction(mActor)); case SelectWrapper::Function_PcCommonDisease: return player.getClass().getCreatureStats (player).hasCommonDisease(); case SelectWrapper::Function_PcBlightDisease: return player.getClass().getCreatureStats (player).hasBlightDisease(); case SelectWrapper::Function_PcCorprus: return player.getClass().getCreatureStats (player). getMagicEffects().get (ESM::MagicEffect::Corprus).getMagnitude()!=0; case SelectWrapper::Function_PcExpelled: { std::string faction = mActor.getClass().getPrimaryFaction(mActor); if (faction.empty()) return false; return player.getClass().getNpcStats(player).getExpelled(faction); } case SelectWrapper::Function_PcVampire: return player.getClass().getCreatureStats(player).getMagicEffects(). get(ESM::MagicEffect::Vampirism).getMagnitude() > 0; case SelectWrapper::Function_TalkedToPc: return mTalkedToPlayer; case SelectWrapper::Function_Alarmed: return mActor.getClass().getCreatureStats (mActor).isAlarmed(); case SelectWrapper::Function_Detected: return MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, mActor); case SelectWrapper::Function_Attacked: return mActor.getClass().getCreatureStats (mActor).getAttacked(); case SelectWrapper::Function_ShouldAttack: return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor, MWMechanics::getPlayer()); case SelectWrapper::Function_Werewolf: return mActor.getClass().getNpcStats (mActor).isWerewolf(); default: throw std::runtime_error ("unknown boolean select function"); } } int MWDialogue::Filter::getFactionRank (const MWWorld::Ptr& actor, const std::string& factionId) const { MWMechanics::NpcStats& stats = actor.getClass().getNpcStats (actor); std::map::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase(factionId)); if (iter==stats.getFactionRanks().end()) return -1; return iter->second; } bool MWDialogue::Filter::hasFactionRankSkillRequirements (const MWWorld::Ptr& actor, const std::string& factionId, int rank) const { if (rank<0 || rank>=10) throw std::runtime_error ("rank index out of range"); if (!actor.getClass().getNpcStats (actor).hasSkillsForRank (factionId, rank)) return false; const ESM::Faction& faction = *MWBase::Environment::get().getWorld()->getStore().get().find (factionId); MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats (actor); return stats.getAttribute (faction.mData.mAttribute[0]).getBase()>=faction.mData.mRankData[rank].mAttribute1 && stats.getAttribute (faction.mData.mAttribute[1]).getBase()>=faction.mData.mRankData[rank].mAttribute2; } bool MWDialogue::Filter::hasFactionRankReputationRequirements (const MWWorld::Ptr& actor, const std::string& factionId, int rank) const { if (rank<0 || rank>=10) throw std::runtime_error ("rank index out of range"); MWMechanics::NpcStats& stats = actor.getClass().getNpcStats (actor); const ESM::Faction& faction = *MWBase::Environment::get().getWorld()->getStore().get().find (factionId); return stats.getFactionReputation (factionId)>=faction.mData.mRankData[rank].mFactReaction; } MWDialogue::Filter::Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer) : mActor (actor), mChoice (choice), mTalkedToPlayer (talkedToPlayer) {} const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const { std::vector suitableInfos = list (dialogue, fallbackToInfoRefusal, false); if (suitableInfos.empty()) return NULL; else return suitableInfos[0]; } std::vector MWDialogue::Filter::listAll (const ESM::Dialogue& dialogue) const { std::vector infos; for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) { if (testActor (*iter)) infos.push_back(&*iter); } return infos; } std::vector MWDialogue::Filter::list (const ESM::Dialogue& dialogue, bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition) const { std::vector infos; bool infoRefusal = false; // Iterate over topic responses to find a matching one for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) { if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) { if (testDisposition (*iter, invertDisposition)) { infos.push_back(&*iter); if (!searchAll) break; } else infoRefusal = true; } } if (infos.empty() && infoRefusal && fallbackToInfoRefusal) { // No response is valid because of low NPC disposition, // search a response in the topic "Info Refusal" const MWWorld::Store &dialogues = MWBase::Environment::get().getWorld()->getStore().get(); const ESM::Dialogue& infoRefusalDialogue = *dialogues.find ("Info Refusal"); for (ESM::Dialogue::InfoContainer::const_iterator iter = infoRefusalDialogue.mInfo.begin(); iter!=infoRefusalDialogue.mInfo.end(); ++iter) if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter, invertDisposition)) { infos.push_back(&*iter); if (!searchAll) break; } } return infos; } bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const { for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) { if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) return true; } return false; } openmw-openmw-0.38.0/apps/openmw/mwdialogue/filter.hpp000066400000000000000000000054421264522266000230270ustar00rootroot00000000000000#ifndef GAME_MWDIALOGUE_FILTER_H #define GAME_MWDIALOGUE_FILTER_H #include #include "../mwworld/ptr.hpp" namespace ESM { struct DialInfo; struct Dialogue; } namespace MWDialogue { class SelectWrapper; class Filter { MWWorld::Ptr mActor; int mChoice; bool mTalkedToPlayer; bool testActor (const ESM::DialInfo& info) const; ///< Is this the right actor for this \a info? bool testPlayer (const ESM::DialInfo& info) const; ///< Do the player and the cell the player is currently in match \a info? bool testSelectStructs (const ESM::DialInfo& info) const; ///< Are all select structs matching? bool testDisposition (const ESM::DialInfo& info, bool invert=false) const; ///< Is the actor disposition toward the player high enough (or low enough, if \a invert is true)? bool testSelectStruct (const SelectWrapper& select) const; bool testSelectStructNumeric (const SelectWrapper& select) const; int getSelectStructInteger (const SelectWrapper& select) const; bool getSelectStructBoolean (const SelectWrapper& select) const; int getFactionRank (const MWWorld::Ptr& actor, const std::string& factionId) const; bool hasFactionRankSkillRequirements (const MWWorld::Ptr& actor, const std::string& factionId, int rank) const; bool hasFactionRankReputationRequirements (const MWWorld::Ptr& actor, const std::string& factionId, int rank) const; public: Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); std::vector list (const ESM::Dialogue& dialogue, bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition=false) const; ///< List all infos that could be used on the given actor, using the current runtime state of the actor. /// \note If fallbackToInfoRefusal is used, the returned DialInfo might not be from the supplied ESM::Dialogue. std::vector listAll (const ESM::Dialogue& dialogue) const; ///< List all infos that could possibly be used on the given actor, ignoring runtime state filters and ignoring player filters. const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; ///< Get a matching response for the requested dialogue. /// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition. bool responseAvailable (const ESM::Dialogue& dialogue) const; ///< Does a matching response exist? (disposition is ignored for this check) }; } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/hypertextparser.cpp000066400000000000000000000063451264522266000250110ustar00rootroot00000000000000#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/store.hpp" #include "../mwworld/esmstore.hpp" #include "keywordsearch.hpp" #include "hypertextparser.hpp" namespace MWDialogue { namespace HyperTextParser { std::vector parseHyperText(const std::string & text) { std::vector result; size_t pos_end, iteration_pos = 0; for(;;) { size_t pos_begin = text.find('@', iteration_pos); if (pos_begin != std::string::npos) pos_end = text.find('#', pos_begin); if (pos_begin != std::string::npos && pos_end != std::string::npos) { if (pos_begin != iteration_pos) tokenizeKeywords(text.substr(iteration_pos, pos_begin - iteration_pos), result); std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1); result.push_back(Token(link, Token::ExplicitLink)); iteration_pos = pos_end + 1; } else { if (iteration_pos != text.size()) tokenizeKeywords(text.substr(iteration_pos), result); break; } } return result; } void tokenizeKeywords(const std::string & text, std::vector & tokens) { const MWWorld::Store & dialogs = MWBase::Environment::get().getWorld()->getStore().get(); std::list keywordList; for (MWWorld::Store::iterator it = dialogs.begin(); it != dialogs.end(); ++it) keywordList.push_back(Misc::StringUtils::lowerCase(it->mId)); keywordList.sort(Misc::StringUtils::ciLess); KeywordSearch keywordSearch; for (std::list::const_iterator it = keywordList.begin(); it != keywordList.end(); ++it) keywordSearch.seed(*it, 0 /*unused*/); std::vector::Match> matches; keywordSearch.highlightKeywords(text.begin(), text.end(), matches); for (std::vector::Match>::const_iterator it = matches.begin(); it != matches.end(); ++it) { tokens.push_back(Token(std::string(it->mBeg, it->mEnd), Token::ImplicitKeyword)); } } size_t removePseudoAsterisks(std::string & phrase) { size_t pseudoAsterisksCount = 0; if( !phrase.empty() ) { std::string::reverse_iterator rit = phrase.rbegin(); const char specialPseudoAsteriskCharacter = 127; while( rit != phrase.rend() && *rit == specialPseudoAsteriskCharacter ) { pseudoAsterisksCount++; ++rit; } } phrase = phrase.substr(0, phrase.length() - pseudoAsterisksCount); return pseudoAsterisksCount; } } } openmw-openmw-0.38.0/apps/openmw/mwdialogue/hypertextparser.hpp000066400000000000000000000017451264522266000250150ustar00rootroot00000000000000#ifndef GAME_MWDIALOGUE_HYPERTEXTPARSER_H #define GAME_MWDIALOGUE_HYPERTEXTPARSER_H #include #include namespace MWDialogue { namespace HyperTextParser { struct Token { enum Type { ExplicitLink, // enclosed in @# ImplicitKeyword }; Token(const std::string & text, Type type) : mText(text), mType(type) {} bool isExplicitLink() { return mType == ExplicitLink; } bool isImplicitKeyword() { return mType == ImplicitKeyword; } std::string mText; Type mType; }; // In translations (at least Russian) the links are marked with @#, so // it should be a function to parse it std::vector parseHyperText(const std::string & text); void tokenizeKeywords(const std::string & text, std::vector & tokens); size_t removePseudoAsterisks(std::string & phrase); } } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/journalentry.cpp000066400000000000000000000102611264522266000242640ustar00rootroot00000000000000#include "journalentry.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" #include "../mwscript/interpretercontext.hpp" namespace MWDialogue { Entry::Entry() {} Entry::Entry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor) : mInfoId (infoId) { const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (topic); for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mId == mInfoId) { if (actor.isEmpty()) { MWScript::InterpreterContext interpreterContext(NULL,MWWorld::Ptr()); mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext); } else { MWScript::InterpreterContext interpreterContext(&actor.getRefData().getLocals(),actor); mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext); } return; } throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + topic); } Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText), mActorName(record.mActorName) {} std::string Entry::getText() const { return mText; } void Entry::write (ESM::JournalEntry& entry) const { entry.mInfo = mInfoId; entry.mText = mText; entry.mActorName = mActorName; } JournalEntry::JournalEntry() {} JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor) : Entry (topic, infoId, actor), mTopic (topic) {} JournalEntry::JournalEntry (const ESM::JournalEntry& record) : Entry (record), mTopic (record.mTopic) {} void JournalEntry::write (ESM::JournalEntry& entry) const { Entry::write (entry); entry.mTopic = mTopic; } JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) { return JournalEntry (topic, idFromIndex (topic, index), MWWorld::Ptr()); } std::string JournalEntry::idFromIndex (const std::string& topic, int index) { const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (topic); for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mData.mJournalIndex==index) { return iter->mId; } throw std::runtime_error ("unknown journal index for topic " + topic); } StampedJournalEntry::StampedJournalEntry() : mDay (0), mMonth (0), mDayOfMonth (0) {} StampedJournalEntry::StampedJournalEntry (const std::string& topic, const std::string& infoId, int day, int month, int dayOfMonth) : JournalEntry (topic, infoId, MWWorld::Ptr()), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth) {} StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record) : JournalEntry (record), mDay (record.mDay), mMonth (record.mMonth), mDayOfMonth (record.mDayOfMonth) {} void StampedJournalEntry::write (ESM::JournalEntry& entry) const { JournalEntry::write (entry); entry.mDay = mDay; entry.mMonth = mMonth; entry.mDayOfMonth = mDayOfMonth; } StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index) { int day = MWBase::Environment::get().getWorld()->getGlobalInt ("dayspassed"); int month = MWBase::Environment::get().getWorld()->getGlobalInt ("month"); int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalInt ("day"); return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth); } } openmw-openmw-0.38.0/apps/openmw/mwdialogue/journalentry.hpp000066400000000000000000000033751264522266000243010ustar00rootroot00000000000000#ifndef GAME_MWDIALOGUE_JOURNALENTRY_H #define GAME_MWDIALOGUE_JOURNALENTRY_H #include namespace ESM { struct JournalEntry; } namespace MWWorld { class Ptr; } namespace MWDialogue { /// \brief Basic quest/dialogue/topic entry struct Entry { std::string mInfoId; std::string mText; std::string mActorName; // optional Entry(); /// actor is optional Entry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor); Entry (const ESM::JournalEntry& record); std::string getText() const; void write (ESM::JournalEntry& entry) const; }; /// \brief A dialogue entry /// /// Same as entry, but store TopicID struct JournalEntry : public Entry { std::string mTopic; JournalEntry(); JournalEntry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor); JournalEntry (const ESM::JournalEntry& record); void write (ESM::JournalEntry& entry) const; static JournalEntry makeFromQuest (const std::string& topic, int index); static std::string idFromIndex (const std::string& topic, int index); }; /// \brief A quest entry with a timestamp. struct StampedJournalEntry : public JournalEntry { int mDay; int mMonth; int mDayOfMonth; StampedJournalEntry(); StampedJournalEntry (const std::string& topic, const std::string& infoId, int day, int month, int dayOfMonth); StampedJournalEntry (const ESM::JournalEntry& record); void write (ESM::JournalEntry& entry) const; static StampedJournalEntry makeFromQuest (const std::string& topic, int index); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/journalimp.cpp000066400000000000000000000173601264522266000237170ustar00rootroot00000000000000#include "journalimp.hpp" #include #include #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwgui/messagebox.hpp" namespace MWDialogue { Quest& Journal::getQuest (const std::string& id) { TQuestContainer::iterator iter = mQuests.find (id); if (iter==mQuests.end()) { std::pair result = mQuests.insert (std::make_pair (id, Quest (id))); iter = result.first; } return iter->second; } Topic& Journal::getTopic (const std::string& id) { TTopicContainer::iterator iter = mTopics.find (id); if (iter==mTopics.end()) { std::pair result = mTopics.insert (std::make_pair (id, Topic (id))); iter = result.first; } return iter->second; } bool Journal::isThere (const std::string& topicId, const std::string& infoId) const { if (const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().search (topicId)) { if (infoId.empty()) return true; for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mId == infoId) return true; } return false; } Journal::Journal() {} void Journal::clear() { mJournal.clear(); mQuests.clear(); mTopics.clear(); } void Journal::addEntry (const std::string& id, int index) { // bail out of we already have heard this... std::string infoId = JournalEntry::idFromIndex (id, index); for (TEntryIter i = mJournal.begin (); i != mJournal.end (); ++i) if (i->mTopic == id && i->mInfoId == infoId) return; StampedJournalEntry entry = StampedJournalEntry::makeFromQuest (id, index); mJournal.push_back (entry); Quest& quest = getQuest (id); quest.addEntry (entry); // we are doing slicing on purpose here MWBase::Environment::get().getWindowManager()->messageBox ("#{sJournalEntry}"); } void Journal::setJournalIndex (const std::string& id, int index) { Quest& quest = getQuest (id); quest.setIndex (index); } void Journal::addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor) { Topic& topic = getTopic (topicId); JournalEntry entry(topicId, infoId, actor); entry.mActorName = actor.getClass().getName(actor); topic.addEntry (entry); } void Journal::removeLastAddedTopicResponse(const std::string &topicId, const std::string &actorName) { Topic& topic = getTopic (topicId); topic.removeLastAddedResponse(actorName); if (topic.begin() == topic.end()) mTopics.erase(mTopics.find(topicId)); // All responses removed -> remove topic } int Journal::getJournalIndex (const std::string& id) const { TQuestContainer::const_iterator iter = mQuests.find (id); if (iter==mQuests.end()) return 0; return iter->second.getIndex(); } Journal::TEntryIter Journal::begin() const { return mJournal.begin(); } Journal::TEntryIter Journal::end() const { return mJournal.end(); } Journal::TQuestIter Journal::questBegin() const { return mQuests.begin(); } Journal::TQuestIter Journal::questEnd() const { return mQuests.end(); } Journal::TTopicIter Journal::topicBegin() const { return mTopics.begin(); } Journal::TTopicIter Journal::topicEnd() const { return mTopics.end(); } int Journal::countSavedGameRecords() const { int count = static_cast (mQuests.size()); for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter) count += std::distance (iter->second.begin(), iter->second.end()); count += std::distance (mJournal.begin(), mJournal.end()); for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter) count += std::distance (iter->second.begin(), iter->second.end()); return count; } void Journal::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter) { const Quest& quest = iter->second; ESM::QuestState state; quest.write (state); writer.startRecord (ESM::REC_QUES); state.save (writer); writer.endRecord (ESM::REC_QUES); for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter) { ESM::JournalEntry entry; entry.mType = ESM::JournalEntry::Type_Quest; entry.mTopic = quest.getTopic(); iter->write (entry); writer.startRecord (ESM::REC_JOUR); entry.save (writer); writer.endRecord (ESM::REC_JOUR); } } for (TEntryIter iter (mJournal.begin()); iter!=mJournal.end(); ++iter) { ESM::JournalEntry entry; entry.mType = ESM::JournalEntry::Type_Journal; iter->write (entry); writer.startRecord (ESM::REC_JOUR); entry.save (writer); writer.endRecord (ESM::REC_JOUR); } for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter) { const Topic& topic = iter->second; for (Topic::TEntryIter iter (topic.begin()); iter!=topic.end(); ++iter) { ESM::JournalEntry entry; entry.mType = ESM::JournalEntry::Type_Topic; entry.mTopic = topic.getTopic(); iter->write (entry); writer.startRecord (ESM::REC_JOUR); entry.save (writer); writer.endRecord (ESM::REC_JOUR); } } } void Journal::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type==ESM::REC_JOUR || type==ESM::REC_JOUR_LEGACY) { ESM::JournalEntry record; record.load (reader); if (isThere (record.mTopic, record.mInfo)) switch (record.mType) { case ESM::JournalEntry::Type_Quest: getQuest (record.mTopic).insertEntry (record); break; case ESM::JournalEntry::Type_Journal: mJournal.push_back (record); break; case ESM::JournalEntry::Type_Topic: getTopic (record.mTopic).insertEntry (record); break; } } else if (type==ESM::REC_QUES) { ESM::QuestState record; record.load (reader); if (isThere (record.mTopic)) { std::pair result = mQuests.insert (std::make_pair (record.mTopic, record)); // reapply quest index, this is to handle users upgrading from only // Morrowind.esm (no quest states) to Morrowind.esm + Tribunal.esm result.first->second.setIndex(record.mState); } } } } openmw-openmw-0.38.0/apps/openmw/mwdialogue/journalimp.hpp000066400000000000000000000050361264522266000237210ustar00rootroot00000000000000#ifndef GAME_MWDIALOG_JOURNAL_H #define GAME_MWDIALOG_JOURNAL_H #include "../mwbase/journal.hpp" #include "journalentry.hpp" #include "quest.hpp" namespace MWDialogue { /// \brief The player's journal class Journal : public MWBase::Journal { TEntryContainer mJournal; TQuestContainer mQuests; TTopicContainer mTopics; private: Quest& getQuest (const std::string& id); Topic& getTopic (const std::string& id); bool isThere (const std::string& topicId, const std::string& infoId = "") const; public: Journal(); virtual void clear(); virtual void addEntry (const std::string& id, int index); ///< Add a journal entry. virtual void setJournalIndex (const std::string& id, int index); ///< Set the journal index without adding an entry. virtual int getJournalIndex (const std::string& id) const; ///< Get the journal index. virtual void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor); /// \note topicId must be lowercase virtual void removeLastAddedTopicResponse (const std::string& topicId, const std::string& actorName); ///< Removes the last topic response added for the given topicId and actor name. /// \note topicId must be lowercase virtual TEntryIter begin() const; ///< Iterator pointing to the begin of the main journal. /// /// \note Iterators to main journal entries will never become invalid. virtual TEntryIter end() const; ///< Iterator pointing past the end of the main journal. virtual TQuestIter questBegin() const; ///< Iterator pointing to the first quest (sorted by topic ID) virtual TQuestIter questEnd() const; ///< Iterator pointing past the last quest. virtual TTopicIter topicBegin() const; ///< Iterator pointing to the first topic (sorted by topic ID) /// /// \note The topic ID is identical with the user-visible topic string. virtual TTopicIter topicEnd() const; ///< Iterator pointing past the last topic. virtual int countSavedGameRecords() const; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; virtual void readRecord (ESM::ESMReader& reader, uint32_t type); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/keywordsearch.cpp000066400000000000000000000000001264522266000243700ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwdialogue/keywordsearch.hpp000066400000000000000000000201741264522266000244130ustar00rootroot00000000000000#ifndef GAME_MWDIALOGUE_KEYWORDSEARCH_H #define GAME_MWDIALOGUE_KEYWORDSEARCH_H #include #include #include #include #include // std::reverse #include namespace MWDialogue { template class KeywordSearch { public: typedef typename string_t::const_iterator Point; struct Match { Point mBeg; Point mEnd; value_t mValue; }; void seed (string_t keyword, value_t value) { if (keyword.empty()) return; seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), 0, mRoot); } void clear () { mRoot.mChildren.clear (); mRoot.mKeyword.clear (); } bool containsKeyword (string_t keyword, value_t& value) { typename Entry::childen_t::iterator current; typename Entry::childen_t::iterator next; current = mRoot.mChildren.find (Misc::StringUtils::toLower (*keyword.begin())); if (current == mRoot.mChildren.end()) return false; else if (current->second.mKeyword.size() && Misc::StringUtils::ciEqual(current->second.mKeyword, keyword)) { value = current->second.mValue; return true; } for (Point i = ++keyword.begin(); i != keyword.end(); ++i) { next = current->second.mChildren.find(Misc::StringUtils::toLower (*i)); if (next == current->second.mChildren.end()) return false; if (Misc::StringUtils::ciEqual(next->second.mKeyword, keyword)) { value = next->second.mValue; return true; } current = next; } return false; } static bool sortMatches(const Match& left, const Match& right) { return left.mBeg < right.mBeg; } void highlightKeywords (Point beg, Point end, std::vector& out) { std::vector matches; for (Point i = beg; i != end; ++i) { // check if previous character marked start of new word if (i != beg) { Point prev = i; --prev; if(isalpha(*prev)) continue; } // check first character typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (Misc::StringUtils::toLower (*i)); // no match, on to next character if (candidate == mRoot.mChildren.end ()) continue; // see how far the match goes Point j = i; // some keywords might be longer variations of other keywords, so we definitely need a list of candidates // the first element in the pair is length of the match, i.e. depth from the first character on std::vector< typename std::pair > candidates; while ((j + 1) != end) { typename Entry::childen_t::iterator next = candidate->second.mChildren.find (Misc::StringUtils::toLower (*++j)); if (next == candidate->second.mChildren.end ()) { if (candidate->second.mKeyword.size() > 0) candidates.push_back(std::make_pair((j-i), candidate)); break; } candidate = next; if (candidate->second.mKeyword.size() > 0) candidates.push_back(std::make_pair((j-i), candidate)); } if (candidates.empty()) continue; // didn't match enough to disambiguate, on to next character // shorter candidates will be added to the vector first. however, we want to check against longer candidates first std::reverse(candidates.begin(), candidates.end()); for (typename std::vector< std::pair >::iterator it = candidates.begin(); it != candidates.end(); ++it) { candidate = it->second; // try to match the rest of the keyword Point k = i + it->first; typename string_t::const_iterator t = candidate->second.mKeyword.begin () + (k - i); while (k != end && t != candidate->second.mKeyword.end ()) { if (Misc::StringUtils::toLower (*k) != Misc::StringUtils::toLower (*t)) break; ++k, ++t; } // didn't match full keyword, try the next candidate if (t != candidate->second.mKeyword.end ()) continue; // found a keyword, but there might still be longer keywords that start somewhere _within_ this keyword // we will resolve these overlapping keywords later, choosing the longest one in case of conflict Match match; match.mValue = candidate->second.mValue; match.mBeg = i; match.mEnd = k; matches.push_back(match); break; } } // resolve overlapping keywords while (matches.size()) { int longestKeywordSize = 0; typename std::vector::iterator longestKeyword = matches.begin(); for (typename std::vector::iterator it = matches.begin(); it != matches.end(); ++it) { int size = it->mEnd - it->mBeg; if (size > longestKeywordSize) { longestKeywordSize = size; longestKeyword = it; } typename std::vector::iterator next = it; ++next; if (next == matches.end()) break; if (it->mEnd <= next->mBeg) { break; // no overlap } } Match keyword = *longestKeyword; matches.erase(longestKeyword); out.push_back(keyword); // erase anything that overlaps with the keyword we just added to the output for (typename std::vector::iterator it = matches.begin(); it != matches.end();) { if (it->mBeg < keyword.mEnd && it->mEnd > keyword.mBeg) it = matches.erase(it); else ++it; } } std::sort(out.begin(), out.end(), sortMatches); } private: struct Entry { typedef std::map childen_t; string_t mKeyword; value_t mValue; childen_t mChildren; }; void seed_impl (string_t keyword, value_t value, size_t depth, Entry & entry) { int ch = Misc::StringUtils::toLower (keyword.at (depth)); typename Entry::childen_t::iterator j = entry.mChildren.find (ch); if (j == entry.mChildren.end ()) { entry.mChildren [ch].mValue = /*std::move*/ (value); entry.mChildren [ch].mKeyword = /*std::move*/ (keyword); } else { if (j->second.mKeyword.size () > 0) { if (keyword == j->second.mKeyword) throw std::runtime_error ("duplicate keyword inserted"); value_t pushValue = /*std::move*/ (j->second.mValue); string_t pushKeyword = /*std::move*/ (j->second.mKeyword); if (depth >= pushKeyword.size ()) throw std::runtime_error ("unexpected"); if (depth+1 < pushKeyword.size()) { seed_impl (/*std::move*/ (pushKeyword), /*std::move*/ (pushValue), depth+1, j->second); j->second.mKeyword.clear (); } } if (depth+1 == keyword.size()) j->second.mKeyword = value; else // depth+1 < keyword.size() seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), depth+1, j->second); } } Entry mRoot; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/quest.cpp000066400000000000000000000055671264522266000227060ustar00rootroot00000000000000#include "quest.hpp" #include #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" namespace MWDialogue { Quest::Quest() : Topic(), mIndex (0), mFinished (false) {} Quest::Quest (const std::string& topic) : Topic (topic), mIndex (0), mFinished (false) {} Quest::Quest (const ESM::QuestState& state) : Topic (state.mTopic), mIndex (state.mState), mFinished (state.mFinished!=0) {} std::string Quest::getName() const { const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mQuestStatus==ESM::DialInfo::QS_Name) return iter->mResponse; return ""; } int Quest::getIndex() const { return mIndex; } void Quest::setIndex (int index) { const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mData.mDisposition==index && iter->mQuestStatus!=ESM::DialInfo::QS_Name) { if (iter->mQuestStatus==ESM::DialInfo::QS_Finished) mFinished = true; else if (iter->mQuestStatus==ESM::DialInfo::QS_Restart) mFinished = false; } // The index must be set even if no related journal entry was found mIndex = index; } bool Quest::isFinished() const { return mFinished; } void Quest::addEntry (const JournalEntry& entry) { int index = -1; const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (entry.mTopic); for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mId == entry.mInfoId) { index = iter->mData.mJournalIndex; break; } if (index==-1) throw std::runtime_error ("unknown journal entry for topic " + mTopic); if (index > mIndex) setIndex (index); for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter) if (iter->mInfoId==entry.mInfoId) return; mEntries.push_back (entry); // we want slicing here } void Quest::write (ESM::QuestState& state) const { state.mTopic = getTopic(); state.mState = mIndex; state.mFinished = mFinished; } } openmw-openmw-0.38.0/apps/openmw/mwdialogue/quest.hpp000066400000000000000000000017731264522266000227060ustar00rootroot00000000000000#ifndef GAME_MWDIALOG_QUEST_H #define GAME_MWDIALOG_QUEST_H #include "topic.hpp" namespace ESM { struct QuestState; } namespace MWDialogue { /// \brief A quest in progress or a completed quest class Quest : public Topic { int mIndex; bool mFinished; public: Quest(); Quest (const std::string& topic); Quest (const ESM::QuestState& state); virtual std::string getName() const; ///< May be an empty string int getIndex() const; void setIndex (int index); ///< Calling this function with a non-existent index will throw an exception. bool isFinished() const; virtual void addEntry (const JournalEntry& entry); ///< Add entry and adjust index accordingly. /// /// \note Redundant entries are ignored, but the index is still adjusted. void write (ESM::QuestState& state) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/scripttest.cpp000066400000000000000000000105231264522266000237350ustar00rootroot00000000000000#include "scripttest.hpp" #include #include "../mwworld/manualref.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwscript/compilercontext.hpp" #include #include #include #include #include #include #include "filter.hpp" namespace { void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler::Extensions* extensions, int warningsMode) { MWDialogue::Filter filter(actor, 0, false); MWScript::CompilerContext compilerContext(MWScript::CompilerContext::Type_Dialogue); compilerContext.setExtensions(extensions); std::ostream errorStream(std::cout.rdbuf()); Compiler::StreamErrorHandler errorHandler(errorStream); errorHandler.setWarningsMode (warningsMode); const MWWorld::Store& dialogues = MWBase::Environment::get().getWorld()->getStore().get(); for (MWWorld::Store::iterator it = dialogues.begin(); it != dialogues.end(); ++it) { std::vector infos = filter.listAll(*it); for (std::vector::iterator it = infos.begin(); it != infos.end(); ++it) { const ESM::DialInfo* info = *it; if (!info->mResultScript.empty()) { bool success = true; ++total; try { errorHandler.reset(); std::istringstream input (info->mResultScript + "\n"); Compiler::Scanner scanner (errorHandler, input, extensions); Compiler::Locals locals; std::string actorScript = actor.getClass().getScript(actor); if (!actorScript.empty()) { // grab local variables from actor's script, if available. locals = MWBase::Environment::get().getScriptManager()->getLocals (actorScript); } Compiler::ScriptParser parser(errorHandler, compilerContext, locals, false); scanner.scan (parser); if (!errorHandler.isGood()) success = false; ++compiled; } catch (const Compiler::SourceException& /* error */) { // error has already been reported via error handler success = false; } catch (const std::exception& error) { std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; success = false; } if (!success) { std::cerr << "compiling failed (dialogue script)" << std::endl << info->mResultScript << std::endl << std::endl; } } } } } } namespace MWDialogue { namespace ScriptTest { std::pair compileAll(const Compiler::Extensions *extensions, int warningsMode) { int compiled = 0, total = 0; const MWWorld::Store& npcs = MWBase::Environment::get().getWorld()->getStore().get(); for (MWWorld::Store::iterator it = npcs.begin(); it != npcs.end(); ++it) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->mId); test(ref.getPtr(), compiled, total, extensions, warningsMode); } const MWWorld::Store& creatures = MWBase::Environment::get().getWorld()->getStore().get(); for (MWWorld::Store::iterator it = creatures.begin(); it != creatures.end(); ++it) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->mId); test(ref.getPtr(), compiled, total, extensions, warningsMode); } return std::make_pair(total, compiled); } } } openmw-openmw-0.38.0/apps/openmw/mwdialogue/scripttest.hpp000066400000000000000000000007021264522266000237400ustar00rootroot00000000000000#ifndef OPENMW_MWDIALOGUE_SCRIPTTEST_H #define OPENMW_MWDIALOGUE_SCRIPTTEST_H #include namespace MWDialogue { namespace ScriptTest { /// Attempt to compile all dialogue scripts, use for verification purposes /// @return A pair containing std::pair compileAll(const Compiler::Extensions* extensions, int warningsMode); } } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/selectwrapper.cpp000066400000000000000000000221371264522266000244150ustar00rootroot00000000000000#include "selectwrapper.hpp" #include #include #include #include #include #include namespace { template bool selectCompareImp (char comp, T1 value1, T2 value2) { switch (comp) { case '0': return value1==value2; case '1': return value1!=value2; case '2': return value1>value2; case '3': return value1>=value2; case '4': return value1 bool selectCompareImp (const ESM::DialInfo::SelectStruct& select, T value1) { if (select.mValue.getType()==ESM::VT_Int) { return selectCompareImp (select.mSelectRule[4], value1, select.mValue.getInteger()); } else if (select.mValue.getType()==ESM::VT_Float) { return selectCompareImp (select.mSelectRule[4], value1, select.mValue.getFloat()); } else throw std::runtime_error ( "unsupported variable type in dialogue info select"); } } MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::decodeFunction() const { int index = 0; std::istringstream (mSelect.mSelectRule.substr(2,2)) >> index; switch (index) { case 0: return Function_RankLow; case 1: return Function_RankHigh; case 2: return Function_RankRequirement; case 3: return Function_Reputation; case 4: return Function_HealthPercent; case 5: return Function_PCReputation; case 6: return Function_PcLevel; case 7: return Function_PcHealthPercent; case 8: case 9: return Function_PcDynamicStat; case 10: return Function_PcAttribute; case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: return Function_PcSkill; case 38: return Function_PcGender; case 39: return Function_PcExpelled; case 40: return Function_PcCommonDisease; case 41: return Function_PcBlightDisease; case 42: return Function_PcClothingModifier; case 43: return Function_PcCrimeLevel; case 44: return Function_SameGender; case 45: return Function_SameRace; case 46: return Function_SameFaction; case 47: return Function_FactionRankDiff; case 48: return Function_Detected; case 49: return Function_Alarmed; case 50: return Function_Choice; case 51: case 52: case 53: case 54: case 55: case 56: case 57: return Function_PcAttribute; case 58: return Function_PcCorprus; case 59: return Function_Weather; case 60: return Function_PcVampire; case 61: return Function_Level; case 62: return Function_Attacked; case 63: return Function_TalkedToPc; case 64: return Function_PcDynamicStat; case 65: return Function_CreatureTargetted; case 66: return Function_FriendlyHit; case 67: case 68: case 69: case 70: return Function_AiSetting; case 71: return Function_ShouldAttack; case 72: return Function_Werewolf; case 73: return Function_WerewolfKills; } return Function_False; } MWDialogue::SelectWrapper::SelectWrapper (const ESM::DialInfo::SelectStruct& select) : mSelect (select) {} MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::getFunction() const { char type = mSelect.mSelectRule[1]; switch (type) { case '1': return decodeFunction(); case '2': return Function_Global; case '3': return Function_Local; case '4': return Function_Journal; case '5': return Function_Item; case '6': return Function_Dead; case '7': return Function_NotId; case '8': return Function_NotFaction; case '9': return Function_NotClass; case 'A': return Function_NotRace; case 'B': return Function_NotCell; case 'C': return Function_NotLocal; } return Function_None; } int MWDialogue::SelectWrapper::getArgument() const { if (mSelect.mSelectRule[1]!='1') return 0; int index = 0; std::istringstream (mSelect.mSelectRule.substr(2,2)) >> index; switch (index) { // AI settings case 67: return 1; case 68: return 0; case 69: return 3; case 70: return 2; // attributes case 10: return 0; case 51: return 1; case 52: return 2; case 53: return 3; case 54: return 4; case 55: return 5; case 56: return 6; case 57: return 7; // skills case 11: return 0; case 12: return 1; case 13: return 2; case 14: return 3; case 15: return 4; case 16: return 5; case 17: return 6; case 18: return 7; case 19: return 8; case 20: return 9; case 21: return 10; case 22: return 11; case 23: return 12; case 24: return 13; case 25: return 14; case 26: return 15; case 27: return 16; case 28: return 17; case 29: return 18; case 30: return 19; case 31: return 20; case 32: return 21; case 33: return 22; case 34: return 23; case 35: return 24; case 36: return 25; case 37: return 26; // dynamic stats case 8: return 1; case 9: return 2; case 64: return 0; } return 0; } MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const { static const Function integerFunctions[] = { Function_Journal, Function_Item, Function_Dead, Function_Choice, Function_AiSetting, Function_PcAttribute, Function_PcSkill, Function_FriendlyHit, Function_PcLevel, Function_PcGender, Function_PcClothingModifier, Function_PcCrimeLevel, Function_RankRequirement, Function_Level, Function_PCReputation, Function_Weather, Function_Reputation, Function_FactionRankDiff, Function_WerewolfKills, Function_RankLow, Function_RankHigh, Function_CreatureTargetted, Function_None // end marker }; static const Function numericFunctions[] = { Function_Global, Function_Local, Function_PcDynamicStat, Function_PcHealthPercent, Function_HealthPercent, Function_None // end marker }; static const Function booleanFunctions[] = { Function_False, Function_SameGender, Function_SameRace, Function_SameFaction, Function_PcCommonDisease, Function_PcBlightDisease, Function_PcCorprus, Function_PcExpelled, Function_PcVampire, Function_TalkedToPc, Function_Alarmed, Function_Detected, Function_Attacked, Function_ShouldAttack, Function_Werewolf, Function_None // end marker }; static const Function invertedBooleanFunctions[] = { Function_NotId, Function_NotFaction, Function_NotClass, Function_NotRace, Function_NotCell, Function_NotLocal, Function_None // end marker }; Function function = getFunction(); for (int i=0; integerFunctions[i]!=Function_None; ++i) if (integerFunctions[i]==function) return Type_Integer; for (int i=0; numericFunctions[i]!=Function_None; ++i) if (numericFunctions[i]==function) return Type_Numeric; for (int i=0; booleanFunctions[i]!=Function_None; ++i) if (booleanFunctions[i]==function) return Type_Boolean; for (int i=0; invertedBooleanFunctions[i]!=Function_None; ++i) if (invertedBooleanFunctions[i]==function) return Type_Inverted; return Type_None; } bool MWDialogue::SelectWrapper::isNpcOnly() const { static const Function functions[] = { Function_NotFaction, Function_NotClass, Function_NotRace, Function_SameGender, Function_SameRace, Function_SameFaction, Function_RankRequirement, Function_Reputation, Function_FactionRankDiff, Function_Werewolf, Function_WerewolfKills, Function_RankLow, Function_RankHigh, Function_None // end marker }; Function function = getFunction(); for (int i=0; functions[i]!=Function_None; ++i) if (functions[i]==function) return true; return false; } bool MWDialogue::SelectWrapper::selectCompare (int value) const { return selectCompareImp (mSelect, value); } bool MWDialogue::SelectWrapper::selectCompare (float value) const { return selectCompareImp (mSelect, value); } bool MWDialogue::SelectWrapper::selectCompare (bool value) const { return selectCompareImp (mSelect, static_cast (value)); } std::string MWDialogue::SelectWrapper::getName() const { return Misc::StringUtils::lowerCase (mSelect.mSelectRule.substr (5)); } openmw-openmw-0.38.0/apps/openmw/mwdialogue/selectwrapper.hpp000066400000000000000000000051331264522266000244170ustar00rootroot00000000000000#ifndef GAME_MWDIALOGUE_SELECTWRAPPER_H #define GAME_MWDIALOGUE_SELECTWRAPPER_H #include namespace MWDialogue { class SelectWrapper { const ESM::DialInfo::SelectStruct& mSelect; public: enum Function { Function_None, Function_False, Function_Journal, Function_Item, Function_Dead, Function_NotId, Function_NotFaction, Function_NotClass, Function_NotRace, Function_NotCell, Function_NotLocal, Function_Local, Function_Global, Function_SameGender, Function_SameRace, Function_SameFaction, Function_Choice, Function_PcCommonDisease, Function_PcBlightDisease, Function_PcCorprus, Function_AiSetting, Function_PcAttribute, Function_PcSkill, Function_PcExpelled, Function_PcVampire, Function_FriendlyHit, Function_TalkedToPc, Function_PcLevel, Function_PcHealthPercent, Function_PcDynamicStat, Function_PcGender, Function_PcClothingModifier, Function_PcCrimeLevel, Function_RankRequirement, Function_HealthPercent, Function_Level, Function_PCReputation, Function_Weather, Function_Reputation, Function_Alarmed, Function_FactionRankDiff, Function_Detected, Function_Attacked, Function_ShouldAttack, Function_CreatureTargetted, Function_Werewolf, Function_WerewolfKills, Function_RankLow, Function_RankHigh }; enum Type { Type_None, Type_Integer, Type_Numeric, Type_Boolean, Type_Inverted }; private: Function decodeFunction() const; public: SelectWrapper (const ESM::DialInfo::SelectStruct& select); Function getFunction() const; int getArgument() const; Type getType() const; bool isNpcOnly() const; ///< \attention Do not call any of the select functions for this select struct! bool selectCompare (int value) const; bool selectCompare (float value) const; bool selectCompare (bool value) const; std::string getName() const; ///< Return case-smashed name. }; } #endif openmw-openmw-0.38.0/apps/openmw/mwdialogue/topic.cpp000066400000000000000000000032371264522266000226530ustar00rootroot00000000000000#include "topic.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" namespace MWDialogue { Topic::Topic() {} Topic::Topic (const std::string& topic) : mTopic (topic), mName ( MWBase::Environment::get().getWorld()->getStore().get().find (topic)->mId) {} Topic::~Topic() {} void Topic::addEntry (const JournalEntry& entry) { if (entry.mTopic!=mTopic) throw std::runtime_error ("topic does not match: " + mTopic); // bail out if we already have heard this for (Topic::TEntryIter it = mEntries.begin(); it != mEntries.end(); ++it) { if (it->mInfoId == entry.mInfoId) return; } mEntries.push_back (entry); // we want slicing here } void Topic::insertEntry (const ESM::JournalEntry& entry) { mEntries.push_back (entry); } std::string Topic::getTopic() const { return mTopic; } std::string Topic::getName() const { return mName; } Topic::TEntryIter Topic::begin() const { return mEntries.begin(); } Topic::TEntryIter Topic::end() const { return mEntries.end(); } void Topic::removeLastAddedResponse (const std::string& actorName) { for (std::vector::reverse_iterator it = mEntries.rbegin(); it != mEntries.rend(); ++it) { if (it->mActorName == actorName) { mEntries.erase( (++it).base() ); // erase doesn't take a reverse_iterator return; } } } } openmw-openmw-0.38.0/apps/openmw/mwdialogue/topic.hpp000066400000000000000000000026121264522266000226540ustar00rootroot00000000000000#ifndef GAME_MWDIALOG_TOPIC_H #define GAME_MWDIALOG_TOPIC_H #include #include #include "journalentry.hpp" namespace ESM { struct JournalEntry; } namespace MWDialogue { /// \brief Collection of seen responses for a topic class Topic { public: typedef std::vector TEntryContainer; typedef TEntryContainer::const_iterator TEntryIter; protected: std::string mTopic; std::string mName; TEntryContainer mEntries; public: Topic(); Topic (const std::string& topic); virtual ~Topic(); virtual void addEntry (const JournalEntry& entry); ///< Add entry /// /// \note Redundant entries are ignored. void insertEntry (const ESM::JournalEntry& entry); ///< Add entry without checking for redundant entries or modifying the state of the /// topic otherwise std::string getTopic() const; virtual std::string getName() const; void removeLastAddedResponse (const std::string& actorName); TEntryIter begin() const; ///< Iterator pointing to the begin of the journal for this topic. TEntryIter end() const; ///< Iterator pointing past the end of the journal for this topic. }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/000077500000000000000000000000001264522266000200175ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwgui/alchemywindow.cpp000066400000000000000000000217331264522266000234030ustar00rootroot00000000000000#include "alchemywindow.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/alchemy.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include #include "inventoryitemmodel.hpp" #include "sortfilteritemmodel.hpp" #include "itemview.hpp" #include "itemwidget.hpp" namespace MWGui { AlchemyWindow::AlchemyWindow() : WindowBase("openmw_alchemy_window.layout") , mSortModel(NULL) , mAlchemy(new MWMechanics::Alchemy()) , mApparatus (4) , mIngredients (4) { getWidget(mCreateButton, "CreateButton"); getWidget(mCancelButton, "CancelButton"); getWidget(mIngredients[0], "Ingredient1"); getWidget(mIngredients[1], "Ingredient2"); getWidget(mIngredients[2], "Ingredient3"); getWidget(mIngredients[3], "Ingredient4"); getWidget(mApparatus[0], "Apparatus1"); getWidget(mApparatus[1], "Apparatus2"); getWidget(mApparatus[2], "Apparatus3"); getWidget(mApparatus[3], "Apparatus4"); getWidget(mEffectsBox, "CreatedEffects"); getWidget(mNameEdit, "NameEdit"); getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &AlchemyWindow::onSelectedItem); mIngredients[0]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); mIngredients[1]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); mIngredients[2]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); mIngredients[3]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked); center(); } void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { exit(); } void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) { MWMechanics::Alchemy::Result result = mAlchemy->create (mNameEdit->getCaption ()); if (result == MWMechanics::Alchemy::Result_NoName) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage37}"); return; } // check if mortar & pestle is available (always needed) if (result == MWMechanics::Alchemy::Result_NoMortarAndPestle) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage45}"); return; } // make sure 2 or more ingredients were selected if (result == MWMechanics::Alchemy::Result_LessThanTwoIngredients) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage6a}"); return; } if (result == MWMechanics::Alchemy::Result_NoEffects) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}"); MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); return; } if (result == MWMechanics::Alchemy::Result_Success) { MWBase::Environment::get().getWindowManager()->messageBox("#{sPotionSuccess}"); MWBase::Environment::get().getSoundManager()->playSound("potion success", 1.f, 1.f); } else if (result == MWMechanics::Alchemy::Result_RandomFailure) { // potion failed MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}"); MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); } // reduce count of the ingredients for (int i=0; i<4; ++i) if (mIngredients[i]->isUserString("ToolTipType")) { MWWorld::Ptr ingred = *mIngredients[i]->getUserData(); if (ingred.getRefData().getCount() == 0) removeIngredient(mIngredients[i]); } update(); } void AlchemyWindow::open() { mAlchemy->setAlchemist (MWMechanics::getPlayer()); InventoryItemModel* model = new InventoryItemModel(MWMechanics::getPlayer()); mSortModel = new SortFilterItemModel(model); mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients); mItemView->setModel (mSortModel); mItemView->resetScrollBars(); mNameEdit->setCaption(""); int index = 0; for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools()); iter!=mAlchemy->endTools() && index (mApparatus.size()); ++iter, ++index) { mApparatus.at (index)->setItem(*iter); mApparatus.at (index)->clearUserStrings(); if (!iter->isEmpty()) { mApparatus.at (index)->setUserString ("ToolTipType", "ItemPtr"); mApparatus.at (index)->setUserData (*iter); } } update(); } void AlchemyWindow::exit() { mAlchemy->clear(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Inventory); } void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender) { removeIngredient(_sender); update(); } void AlchemyWindow::onSelectedItem(int index) { MWWorld::Ptr item = mSortModel->getItem(index).mBase; int res = mAlchemy->addIngredient(item); if (res != -1) { update(); std::string sound = item.getClass().getUpSoundId(item); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); } } void AlchemyWindow::update() { std::string suggestedName = mAlchemy->suggestPotionName(); if (suggestedName != mSuggestedPotionName) mNameEdit->setCaptionWithReplacing(suggestedName); mSuggestedPotionName = suggestedName; mSortModel->clearDragItems(); MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy->beginIngredients (); for (int i=0; i<4; ++i) { ItemWidget* ingredient = mIngredients[i]; MWWorld::Ptr item; if (it != mAlchemy->endIngredients ()) { item = *it; ++it; } if (!item.isEmpty()) mSortModel->addDragItem(item, item.getRefData().getCount()); if (ingredient->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0)); ingredient->clearUserStrings (); ingredient->setItem(item); if (item.isEmpty ()) continue; ingredient->setUserString("ToolTipType", "ItemPtr"); ingredient->setUserData(item); ingredient->setCount(ingredient->getUserData()->getRefData().getCount()); } mItemView->update(); std::set effectIds = mAlchemy->listEffects(); Widgets::SpellEffectList list; for (std::set::iterator it = effectIds.begin(); it != effectIds.end(); ++it) { Widgets::SpellEffectParams params; params.mEffectID = it->mId; const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(it->mId); if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) params.mSkill = it->mArg; else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) params.mAttribute = it->mArg; params.mIsConstant = true; params.mNoTarget = true; list.push_back(params); } while (mEffectsBox->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mEffectsBox->getChildAt(0)); MyGUI::IntCoord coord(0, 0, mEffectsBox->getWidth(), 24); Widgets::MWEffectListPtr effectsWidget = mEffectsBox->createWidget ("MW_StatName", coord, MyGUI::Align::Left | MyGUI::Align::Top); effectsWidget->setEffectList(list); std::vector effectItems; effectsWidget->createEffectWidgets(effectItems, mEffectsBox, coord, false, 0); effectsWidget->setCoord(coord); } void AlchemyWindow::removeIngredient(MyGUI::Widget* ingredient) { for (int i=0; i<4; ++i) if (mIngredients[i] == ingredient) mAlchemy->removeIngredient (i); update(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/alchemywindow.hpp000066400000000000000000000021741264522266000234060ustar00rootroot00000000000000#ifndef MWGUI_ALCHEMY_H #define MWGUI_ALCHEMY_H #include #include "widgets.hpp" #include "windowbase.hpp" namespace MWMechanics { class Alchemy; } namespace MWGui { class ItemView; class ItemWidget; class SortFilterItemModel; class AlchemyWindow : public WindowBase { public: AlchemyWindow(); virtual void open(); virtual void exit(); private: std::string mSuggestedPotionName; ItemView* mItemView; SortFilterItemModel* mSortModel; MyGUI::Button* mCreateButton; MyGUI::Button* mCancelButton; MyGUI::Widget* mEffectsBox; MyGUI::EditBox* mNameEdit; void onCancelButtonClicked(MyGUI::Widget* _sender); void onCreateButtonClicked(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender); void onSelectedItem(int index); void removeIngredient(MyGUI::Widget* ingredient); void update(); std::auto_ptr mAlchemy; std::vector mApparatus; std::vector mIngredients; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/backgroundimage.cpp000066400000000000000000000026061264522266000236510ustar00rootroot00000000000000#include "backgroundimage.hpp" #include namespace MWGui { void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRatio, bool stretch) { if (mChild) { MyGUI::Gui::getInstance().destroyWidget(mChild); mChild = NULL; } if (!stretch) { setImageTexture("black"); if (fixedRatio) mAspect = 4.0/3.0; else mAspect = 0; // TODO mChild = createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default); mChild->setImageTexture(image); adjustSize(); } else { mAspect = 0; setImageTexture(image); } } void BackgroundImage::adjustSize() { if (mAspect == 0) return; MyGUI::IntSize screenSize = getSize(); int leftPadding = std::max(0, static_cast(screenSize.width - screenSize.height * mAspect) / 2); int topPadding = std::max(0, static_cast(screenSize.height - screenSize.width / mAspect) / 2); mChild->setCoord(leftPadding, topPadding, screenSize.width - leftPadding*2, screenSize.height - topPadding*2); } void BackgroundImage::setSize (const MyGUI::IntSize& _value) { MyGUI::Widget::setSize (_value); adjustSize(); } void BackgroundImage::setCoord (const MyGUI::IntCoord& _value) { MyGUI::Widget::setCoord (_value); adjustSize(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/backgroundimage.hpp000066400000000000000000000016471264522266000236620ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_BACKGROUNDIMAGE_H #define OPENMW_MWGUI_BACKGROUNDIMAGE_H #include namespace MWGui { /** * @brief A variant of MyGUI::ImageBox with aspect ratio correction using black bars */ class BackgroundImage : public MyGUI::ImageBox { MYGUI_RTTI_DERIVED(BackgroundImage) public: BackgroundImage() : mChild(NULL), mAspect(0) {} /** * @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions * @param stretch Stretch to fill the whole screen, or add black bars? */ void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool stretch=true); virtual void setSize (const MyGUI::IntSize &_value); virtual void setCoord (const MyGUI::IntCoord &_value); private: MyGUI::ImageBox* mChild; double mAspect; void adjustSize(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/birth.cpp000066400000000000000000000204441264522266000216370ustar00rootroot00000000000000#include "birth.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/esmstore.hpp" #include "widgets.hpp" namespace { bool sortBirthSigns(const std::pair& left, const std::pair& right) { return left.second->mName.compare (right.second->mName) < 0; } } namespace MWGui { BirthDialog::BirthDialog() : WindowModal("openmw_chargen_birth.layout") { // Centre dialog center(); getWidget(mSpellArea, "SpellArea"); getWidget(mBirthImage, "BirthsignImage"); getWidget(mBirthList, "BirthsignList"); mBirthList->setScrollVisible(true); mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onAccept); mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); MyGUI::Button* backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); updateBirths(); updateSpells(); } void BirthDialog::setNextButtonShow(bool shown) { MyGUI::Button* okButton; getWidget(okButton, "OKButton"); if (shown) okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } void BirthDialog::open() { WindowModal::open(); updateBirths(); updateSpells(); } void BirthDialog::setBirthId(const std::string &birthId) { mCurrentBirthId = birthId; mBirthList->setIndexSelected(MyGUI::ITEM_NONE); size_t count = mBirthList->getItemCount(); for (size_t i = 0; i < count; ++i) { if (Misc::StringUtils::ciEqual(*mBirthList->getItemDataAt(i), birthId)) { mBirthList->setIndexSelected(i); break; } } updateSpells(); } // widget controls void BirthDialog::onOkClicked(MyGUI::Widget* _sender) { if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) return; eventDone(this); } void BirthDialog::onAccept(MyGUI::ListBox *_sender, size_t _index) { onSelectBirth(_sender, _index); if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) return; eventDone(this); } void BirthDialog::onBackClicked(MyGUI::Widget* _sender) { eventBack(); } void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index) { if (_index == MyGUI::ITEM_NONE) return; const std::string *birthId = mBirthList->getItemDataAt(_index); if (Misc::StringUtils::ciEqual(mCurrentBirthId, *birthId)) return; mCurrentBirthId = *birthId; updateSpells(); } // update widget content void BirthDialog::updateBirths() { mBirthList->removeAllItems(); const MWWorld::Store &signs = MWBase::Environment::get().getWorld()->getStore().get(); // sort by name std::vector < std::pair > birthSigns; MWWorld::Store::iterator it = signs.begin(); for (; it != signs.end(); ++it) { birthSigns.push_back(std::make_pair(it->mId, &(*it))); } std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); int index = 0; for (std::vector >::const_iterator it2 = birthSigns.begin(); it2 != birthSigns.end(); ++it2, ++index) { mBirthList->addItem(it2->second->mName, it2->first); if (mCurrentBirthId.empty()) { mBirthList->setIndexSelected(index); mCurrentBirthId = it2->first; } else if (Misc::StringUtils::ciEqual(it2->first, mCurrentBirthId)) { mBirthList->setIndexSelected(index); } } } void BirthDialog::updateSpells() { for (std::vector::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } mSpellItems.clear(); if (mCurrentBirthId.empty()) return; Widgets::MWSpellPtr spellWidget; const int lineHeight = 18; MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::BirthSign *birth = store.get().find(mCurrentBirthId); mBirthImage->setImageTexture(MWBase::Environment::get().getWindowManager()->correctTexturePath(birth->mTexture)); std::vector abilities, powers, spells; std::vector::const_iterator it = birth->mPowers.mList.begin(); std::vector::const_iterator end = birth->mPowers.mList.end(); for (; it != end; ++it) { const std::string &spellId = *it; const ESM::Spell *spell = store.get().search(spellId); if (!spell) continue; // Skip spells which cannot be found ESM::Spell::SpellType type = static_cast(spell->mData.mType); if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) continue; // We only want spell, ability and powers. if (type == ESM::Spell::ST_Ability) abilities.push_back(spellId); else if (type == ESM::Spell::ST_Power) powers.push_back(spellId); else if (type == ESM::Spell::ST_Spell) spells.push_back(spellId); } int i = 0; struct { const std::vector &spells; const char *label; } categories[3] = { {abilities, "sBirthsignmenu1"}, {powers, "sPowers"}, {spells, "sBirthsignmenu2"} }; for (int category = 0; category < 3; ++category) { if (!categories[category].spells.empty()) { MyGUI::TextBox* label = mSpellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); label->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(categories[category].label, "")); mSpellItems.push_back(label); coord.top += lineHeight; std::vector::const_iterator end = categories[category].spells.end(); for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) { const std::string &spellId = *it; spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + MyGUI::utility::toString(i)); spellWidget->setSpellId(spellId); mSpellItems.push_back(spellWidget); coord.top += lineHeight; MyGUI::IntCoord spellCoord = coord; spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? Widgets::MWEffectList::EF_Constant : 0); coord.top = spellCoord.top; ++i; } } } } } openmw-openmw-0.38.0/apps/openmw/mwgui/birth.hpp000066400000000000000000000025251264522266000216440ustar00rootroot00000000000000#ifndef MWGUI_BIRTH_H #define MWGUI_BIRTH_H #include "windowbase.hpp" namespace MWGui { class BirthDialog : public WindowModal { public: BirthDialog(); enum Gender { GM_Male, GM_Female }; const std::string &getBirthId() const { return mCurrentBirthId; } void setBirthId(const std::string &raceId); void setNextButtonShow(bool shown); virtual void open(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n */ EventHandle_Void eventBack; /** Event : Dialog finished, OK button clicked.\n signature : void method()\n */ EventHandle_WindowBase eventDone; protected: void onSelectBirth(MyGUI::ListBox* _sender, size_t _index); void onAccept(MyGUI::ListBox* _sender, size_t index); void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); private: void updateBirths(); void updateSpells(); MyGUI::ListBox* mBirthList; MyGUI::Widget* mSpellArea; MyGUI::ImageBox* mBirthImage; std::vector mSpellItems; std::string mCurrentBirthId; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/bookpage.cpp000066400000000000000000001217741264522266000223260ustar00rootroot00000000000000#include "bookpage.hpp" #include "MyGUI_FontManager.h" #include "MyGUI_RenderItem.h" #include "MyGUI_RenderManager.h" #include "MyGUI_TextureUtility.h" #include "MyGUI_FactoryManager.h" #include #include #include #include #include namespace MWGui { struct TypesetBookImpl; class PageDisplay; class BookPageImpl; static bool ucsSpace (int codePoint); static bool ucsLineBreak (int codePoint); static bool ucsBreakingSpace (int codePoint); struct BookTypesetter::Style { virtual ~Style () {} }; struct TypesetBookImpl : TypesetBook { typedef std::vector Content; typedef std::list Contents; typedef Utf8Stream::Point Utf8Point; typedef std::pair Range; struct StyleImpl : BookTypesetter::Style { MyGUI::IFont* mFont; MyGUI::Colour mHotColour; MyGUI::Colour mActiveColour; MyGUI::Colour mNormalColour; InteractiveId mInteractiveId; bool match (MyGUI::IFont* tstFont, MyGUI::Colour tstHotColour, MyGUI::Colour tstActiveColour, MyGUI::Colour tstNormalColour, intptr_t tstInteractiveId) { return (mFont == tstFont) && partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); } bool match (char const * tstFont, MyGUI::Colour tstHotColour, MyGUI::Colour tstActiveColour, MyGUI::Colour tstNormalColour, intptr_t tstInteractiveId) { return (mFont->getResourceName () == tstFont) && partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); } bool partal_match (MyGUI::Colour tstHotColour, MyGUI::Colour tstActiveColour, MyGUI::Colour tstNormalColour, intptr_t tstInteractiveId) { return (mHotColour == tstHotColour ) && (mActiveColour == tstActiveColour ) && (mNormalColour == tstNormalColour ) && (mInteractiveId == tstInteractiveId ) ; } }; typedef std::list Styles; struct Run { StyleImpl* mStyle; Range mRange; int mLeft, mRight; int mPrintableChars; }; typedef std::vector Runs; struct Line { Runs mRuns; MyGUI::IntRect mRect; }; typedef std::vector Lines; struct Section { Lines mLines; MyGUI::IntRect mRect; }; typedef std::vector

Sections; // Holds "top" and "bottom" vertical coordinates in the source text. // A page is basically a "window" into a portion of the source text, similar to a ScrollView. typedef std::pair Page; typedef std::vector Pages; Pages mPages; Sections mSections; Contents mContents; Styles mStyles; MyGUI::IntRect mRect; virtual ~TypesetBookImpl () {} Range addContent (BookTypesetter::Utf8Span text) { Contents::iterator i = mContents.insert (mContents.end (), Content (text.first, text.second)); if (i->empty()) return Range (Utf8Point (NULL), Utf8Point (NULL)); Utf8Point begin = &i->front (); Utf8Point end = &i->front () + i->size (); return Range (begin, end); } size_t pageCount () const { return mPages.size (); } std::pair getSize () const { return std::make_pair (mRect.width (), mRect.height ()); } template void visitRuns (int top, int bottom, MyGUI::IFont* Font, Visitor const & visitor) const { for (Sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i) { if (top >= mRect.bottom || bottom <= i->mRect.top) continue; for (Lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) { if (top >= j->mRect.bottom || bottom <= j->mRect.top) continue; for (Runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k) if (!Font || k->mStyle->mFont == Font) visitor (*i, *j, *k); } } } template void visitRuns (int top, int bottom, Visitor const & visitor) const { visitRuns (top, bottom, NULL, visitor); } /// hit test with a margin for error. only hits on interactive text fragments are reported. StyleImpl * hitTestWithMargin (int left, int top) { StyleImpl * hit = hitTest(left, top); if (hit && hit->mInteractiveId > 0) return hit; const int maxMargin = 10; for (int margin=1; margin < maxMargin; ++margin) { for (int i=0; i<4; ++i) { if (i==0) hit = hitTest(left, top-margin); else if (i==1) hit = hitTest(left, top+margin); else if (i==2) hit = hitTest(left-margin, top); else hit = hitTest(left+margin, top); if (hit && hit->mInteractiveId > 0) return hit; } } return NULL; } StyleImpl * hitTest (int left, int top) const { for (Sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i) { if (top < i->mRect.top || top >= i->mRect.bottom) continue; int left1 = left - i->mRect.left; for (Lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) { if (top < j->mRect.top || top >= j->mRect.bottom) continue; int left2 = left1 - j->mRect.left; for (Runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k) { if (left2 < k->mLeft || left2 >= k->mRight) continue; return k->mStyle; } } } return nullptr; } MyGUI::IFont* affectedFont (StyleImpl* style) { for (Styles::iterator i = mStyles.begin (); i != mStyles.end (); ++i) if (&*i == style) return i->mFont; return NULL; } struct Typesetter; }; struct TypesetBookImpl::Typesetter : BookTypesetter { struct PartialText { StyleImpl *mStyle; Utf8Stream::Point mBegin; Utf8Stream::Point mEnd; int mWidth; PartialText( StyleImpl *style, Utf8Stream::Point begin, Utf8Stream::Point end, int width) : mStyle(style), mBegin(begin), mEnd(end), mWidth(width) {} }; typedef TypesetBookImpl Book; typedef boost::shared_ptr BookPtr; typedef std::vector::const_iterator PartialTextConstIterator; int mPageWidth; int mPageHeight; BookPtr mBook; Section * mSection; Line * mLine; Run * mRun; std::vector mSectionAlignment; std::vector mPartialWhitespace; std::vector mPartialWord; Book::Content const * mCurrentContent; Alignment mCurrentAlignment; Typesetter (size_t width, size_t height) : mPageWidth (width), mPageHeight(height), mSection (NULL), mLine (NULL), mRun (NULL), mCurrentContent (NULL), mCurrentAlignment (AlignLeft) { mBook = boost::make_shared (); } virtual ~Typesetter () { } Style * createStyle (char const * fontName, Colour fontColour) { if (strcmp(fontName, "") == 0) return createStyle(MyGUI::FontManager::getInstance().getDefaultFont().c_str(), fontColour); for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) if (i->match (fontName, fontColour, fontColour, fontColour, 0)) return &*i; StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); style.mFont = MyGUI::FontManager::getInstance().getByName(fontName); style.mHotColour = fontColour; style.mActiveColour = fontColour; style.mNormalColour = fontColour; style.mInteractiveId = 0; return &style; } Style* createHotStyle (Style* baseStyle, Colour normalColour, Colour hoverColour, Colour activeColour, InteractiveId id, bool unique) { StyleImpl* BaseStyle = static_cast (baseStyle); if (!unique) for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) if (i->match (BaseStyle->mFont, hoverColour, activeColour, normalColour, id)) return &*i; StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); style.mFont = BaseStyle->mFont; style.mHotColour = hoverColour; style.mActiveColour = activeColour; style.mNormalColour = normalColour; style.mInteractiveId = id; return &style; } void write (Style * style, Utf8Span text) { Range range = mBook->addContent (text); writeImpl (static_cast (style), range.first, range.second); } intptr_t addContent (Utf8Span text, bool select) { add_partial_text(); Contents::iterator i = mBook->mContents.insert (mBook->mContents.end (), Content (text.first, text.second)); if (select) mCurrentContent = &(*i); return reinterpret_cast (&(*i)); } void selectContent (intptr_t contentHandle) { add_partial_text(); mCurrentContent = reinterpret_cast (contentHandle); } void write (Style * style, size_t begin, size_t end) { assert (mCurrentContent != NULL); assert (end <= mCurrentContent->size ()); assert (begin <= mCurrentContent->size ()); Utf8Point begin_ = &mCurrentContent->front () + begin; Utf8Point end_ = &mCurrentContent->front () + end ; writeImpl (static_cast (style), begin_, end_); } void lineBreak (float margin) { assert (margin == 0); //TODO: figure out proper behavior here... add_partial_text(); mRun = NULL; mLine = NULL; } void sectionBreak (int margin) { add_partial_text(); if (mBook->mSections.size () > 0) { mRun = NULL; mLine = NULL; mSection = NULL; if (mBook->mRect.bottom < (mBook->mSections.back ().mRect.bottom + margin)) mBook->mRect.bottom = (mBook->mSections.back ().mRect.bottom + margin); } } void setSectionAlignment (Alignment sectionAlignment) { add_partial_text(); if (mSection != NULL) mSectionAlignment.back () = sectionAlignment; mCurrentAlignment = sectionAlignment; } TypesetBook::Ptr complete () { int curPageStart = 0; int curPageStop = 0; add_partial_text(); std::vector ::iterator sa = mSectionAlignment.begin (); for (Sections::iterator i = mBook->mSections.begin (); i != mBook->mSections.end (); ++i, ++sa) { // apply alignment to individual lines... for (Lines::iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) { int width = j->mRect.width (); int excess = mPageWidth - width; switch (*sa) { default: case AlignLeft: j->mRect.left = 0; break; case AlignCenter: j->mRect.left = excess/2; break; case AlignRight: j->mRect.left = excess; break; } j->mRect.right = j->mRect.left + width; } if (curPageStop == curPageStart) { curPageStart = i->mRect.top; curPageStop = i->mRect.top; } int spaceLeft = mPageHeight - (curPageStop - curPageStart); int sectionHeight = i->mRect.height (); // This is NOT equal to i->mRect.height(), which doesn't account for section breaks. int spaceRequired = (i->mRect.bottom - curPageStop); if (curPageStart == curPageStop) // If this is a new page, the section break is not needed spaceRequired = i->mRect.height(); if (spaceRequired <= mPageHeight) { if (spaceRequired > spaceLeft) { // The section won't completely fit on the current page. Finish the current page and start a new one. assert (curPageStart != curPageStop); mBook->mPages.push_back (Page (curPageStart, curPageStop)); curPageStart = i->mRect.top; curPageStop = i->mRect.bottom; } else curPageStop = i->mRect.bottom; } else { //split section int sectionHeightLeft = sectionHeight; while (sectionHeightLeft > mPageHeight) { // Adjust to the top of the first line that does not fit on the current page anymore int splitPos = curPageStop; for (Lines::iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) { if (j->mRect.bottom > curPageStart + mPageHeight) { splitPos = j->mRect.top; break; } } mBook->mPages.push_back (Page (curPageStart, splitPos)); curPageStart = splitPos; curPageStop = splitPos; sectionHeightLeft = (i->mRect.bottom - splitPos); } curPageStop = i->mRect.bottom; } } if (curPageStart != curPageStop) mBook->mPages.push_back (Page (curPageStart, curPageStop)); return mBook; } void writeImpl (StyleImpl * style, Utf8Stream::Point _begin, Utf8Stream::Point _end) { Utf8Stream stream (_begin, _end); while (!stream.eof ()) { if (ucsLineBreak (stream.peek ())) { add_partial_text(); stream.consume (); mLine = NULL, mRun = NULL; continue; } if (ucsBreakingSpace (stream.peek ()) && !mPartialWord.empty()) add_partial_text(); int word_width = 0; int space_width = 0; Utf8Stream::Point lead = stream.current (); while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) { MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); if (gi) space_width += static_cast(gi->advance + gi->bearingX); stream.consume (); } Utf8Stream::Point origin = stream.current (); while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) { MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); if (gi) word_width += static_cast(gi->advance + gi->bearingX); stream.consume (); } Utf8Stream::Point extent = stream.current (); if (lead == extent) break; if ( lead != origin ) mPartialWhitespace.push_back (PartialText (style, lead, origin, space_width)); if ( origin != extent ) mPartialWord.push_back (PartialText (style, origin, extent, word_width)); } } void add_partial_text () { if (mPartialWhitespace.empty() && mPartialWord.empty()) return; int space_width = 0; int word_width = 0; for (PartialTextConstIterator i = mPartialWhitespace.begin (); i != mPartialWhitespace.end (); ++i) space_width += i->mWidth; for (PartialTextConstIterator i = mPartialWord.begin (); i != mPartialWord.end (); ++i) word_width += i->mWidth; int left = mLine ? mLine->mRect.right : 0; if (left + space_width + word_width > mPageWidth) { mLine = NULL, mRun = NULL, left = 0; } else { for (PartialTextConstIterator i = mPartialWhitespace.begin (); i != mPartialWhitespace.end (); ++i) { int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; int line_height = i->mStyle->mFont->getDefaultHeight (); append_run ( i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + line_height); left = mLine->mRect.right; } } for (PartialTextConstIterator i = mPartialWord.begin (); i != mPartialWord.end (); ++i) { int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; int line_height = i->mStyle->mFont->getDefaultHeight (); append_run (i->mStyle, i->mBegin, i->mEnd, i->mEnd - i->mBegin, left + i->mWidth, top + line_height); left = mLine->mRect.right; } mPartialWhitespace.clear(); mPartialWord.clear(); } void append_run (StyleImpl * style, Utf8Stream::Point begin, Utf8Stream::Point end, int pc, int right, int bottom) { if (mSection == NULL) { mBook->mSections.push_back (Section ()); mSection = &mBook->mSections.back (); mSection->mRect = MyGUI::IntRect (0, mBook->mRect.bottom, 0, mBook->mRect.bottom); mSectionAlignment.push_back (mCurrentAlignment); } if (mLine == NULL) { mSection->mLines.push_back (Line ()); mLine = &mSection->mLines.back (); mLine->mRect = MyGUI::IntRect (0, mSection->mRect.bottom, 0, mBook->mRect.bottom); } if (mBook->mRect.right < right) mBook->mRect.right = right; if (mBook->mRect.bottom < bottom) mBook->mRect.bottom = bottom; if (mSection->mRect.right < right) mSection->mRect.right = right; if (mSection->mRect.bottom < bottom) mSection->mRect.bottom = bottom; if (mLine->mRect.right < right) mLine->mRect.right = right; if (mLine->mRect.bottom < bottom) mLine->mRect.bottom = bottom; if (mRun == NULL || mRun->mStyle != style || mRun->mRange.second != begin) { int left = mRun ? mRun->mRight : mLine->mRect.left; mLine->mRuns.push_back (Run ()); mRun = &mLine->mRuns.back (); mRun->mStyle = style; mRun->mLeft = left; mRun->mRight = right; mRun->mRange.first = begin; mRun->mRange.second = end; mRun->mPrintableChars = pc; //Run->Locale = Locale; } else { mRun->mRight = right; mRun->mRange.second = end; mRun->mPrintableChars += pc; } } }; BookTypesetter::Ptr BookTypesetter::create (int pageWidth, int pageHeight) { return boost::make_shared (pageWidth, pageHeight); } namespace { struct RenderXform { public: float clipTop; float clipLeft; float clipRight; float clipBottom; float absoluteLeft; float absoluteTop; float leftOffset; float topOffset; float pixScaleX; float pixScaleY; float hOffset; float vOffset; RenderXform (MyGUI::ICroppedRectangle* croppedParent, MyGUI::RenderTargetInfo const & renderTargetInfo) { clipTop = static_cast(croppedParent->_getMarginTop()); clipLeft = static_cast(croppedParent->_getMarginLeft ()); clipRight = static_cast(croppedParent->getWidth () - croppedParent->_getMarginRight ()); clipBottom = static_cast(croppedParent->getHeight() - croppedParent->_getMarginBottom()); absoluteLeft = static_cast(croppedParent->getAbsoluteLeft()); absoluteTop = static_cast(croppedParent->getAbsoluteTop()); leftOffset = static_cast(renderTargetInfo.leftOffset); topOffset = static_cast(renderTargetInfo.topOffset); pixScaleX = renderTargetInfo.pixScaleX; pixScaleY = renderTargetInfo.pixScaleY; hOffset = renderTargetInfo.hOffset; vOffset = renderTargetInfo.vOffset; } bool clip (MyGUI::FloatRect & vr, MyGUI::FloatRect & tr) { if (vr.bottom <= clipTop || vr.right <= clipLeft || vr.left >= clipRight || vr.top >= clipBottom ) return false; if (vr.top < clipTop) { tr.top += tr.height () * (clipTop - vr.top) / vr.height (); vr.top = clipTop; } if (vr.left < clipLeft) { tr.left += tr.width () * (clipLeft - vr.left) / vr.width (); vr.left = clipLeft; } if (vr.right > clipRight) { tr.right -= tr.width () * (vr.right - clipRight) / vr.width (); vr.right = clipRight; } if (vr.bottom > clipBottom) { tr.bottom -= tr.height () * (vr.bottom - clipBottom) / vr.height (); vr.bottom = clipBottom; } return true; } MyGUI::FloatPoint operator () (MyGUI::FloatPoint pt) { pt.left = absoluteLeft - leftOffset + pt.left; pt.top = absoluteTop - topOffset + pt.top; pt.left = +(((pixScaleX * pt.left + hOffset) * 2.0f) - 1.0f); pt.top = -(((pixScaleY * pt.top + vOffset) * 2.0f) - 1.0f); return pt; } }; struct GlyphStream { float mZ; uint32_t mC; MyGUI::IFont* mFont; MyGUI::FloatPoint mOrigin; MyGUI::FloatPoint mCursor; MyGUI::Vertex* mVertices; RenderXform mRenderXform; MyGUI::VertexColourType mVertexColourType; GlyphStream (MyGUI::IFont* font, float left, float top, float Z, MyGUI::Vertex* vertices, RenderXform const & renderXform) : mZ(Z), mC(0), mFont (font), mOrigin (left, top), mVertices (vertices), mRenderXform (renderXform) { mVertexColourType = MyGUI::RenderManager::getInstance().getVertexFormat(); } ~GlyphStream () { } MyGUI::Vertex* end () const { return mVertices; } void reset (float left, float top, MyGUI::Colour colour) { mC = MyGUI::texture_utility::toColourARGB(colour) | 0xFF000000; MyGUI::texture_utility::convertColour(mC, mVertexColourType); mCursor.left = mOrigin.left + left; mCursor.top = mOrigin.top + top; } void emitGlyph (wchar_t ch) { MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); if (!gi) return; MyGUI::FloatRect vr; vr.left = mCursor.left + gi->bearingX; vr.top = mCursor.top + gi->bearingY; vr.right = vr.left + gi->width; vr.bottom = vr.top + gi->height; MyGUI::FloatRect tr = gi->uvRect; if (mRenderXform.clip (vr, tr)) quad (vr, tr); mCursor.left += gi->bearingX + gi->advance; } void emitSpace (wchar_t ch) { MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); if (gi) mCursor.left += gi->bearingX + gi->advance; } private: void quad (const MyGUI::FloatRect& vr, const MyGUI::FloatRect& tr) { vertex (vr.left, vr.top, tr.left, tr.top); vertex (vr.right, vr.top, tr.right, tr.top); vertex (vr.left, vr.bottom, tr.left, tr.bottom); vertex (vr.right, vr.top, tr.right, tr.top); vertex (vr.left, vr.bottom, tr.left, tr.bottom); vertex (vr.right, vr.bottom, tr.right, tr.bottom); } void vertex (float x, float y, float u, float v) { MyGUI::FloatPoint pt = mRenderXform (MyGUI::FloatPoint (x, y)); mVertices->x = pt.left; mVertices->y = pt.top ; mVertices->z = mZ; mVertices->u = u; mVertices->v = v; mVertices->colour = mC; ++mVertices; } }; } class PageDisplay : public MyGUI::ISubWidgetText { MYGUI_RTTI_DERIVED(PageDisplay) protected: typedef TypesetBookImpl::Section Section; typedef TypesetBookImpl::Line Line; typedef TypesetBookImpl::Run Run; bool mIsPageReset; size_t mPage; struct TextFormat : ISubWidget { typedef MyGUI::IFont* Id; Id mFont; int mCountVertex; MyGUI::ITexture* mTexture; MyGUI::RenderItem* mRenderItem; PageDisplay * mDisplay; TextFormat (MyGUI::IFont* id, PageDisplay * display) : mFont (id), mCountVertex (0), mTexture (NULL), mRenderItem (NULL), mDisplay (display) { } void createDrawItem (MyGUI::ILayerNode* node) { assert (mRenderItem == NULL); if (mTexture != NULL) { mRenderItem = node->addToRenderItem(mTexture, false, false); mRenderItem->addDrawItem(this, mCountVertex); } } void destroyDrawItem (MyGUI::ILayerNode* node) { assert (mTexture != NULL ? mRenderItem != NULL : mRenderItem == NULL); if (mTexture != NULL) { mRenderItem->removeDrawItem (this); mRenderItem = NULL; } } void doRender() { mDisplay->doRender (*this); } // this isn't really a sub-widget, its just a "drawitem" which // should have its own interface void createDrawItem(MyGUI::ITexture* _texture, MyGUI::ILayerNode* _node) {} void destroyDrawItem() {}; }; void resetPage() { mIsPageReset = true; mPage = 0; } void setPage(size_t page) { mIsPageReset = false; mPage = page; } bool isPageDifferent(size_t page) { return mIsPageReset || (mPage != page); } public: typedef TypesetBookImpl::StyleImpl Style; typedef std::map ActiveTextFormats; int mViewTop; int mViewBottom; Style* mFocusItem; bool mItemActive; MyGUI::MouseButton mLastDown; boost::function mLinkClicked; boost::shared_ptr mBook; MyGUI::ILayerNode* mNode; ActiveTextFormats mActiveTextFormats; PageDisplay () { resetPage (); mViewTop = 0; mViewBottom = 0; mFocusItem = NULL; mItemActive = false; mNode = NULL; } void dirtyFocusItem () { if (mFocusItem != 0) { MyGUI::IFont* Font = mBook->affectedFont (mFocusItem); ActiveTextFormats::iterator i = mActiveTextFormats.find (Font); if (mNode) mNode->outOfDate (i->second->mRenderItem); } } void onMouseLostFocus () { if (!mBook) return; dirtyFocusItem (); mFocusItem = 0; mItemActive = false; } void onMouseMove (int left, int top) { if (!mBook) return; left -= mCroppedParent->getAbsoluteLeft (); top -= mCroppedParent->getAbsoluteTop (); Style * hit = mBook->hitTestWithMargin (left, mViewTop + top); if (mLastDown == MyGUI::MouseButton::None) { if (hit != mFocusItem) { dirtyFocusItem (); mFocusItem = hit; mItemActive = false; dirtyFocusItem (); } } else if (mFocusItem != 0) { bool newItemActive = hit == mFocusItem; if (newItemActive != mItemActive) { mItemActive = newItemActive; dirtyFocusItem (); } } } void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) { if (!mBook) return; // work around inconsistency in MyGUI where the mouse press coordinates aren't // transformed by the current Layer (even though mouse *move* events are). MyGUI::IntPoint pos (left, top); #if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3) pos = mNode->getLayer()->getPosition(left, top); #endif pos.left -= mCroppedParent->getAbsoluteLeft (); pos.top -= mCroppedParent->getAbsoluteTop (); if (mLastDown == MyGUI::MouseButton::None) { mFocusItem = mBook->hitTestWithMargin (pos.left, mViewTop + pos.top); mItemActive = true; dirtyFocusItem (); mLastDown = id; } } void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) { if (!mBook) return; // work around inconsistency in MyGUI where the mouse release coordinates aren't // transformed by the current Layer (even though mouse *move* events are). MyGUI::IntPoint pos (left, top); #if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3) pos = mNode->getLayer()->getPosition(left, top); #endif pos.left -= mCroppedParent->getAbsoluteLeft (); pos.top -= mCroppedParent->getAbsoluteTop (); if (mLastDown == id) { Style * item = mBook->hitTestWithMargin (pos.left, mViewTop + pos.top); bool clicked = mFocusItem == item; mItemActive = false; dirtyFocusItem (); mLastDown = MyGUI::MouseButton::None; if (clicked && mLinkClicked && item && item->mInteractiveId != 0) mLinkClicked (item->mInteractiveId); } } void showPage (TypesetBook::Ptr book, size_t newPage) { boost::shared_ptr newBook = boost::dynamic_pointer_cast (book); if (mBook != newBook) { mFocusItem = nullptr; mItemActive = 0; for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) { if (mNode != NULL) i->second->destroyDrawItem (mNode); delete i->second; } mActiveTextFormats.clear (); if (newBook != NULL) { createActiveFormats (newBook); mBook = newBook; setPage (newPage); if (newPage < mBook->mPages.size ()) { mViewTop = mBook->mPages [newPage].first; mViewBottom = mBook->mPages [newPage].second; } else { mViewTop = 0; mViewBottom = 0; } } else { mBook.reset (); resetPage (); mViewTop = 0; mViewBottom = 0; } } else if (mBook && isPageDifferent (newPage)) { if (mNode != NULL) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate(i->second->mRenderItem); setPage (newPage); if (newPage < mBook->mPages.size ()) { mViewTop = mBook->mPages [newPage].first; mViewBottom = mBook->mPages [newPage].second; } else { mViewTop = 0; mViewBottom = 0; } } } struct CreateActiveFormat { PageDisplay * this_; CreateActiveFormat (PageDisplay * this_) : this_ (this_) {} void operator () (Section const & section, Line const & line, Run const & run) const { MyGUI::IFont* Font = run.mStyle->mFont; ActiveTextFormats::iterator j = this_->mActiveTextFormats.find (Font); if (j == this_->mActiveTextFormats.end ()) { TextFormat * textFormat = new TextFormat (Font, this_); textFormat->mTexture = Font->getTextureFont (); j = this_->mActiveTextFormats.insert (std::make_pair (Font, textFormat)).first; } j->second->mCountVertex += run.mPrintableChars * 6; } }; void createActiveFormats (boost::shared_ptr newBook) { newBook->visitRuns (0, 0x7FFFFFFF, CreateActiveFormat (this)); if (mNode != NULL) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->createDrawItem (mNode); } void setVisible (bool newVisible) { if (mVisible == newVisible) return; mVisible = newVisible; if (mVisible) { // reset input state mLastDown = MyGUI::MouseButton::None; mFocusItem = nullptr; mItemActive = 0; } if (nullptr != mNode) { for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate(i->second->mRenderItem); } } void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node) { //test (); mNode = node; for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->createDrawItem (node); } struct RenderRun { PageDisplay * this_; GlyphStream &glyphStream; RenderRun (PageDisplay * this_, GlyphStream &glyphStream) : this_(this_), glyphStream (glyphStream) { } void operator () (Section const & section, Line const & line, Run const & run) const { bool isActive = run.mStyle->mInteractiveId && (run.mStyle == this_->mFocusItem); MyGUI::Colour colour = isActive ? (this_->mItemActive ? run.mStyle->mActiveColour: run.mStyle->mHotColour) : run.mStyle->mNormalColour; glyphStream.reset(static_cast(section.mRect.left + line.mRect.left + run.mLeft), static_cast(line.mRect.top), colour); Utf8Stream stream (run.mRange); while (!stream.eof ()) { Utf8Stream::UnicodeChar code_point = stream.consume (); if (!ucsSpace (code_point)) glyphStream.emitGlyph (code_point); else glyphStream.emitSpace (code_point); } } }; /* queue up rendering operations for this text format */ void doRender(TextFormat & textFormat) { if (!mVisible) return; MyGUI::Vertex* vertices = textFormat.mRenderItem->getCurrentVertexBuffer(); RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); GlyphStream glyphStream(textFormat.mFont, static_cast(mCoord.left), static_cast(mCoord.top - mViewTop), -1 /*mNode->getNodeDepth()*/, vertices, renderXform); int visit_top = (std::max) (mViewTop, mViewTop + int (renderXform.clipTop )); int visit_bottom = (std::min) (mViewBottom, mViewTop + int (renderXform.clipBottom)); mBook->visitRuns (visit_top, visit_bottom, textFormat.mFont, RenderRun (this, glyphStream)); textFormat.mRenderItem->setLastVertexCount(glyphStream.end () - vertices); } // ISubWidget should not necessarily be a drawitem // in this case, it is not... void doRender() { } void _updateView () { } void _correctView() { _checkMargin (); if (mNode != NULL) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate (i->second->mRenderItem); } void destroyDrawItem() { for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->destroyDrawItem (mNode); mNode = NULL; } }; class BookPageImpl : public BookPage { MYGUI_RTTI_DERIVED(BookPage) public: void showPage (TypesetBook::Ptr book, size_t page) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) pd->showPage (book, page); else throw std::runtime_error ("The main sub-widget for a BookPage must be a PageDisplay."); } void adviseLinkClicked (boost::function linkClicked) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->mLinkClicked = linkClicked; } } void unadviseLinkClicked () { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->mLinkClicked = boost::function (); } } protected: void onMouseLostFocus(Widget* _new) { // NOTE: MyGUI also fires eventMouseLostFocus for widgets that are about to be destroyed (if they had focus). // Child widgets may already be destroyed! So be careful. if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->onMouseLostFocus (); } else Widget::onMouseLostFocus (_new); } void onMouseMove(int left, int top) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->onMouseMove (left, top); } else Widget::onMouseMove (left, top); } void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->onMouseButtonPressed (left, top, id); } else Widget::onMouseButtonPressed (left, top, id); } void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->onMouseButtonReleased (left, top, id); } else Widget::onMouseButtonReleased (left, top, id); } }; void BookPage::registerMyGUIComponents () { MyGUI::FactoryManager & factory = MyGUI::FactoryManager::getInstance(); factory.registerFactory("Widget"); factory.registerFactory("BasisSkin"); } static bool ucsLineBreak (int codePoint) { return codePoint == '\n'; } static bool ucsSpace (int codePoint) { switch (codePoint) { case 0x0020: // SPACE case 0x00A0: // NO-BREAK SPACE case 0x1680: // OGHAM SPACE MARK case 0x180E: // MONGOLIAN VOWEL SEPARATOR case 0x2000: // EN QUAD case 0x2001: // EM QUAD case 0x2002: // EN SPACE case 0x2003: // EM SPACE case 0x2004: // THREE-PER-EM SPACE case 0x2005: // FOUR-PER-EM SPACE case 0x2006: // SIX-PER-EM SPACE case 0x2007: // FIGURE SPACE case 0x2008: // PUNCTUATION SPACE case 0x2009: // THIN SPACE case 0x200A: // HAIR SPACE case 0x200B: // ZERO WIDTH SPACE case 0x202F: // NARROW NO-BREAK SPACE case 0x205F: // MEDIUM MATHEMATICAL SPACE case 0x3000: // IDEOGRAPHIC SPACE case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE return true; default: return false; } } static bool ucsBreakingSpace (int codePoint) { switch (codePoint) { case 0x0020: // SPACE //case 0x00A0: // NO-BREAK SPACE case 0x1680: // OGHAM SPACE MARK case 0x180E: // MONGOLIAN VOWEL SEPARATOR case 0x2000: // EN QUAD case 0x2001: // EM QUAD case 0x2002: // EN SPACE case 0x2003: // EM SPACE case 0x2004: // THREE-PER-EM SPACE case 0x2005: // FOUR-PER-EM SPACE case 0x2006: // SIX-PER-EM SPACE case 0x2007: // FIGURE SPACE case 0x2008: // PUNCTUATION SPACE case 0x2009: // THIN SPACE case 0x200A: // HAIR SPACE case 0x200B: // ZERO WIDTH SPACE case 0x202F: // NARROW NO-BREAK SPACE case 0x205F: // MEDIUM MATHEMATICAL SPACE case 0x3000: // IDEOGRAPHIC SPACE //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE return true; default: return false; } } } openmw-openmw-0.38.0/apps/openmw/mwgui/bookpage.hpp000066400000000000000000000115711264522266000223240ustar00rootroot00000000000000#ifndef MWGUI_BOOKPAGE_HPP #define MWGUI_BOOKPAGE_HPP #include "MyGUI_Colour.h" #include "MyGUI_Widget.h" #include #include #include #include namespace MWGui { /// A formatted and paginated document to be used with /// the book page widget. struct TypesetBook { typedef boost::shared_ptr Ptr; typedef intptr_t InteractiveId; /// Returns the number of pages in the document. virtual size_t pageCount () const = 0; /// Return the area covered by the document. The first /// integer is the maximum with of any line. This is not /// the largest coordinate of the right edge of any line, /// it is the largest distance from the left edge to the /// right edge. The second integer is the height of all /// text combined prior to pagination. virtual std::pair getSize () const = 0; }; /// A factory class for creating a typeset book instance. struct BookTypesetter { typedef boost::shared_ptr Ptr; typedef TypesetBook::InteractiveId InteractiveId; typedef MyGUI::Colour Colour; typedef uint8_t const * Utf8Point; typedef std::pair Utf8Span; enum Alignment { AlignLeft = -1, AlignCenter = 0, AlignRight = +1 }; /// Styles are used to control the character level formatting /// of text added to a typeset book. Their lifetime is equal /// to the lifetime of the book-typesetter instance that created /// them. struct Style; /// A factory function for creating the default implementation of a book typesetter static Ptr create (int pageWidth, int pageHeight); /// Create a simple text style consisting of a font and a text color. virtual Style* createStyle (char const * Font, Colour Colour) = 0; /// Create a hyper-link style with a user-defined identifier based on an /// existing style. The unique flag forces a new instance of this style /// to be created even if an existing instance is present. virtual Style* createHotStyle (Style * BaseStyle, Colour NormalColour, Colour HoverColour, Colour ActiveColour, InteractiveId Id, bool Unique = true) = 0; /// Insert a line break into the document. Newline characters in the input /// text have the same affect. The margin parameter adds additional space /// before the next line of text. virtual void lineBreak (float margin = 0) = 0; /// Insert a section break into the document. This causes a new section /// to begin when additional text is inserted. Pagination attempts to keep /// sections together on a single page. The margin parameter adds additional space /// before the next line of text. virtual void sectionBreak (int margin = 0) = 0; /// Changes the alignment for the current section of text. virtual void setSectionAlignment (Alignment sectionAlignment) = 0; // Layout a block of text with the specified style into the document. virtual void write (Style * Style, Utf8Span Text) = 0; /// Adds a content block to the document without laying it out. An /// identifier is returned that can be used to refer to it. If select /// is true, the block is activated to be references by future writes. virtual intptr_t addContent (Utf8Span Text, bool Select = true) = 0; /// Select a previously created content block for future writes. virtual void selectContent (intptr_t contentHandle) = 0; /// Layout a span of the selected content block into the document /// using the specified style. virtual void write (Style * Style, size_t Begin, size_t End) = 0; /// Finalize the document layout, and return a pointer to it. virtual TypesetBook::Ptr complete () = 0; }; /// An interface to the BookPage widget. class BookPage : public MyGUI::Widget { MYGUI_RTTI_DERIVED(BookPage) public: typedef TypesetBook::InteractiveId InteractiveId; typedef boost::function ClickCallback; /// Make the widget display the specified page from the specified book. virtual void showPage (TypesetBook::Ptr Book, size_t Page) = 0; /// Set the callback for a clicking a hyper-link in the document. virtual void adviseLinkClicked (ClickCallback callback) = 0; /// Clear the hyper-link click callback. virtual void unadviseLinkClicked () = 0; /// Register the widget and associated sub-widget with MyGUI. Should be /// called once near the beginning of the program. static void registerMyGUIComponents (); }; } #endif // MWGUI_BOOKPAGE_HPP openmw-openmw-0.38.0/apps/openmw/mwgui/bookwindow.cpp000066400000000000000000000145111264522266000227070ustar00rootroot00000000000000#include "bookwindow.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/actiontake.hpp" #include "formatting.hpp" namespace MWGui { BookWindow::BookWindow () : WindowBase("openmw_book.layout") , mCurrentPage(0) , mTakeButtonShow(true) , mTakeButtonAllowed(true) { getWidget(mCloseButton, "CloseButton"); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); getWidget(mTakeButton, "TakeButton"); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); getWidget(mNextPageButton, "NextPageBTN"); mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); getWidget(mPrevPageButton, "PrevPageBTN"); mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); getWidget(mLeftPageNumber, "LeftPageNumber"); getWidget(mRightPageNumber, "RightPageNumber"); getWidget(mLeftPage, "LeftPage"); getWidget(mRightPage, "RightPage"); adjustButton(mCloseButton); adjustButton(mTakeButton); adjustButton(mNextPageButton); adjustButton(mPrevPageButton); mLeftPage->setNeedMouseFocus(true); mLeftPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel); mRightPage->setNeedMouseFocus(true); mRightPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel); if (mNextPageButton->getSize().width == 64) { // english button has a 7 pixel wide strip of garbage on its right edge mNextPageButton->setSize(64-7, mNextPageButton->getSize().height); mNextPageButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,mNextPageButton->getSize().height)); } center(); } void BookWindow::onMouseWheel(MyGUI::Widget *_sender, int _rel) { if (_rel < 0) nextPage(); else prevPage(); } void BookWindow::clearPages() { mPages.clear(); } void BookWindow::openBook (MWWorld::Ptr book, bool showTakeButton) { mBook = book; clearPages(); mCurrentPage = 0; MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); MWWorld::LiveCellRef *ref = mBook.get(); Formatting::BookFormatter formatter; mPages = formatter.markupToWidget(mLeftPage, ref->mBase->mText); formatter.markupToWidget(mRightPage, ref->mBase->mText); updatePages(); setTakeButtonShow(showTakeButton); } void BookWindow::exit() { // no 3d sounds because the object could be in a container. MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); } void BookWindow::setTakeButtonShow(bool show) { mTakeButtonShow = show; mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); } void BookWindow::setInventoryAllowed(bool allowed) { mTakeButtonAllowed = allowed; mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); } void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender) { exit(); } void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) { MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0); MWWorld::ActionTake take(mBook); take.execute (MWMechanics::getPlayer()); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); } void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) { nextPage(); } void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) { prevPage(); } void BookWindow::updatePages() { mLeftPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 1) ); mRightPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 2) ); //If it is the last page, hide the button "Next Page" if ( (mCurrentPage+1)*2 == mPages.size() || (mCurrentPage+1)*2 == mPages.size() + 1) { mNextPageButton->setVisible(false); } else { mNextPageButton->setVisible(true); } //If it is the fist page, hide the button "Prev Page" if (mCurrentPage == 0) { mPrevPageButton->setVisible(false); } else { mPrevPageButton->setVisible(true); } if (mPages.empty()) return; MyGUI::Widget * paper; paper = mLeftPage->getChildAt(0); paper->setCoord(paper->getPosition().left, -mPages[mCurrentPage*2].first, paper->getWidth(), mPages[mCurrentPage*2].second); paper = mRightPage->getChildAt(0); if ((mCurrentPage+1)*2 <= mPages.size()) { paper->setCoord(paper->getPosition().left, -mPages[mCurrentPage*2+1].first, paper->getWidth(), mPages[mCurrentPage*2+1].second); paper->setVisible(true); } else { paper->setVisible(false); } } void BookWindow::adjustButton (Gui::ImageButton* button) { MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); button->setSize(button->getRequestedSize()); if (button->getAlign().isRight()) button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); } void BookWindow::nextPage() { if ((mCurrentPage+1)*2 < mPages.size()) { MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); ++mCurrentPage; updatePages(); } } void BookWindow::prevPage() { if (mCurrentPage > 0) { MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); --mCurrentPage; updatePages(); } } } openmw-openmw-0.38.0/apps/openmw/mwgui/bookwindow.hpp000066400000000000000000000031721264522266000227150ustar00rootroot00000000000000#ifndef MWGUI_BOOKWINDOW_H #define MWGUI_BOOKWINDOW_H #include "windowbase.hpp" #include "../mwworld/ptr.hpp" #include namespace MWGui { class BookWindow : public WindowBase { public: BookWindow(); virtual void exit(); void openBook(MWWorld::Ptr book, bool showTakeButton); void setInventoryAllowed(bool allowed); protected: void onNextPageButtonClicked (MyGUI::Widget* sender); void onPrevPageButtonClicked (MyGUI::Widget* sender); void onCloseButtonClicked (MyGUI::Widget* sender); void onTakeButtonClicked (MyGUI::Widget* sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); void setTakeButtonShow(bool show); void nextPage(); void prevPage(); void updatePages(); void clearPages(); void adjustButton(Gui::ImageButton* button); private: typedef std::pair Page; typedef std::vector Pages; Gui::ImageButton* mCloseButton; Gui::ImageButton* mTakeButton; Gui::ImageButton* mNextPageButton; Gui::ImageButton* mPrevPageButton; MyGUI::TextBox* mLeftPageNumber; MyGUI::TextBox* mRightPageNumber; MyGUI::Widget* mLeftPage; MyGUI::Widget* mRightPage; unsigned int mCurrentPage; // 0 is first page Pages mPages; MWWorld::Ptr mBook; bool mTakeButtonShow; bool mTakeButtonAllowed; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/charactercreation.cpp000066400000000000000000000656471264522266000242260ustar00rootroot00000000000000#include "charactercreation.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" #include "../mwworld/esmstore.hpp" #include "textinput.hpp" #include "race.hpp" #include "class.hpp" #include "birth.hpp" #include "review.hpp" #include "inventorywindow.hpp" namespace { struct Step { const std::string mText; const std::string mButtons[3]; const std::string mSound; }; const ESM::Class::Specialization mSpecializations[3]={ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}; // The specialization for each answer Step sGenerateClassSteps(int number) { number++; const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); Step step = {fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_Question"), {fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerOne"), fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerTwo"), fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerThree")}, "vo\\misc\\chargen qa"+MyGUI::utility::toString(number)+".wav" }; return step; } struct ClassPoint { const char *id; // Specialization points to match, in order: Stealth, Combat, Magic // Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz unsigned int points[3]; }; void updatePlayerHealth() { MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); npcStats.updateHealth(); } } namespace MWGui { CharacterCreation::CharacterCreation(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem) : mViewer(viewer) , mResourceSystem(resourceSystem) , mNameDialog(0) , mRaceDialog(0) , mClassChoiceDialog(0) , mGenerateClassQuestionDialog(0) , mGenerateClassResultDialog(0) , mPickClassDialog(0) , mCreateClassDialog(0) , mBirthSignDialog(0) , mReviewDialog(0) , mGenerateClassStep(0) { mCreationStage = CSE_NotStarted; mGenerateClassSpecializations[0] = 0; mGenerateClassSpecializations[1] = 0; mGenerateClassSpecializations[2] = 0; } void CharacterCreation::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { if (mReviewDialog) { static const char *ids[] = { "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", "AttribVal6", "AttribVal7", "AttribVal8", 0 }; for (int i=0; ids[i]; ++i) { if (ids[i]==id) mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); } } } void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { if (mReviewDialog) { if (id == "HBar") { mReviewDialog->setHealth (value); } else if (id == "MBar") { mReviewDialog->setMagicka (value); } else if (id == "FBar") { mReviewDialog->setFatigue (value); } } } void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) { if (mReviewDialog) mReviewDialog->setSkillValue(parSkill, value); } void CharacterCreation::configureSkills (const SkillList& major, const SkillList& minor) { if (mReviewDialog) mReviewDialog->configureSkills(major, minor); } void CharacterCreation::spawnDialog(const char id) { try { switch (id) { case GM_Name: MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); mNameDialog = 0; mNameDialog = new TextInputDialog(); mNameDialog->setTextLabel(MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "Name")); mNameDialog->setTextInput(mPlayerName); mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); mNameDialog->setVisible(true); break; case GM_Race: MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); mRaceDialog = 0; mRaceDialog = new RaceDialog(mViewer, mResourceSystem); mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); mRaceDialog->setRaceId(mPlayerRaceId); mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); mRaceDialog->setVisible(true); if (mCreationStage < CSE_NameChosen) mCreationStage = CSE_NameChosen; break; case GM_Class: MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); mClassChoiceDialog = 0; mClassChoiceDialog = new ClassChoiceDialog(); mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); mClassChoiceDialog->setVisible(true); if (mCreationStage < CSE_RaceChosen) mCreationStage = CSE_RaceChosen; break; case GM_ClassPick: MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); mPickClassDialog = 0; mPickClassDialog = new PickClassDialog(); mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mPickClassDialog->setClassId(mPlayerClass.mName); mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); mPickClassDialog->setVisible(true); if (mCreationStage < CSE_RaceChosen) mCreationStage = CSE_RaceChosen; break; case GM_Birth: MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); mBirthSignDialog = 0; mBirthSignDialog = new BirthDialog(); mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); mBirthSignDialog->setBirthId(mPlayerBirthSignId); mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); mBirthSignDialog->setVisible(true); if (mCreationStage < CSE_ClassChosen) mCreationStage = CSE_ClassChosen; break; case GM_ClassCreate: if (!mCreateClassDialog) { mCreateClassDialog = new CreateClassDialog(); mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); } mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mCreateClassDialog->setVisible(true); if (mCreationStage < CSE_RaceChosen) mCreationStage = CSE_RaceChosen; break; case GM_ClassGenerate: mGenerateClassStep = 0; mGenerateClass = ""; mGenerateClassSpecializations[0] = 0; mGenerateClassSpecializations[1] = 0; mGenerateClassSpecializations[2] = 0; showClassQuestionDialog(); if (mCreationStage < CSE_RaceChosen) mCreationStage = CSE_RaceChosen; break; case GM_Review: MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; mReviewDialog = new ReviewDialog(); mReviewDialog->setPlayerName(mPlayerName); mReviewDialog->setRace(mPlayerRaceId); mReviewDialog->setClass(mPlayerClass); mReviewDialog->setBirthSign(mPlayerBirthSignId); { MWWorld::Ptr player = MWMechanics::getPlayer(); const MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); mReviewDialog->setHealth ( stats.getHealth() ); mReviewDialog->setMagicka( stats.getMagicka() ); mReviewDialog->setFatigue( stats.getFatigue() ); } { std::map attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); for (std::map::iterator it = attributes.begin(); it != attributes.end(); ++it) { mReviewDialog->setAttribute(static_cast (it->first), it->second); } } { std::map skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); for (std::map::iterator it = skills.begin(); it != skills.end(); ++it) { mReviewDialog->setSkillValue(static_cast (it->first), it->second); } mReviewDialog->configureSkills(MWBase::Environment::get().getWindowManager()->getPlayerMajorSkills(), MWBase::Environment::get().getWindowManager()->getPlayerMinorSkills()); } mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); mReviewDialog->setVisible(true); if (mCreationStage < CSE_BirthSignChosen) mCreationStage = CSE_BirthSignChosen; break; } } catch (std::exception& e) { std::cerr << "Failed to create chargen window: " << e.what() << std::endl; } } void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) { MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; MWBase::Environment::get().getWindowManager()->popGuiMode(); } void CharacterCreation::onReviewDialogBack() { MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; mCreationStage = CSE_ReviewBack; MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } void CharacterCreation::onReviewActivateDialog(int parDialog) { MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; mCreationStage = CSE_ReviewNext; MWBase::Environment::get().getWindowManager()->popGuiMode(); switch(parDialog) { case ReviewDialog::NAME_DIALOG: MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); break; case ReviewDialog::RACE_DIALOG: MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); break; case ReviewDialog::CLASS_DIALOG: MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); break; case ReviewDialog::BIRTHSIGN_DIALOG: MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); }; } void CharacterCreation::selectPickedClass() { if (mPickClassDialog) { const std::string &classId = mPickClassDialog->getClassId(); if (!classId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); const ESM::Class *klass = MWBase::Environment::get().getWorld()->getStore().get().find(classId); if (klass) { mPlayerClass = *klass; MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); } MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); mPickClassDialog = 0; } updatePlayerHealth(); } void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) { selectPickedClass(); handleDialogDone(CSE_ClassChosen, GM_Birth); } void CharacterCreation::onPickClassDialogBack() { selectPickedClass(); MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } void CharacterCreation::onClassChoice(int _index) { MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); mClassChoiceDialog = 0; MWBase::Environment::get().getWindowManager()->popGuiMode(); switch(_index) { case ClassChoiceDialog::Class_Generate: MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassGenerate); break; case ClassChoiceDialog::Class_Pick: MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassPick); break; case ClassChoiceDialog::Class_Create: MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassCreate); break; case ClassChoiceDialog::Class_Back: MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); break; }; } void CharacterCreation::onNameDialogDone(WindowBase* parWindow) { if (mNameDialog) { mPlayerName = mNameDialog->getTextInput(); MWBase::Environment::get().getWindowManager()->setValue("name", mPlayerName); MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); mNameDialog = 0; } handleDialogDone(CSE_NameChosen, GM_Race); } void CharacterCreation::selectRace() { if (mRaceDialog) { const ESM::NPC &data = mRaceDialog->getResult(); mPlayerRaceId = data.mRace; if (!mPlayerRaceId.empty()) { MWBase::Environment::get().getMechanicsManager()->setPlayerRace( data.mRace, data.isMale(), data.mHead, data.mHair ); } MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); mRaceDialog = 0; } updatePlayerHealth(); } void CharacterCreation::onRaceDialogBack() { selectRace(); MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); } void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) { selectRace(); handleDialogDone(CSE_RaceChosen, GM_Class); } void CharacterCreation::selectBirthSign() { if (mBirthSignDialog) { mPlayerBirthSignId = mBirthSignDialog->getBirthId(); if (!mPlayerBirthSignId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); mBirthSignDialog = 0; } updatePlayerHealth(); } void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) { selectBirthSign(); handleDialogDone(CSE_BirthSignChosen, GM_Review); } void CharacterCreation::onBirthSignDialogBack() { selectBirthSign(); MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } void CharacterCreation::selectCreatedClass() { if (mCreateClassDialog) { ESM::Class klass; klass.mName = mCreateClassDialog->getName(); klass.mDescription = mCreateClassDialog->getDescription(); klass.mData.mSpecialization = mCreateClassDialog->getSpecializationId(); klass.mData.mIsPlayable = 0x1; std::vector attributes = mCreateClassDialog->getFavoriteAttributes(); assert(attributes.size() == 2); klass.mData.mAttribute[0] = attributes[0]; klass.mData.mAttribute[1] = attributes[1]; std::vector majorSkills = mCreateClassDialog->getMajorSkills(); std::vector minorSkills = mCreateClassDialog->getMinorSkills(); assert(majorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); assert(minorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); for (size_t i = 0; i < sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]); ++i) { klass.mData.mSkills[i][1] = majorSkills[i]; klass.mData.mSkills[i][0] = minorSkills[i]; } MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); mPlayerClass = klass; MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); // Do not delete dialog, so that choices are remembered in case we want to go back and adjust them later mCreateClassDialog->setVisible(false); } updatePlayerHealth(); } void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) { selectCreatedClass(); handleDialogDone(CSE_ClassChosen, GM_Birth); } void CharacterCreation::onCreateClassDialogBack() { // not done in MW, but we do it for consistency with the other dialogs selectCreatedClass(); MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } void CharacterCreation::onClassQuestionChosen(int _index) { MWBase::Environment::get().getSoundManager()->stopSay(); MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); mGenerateClassQuestionDialog = 0; if (_index < 0 || _index >= 3) { MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); return; } ESM::Class::Specialization specialization = mSpecializations[_index]; if (specialization == ESM::Class::Stealth) ++mGenerateClassSpecializations[0]; else if (specialization == ESM::Class::Combat) ++mGenerateClassSpecializations[1]; else if (specialization == ESM::Class::Magic) ++mGenerateClassSpecializations[2]; ++mGenerateClassStep; showClassQuestionDialog(); } void CharacterCreation::showClassQuestionDialog() { if (mGenerateClassStep == 10) { static boost::array classes = { { {"Acrobat", {6, 2, 2}}, {"Agent", {6, 1, 3}}, {"Archer", {3, 5, 2}}, {"Archer", {5, 5, 0}}, {"Assassin", {6, 3, 1}}, {"Barbarian", {3, 6, 1}}, {"Bard", {3, 3, 3}}, {"Battlemage", {1, 3, 6}}, {"Crusader", {1, 6, 3}}, {"Healer", {3, 1, 6}}, {"Knight", {2, 6, 2}}, {"Monk", {5, 3, 2}}, {"Nightblade", {4, 2, 4}}, {"Pilgrim", {5, 2, 3}}, {"Rogue", {3, 4, 3}}, {"Rogue", {4, 4, 2}}, {"Rogue", {5, 4, 1}}, {"Scout", {2, 5, 3}}, {"Sorcerer", {2, 2, 6}}, {"Spellsword", {2, 4, 4}}, {"Spellsword", {5, 1, 4}}, {"Witchhunter", {2, 3, 5}}, {"Witchhunter", {5, 0, 5}} } }; int match = -1; for (unsigned i = 0; i < classes.size(); ++i) { if (mGenerateClassSpecializations[0] == classes[i].points[0] && mGenerateClassSpecializations[1] == classes[i].points[1] && mGenerateClassSpecializations[2] == classes[i].points[2]) { match = i; mGenerateClass = classes[i].id; break; } } if (match == -1) { if (mGenerateClassSpecializations[0] >= 7) mGenerateClass = "Thief"; else if (mGenerateClassSpecializations[1] >= 7) mGenerateClass = "Warrior"; else if (mGenerateClassSpecializations[2] >= 7) mGenerateClass = "Mage"; else { std::cerr << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; mGenerateClass = "Thief"; } } MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); mGenerateClassResultDialog = 0; mGenerateClassResultDialog = new GenerateClassResultDialog(); mGenerateClassResultDialog->setClassId(mGenerateClass); mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); mGenerateClassResultDialog->setVisible(true); return; } if (mGenerateClassStep > 10) { MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); return; } MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); mGenerateClassQuestionDialog = 0; mGenerateClassQuestionDialog = new InfoBoxDialog(); InfoBoxDialog::ButtonList buttons; mGenerateClassQuestionDialog->setText(sGenerateClassSteps(mGenerateClassStep).mText); buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[0]); buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[1]); buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[2]); mGenerateClassQuestionDialog->setButtons(buttons); mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); mGenerateClassQuestionDialog->setVisible(true); MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); } void CharacterCreation::selectGeneratedClass() { MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); mGenerateClassResultDialog = 0; MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); const ESM::Class *klass = MWBase::Environment::get().getWorld()->getStore().get().find(mGenerateClass); mPlayerClass = *klass; MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); updatePlayerHealth(); } void CharacterCreation::onGenerateClassBack() { selectGeneratedClass(); MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) { selectGeneratedClass(); handleDialogDone(CSE_ClassChosen, GM_Birth); } CharacterCreation::~CharacterCreation() { delete mNameDialog; delete mRaceDialog; delete mClassChoiceDialog; delete mGenerateClassQuestionDialog; delete mGenerateClassResultDialog; delete mPickClassDialog; delete mCreateClassDialog; delete mBirthSignDialog; delete mReviewDialog; } void CharacterCreation::handleDialogDone(CSE currentStage, int nextMode) { MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= currentStage) { MWBase::Environment::get().getWindowManager()->pushGuiMode((GuiMode)nextMode); } else { mCreationStage = currentStage; } } } openmw-openmw-0.38.0/apps/openmw/mwgui/charactercreation.hpp000066400000000000000000000070301264522266000242110ustar00rootroot00000000000000#ifndef CHARACTER_CREATION_HPP #define CHARACTER_CREATION_HPP #include #include #include #include "../mwmechanics/stat.hpp" namespace osgViewer { class Viewer; } namespace Resource { class ResourceSystem; } namespace MWGui { class WindowBase; class TextInputDialog; class InfoBoxDialog; class RaceDialog; class DialogueWindow; class ClassChoiceDialog; class GenerateClassResultDialog; class PickClassDialog; class CreateClassDialog; class BirthDialog; class ReviewDialog; class MessageBoxManager; class CharacterCreation { public: typedef std::vector SkillList; CharacterCreation(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem); ~CharacterCreation(); //Show a dialog void spawnDialog(const char id); void setValue (const std::string& id, const MWMechanics::AttributeValue& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); void configureSkills (const SkillList& major, const SkillList& minor); private: osgViewer::Viewer* mViewer; Resource::ResourceSystem* mResourceSystem; //Dialogs TextInputDialog* mNameDialog; RaceDialog* mRaceDialog; ClassChoiceDialog* mClassChoiceDialog; InfoBoxDialog* mGenerateClassQuestionDialog; GenerateClassResultDialog* mGenerateClassResultDialog; PickClassDialog* mPickClassDialog; CreateClassDialog* mCreateClassDialog; BirthDialog* mBirthSignDialog; ReviewDialog* mReviewDialog; //Player data std::string mPlayerName; std::string mPlayerRaceId; std::string mPlayerBirthSignId; ESM::Class mPlayerClass; //Class generation vars unsigned mGenerateClassStep; // Keeps track of current step in Generate Class dialog unsigned mGenerateClassSpecializations[3]; // A counter for each specialization which is increased when an answer is chosen std::string mGenerateClass; // In order: Stealth, Combat, Magic ////Dialog events //Name dialog void onNameDialogDone(WindowBase* parWindow); //Race dialog void onRaceDialogDone(WindowBase* parWindow); void onRaceDialogBack(); void selectRace(); //Class dialogs void onClassChoice(int _index); void onPickClassDialogDone(WindowBase* parWindow); void onPickClassDialogBack(); void onCreateClassDialogDone(WindowBase* parWindow); void onCreateClassDialogBack(); void showClassQuestionDialog(); void onClassQuestionChosen(int _index); void onGenerateClassBack(); void onGenerateClassDone(WindowBase* parWindow); void selectGeneratedClass(); void selectCreatedClass(); void selectPickedClass(); //Birthsign dialog void onBirthSignDialogDone(WindowBase* parWindow); void onBirthSignDialogBack(); void selectBirthSign(); //Review dialog void onReviewDialogDone(WindowBase* parWindow); void onReviewDialogBack(); void onReviewActivateDialog(int parDialog); enum CSE //Creation Stage Enum { CSE_NotStarted, CSE_NameChosen, CSE_RaceChosen, CSE_ClassChosen, CSE_BirthSignChosen, CSE_ReviewBack, CSE_ReviewNext }; CSE mCreationStage; // Which state the character creating is in, controls back/next/ok buttons void handleDialogDone(CSE currentStage, int nextMode); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/class.cpp000066400000000000000000000766211264522266000216440ustar00rootroot00000000000000#include "class.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/esmstore.hpp" #include "tooltips.hpp" namespace { bool sortClasses(const std::pair& left, const std::pair& right) { return left.second.compare(right.second) < 0; } } namespace MWGui { /* GenerateClassResultDialog */ GenerateClassResultDialog::GenerateClassResultDialog() : WindowModal("openmw_chargen_generate_class_result.layout") { setText("ReflectT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sMessageQuestionAnswer1", "")); getWidget(mClassImage, "ClassImage"); getWidget(mClassName, "ClassName"); MyGUI::Button* backButton; getWidget(backButton, "BackButton"); backButton->setCaptionWithReplacing("#{sMessageQuestionAnswer3}"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->setCaptionWithReplacing("#{sMessageQuestionAnswer2}"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); center(); } std::string GenerateClassResultDialog::getClassId() const { return mClassName->getCaption(); } void GenerateClassResultDialog::setClassId(const std::string &classId) { mCurrentClassId = classId; mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().get().find(mCurrentClassId)->mName); center(); } // widget controls void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) { eventDone(this); } void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) { eventBack(); } /* PickClassDialog */ PickClassDialog::PickClassDialog() : WindowModal("openmw_chargen_class.layout") { // Centre dialog center(); getWidget(mSpecializationName, "SpecializationName"); getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); for(int i = 0; i < 5; i++) { char theIndex = '0'+i; getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); } getWidget(mClassList, "ClassList"); mClassList->setScrollVisible(true); mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onAccept); mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); getWidget(mClassImage, "ClassImage"); MyGUI::Button* backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); updateClasses(); updateStats(); } void PickClassDialog::setNextButtonShow(bool shown) { MyGUI::Button* okButton; getWidget(okButton, "OKButton"); if (shown) okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } void PickClassDialog::open() { WindowModal::open (); updateClasses(); updateStats(); } void PickClassDialog::setClassId(const std::string &classId) { mCurrentClassId = classId; mClassList->setIndexSelected(MyGUI::ITEM_NONE); size_t count = mClassList->getItemCount(); for (size_t i = 0; i < count; ++i) { if (Misc::StringUtils::ciEqual(*mClassList->getItemDataAt(i), classId)) { mClassList->setIndexSelected(i); break; } } updateStats(); } // widget controls void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) { if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) return; eventDone(this); } void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) { eventBack(); } void PickClassDialog::onAccept(MyGUI::ListBox* _sender, size_t _index) { onSelectClass(_sender, _index); if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) return; eventDone(this); } void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) { if (_index == MyGUI::ITEM_NONE) return; const std::string *classId = mClassList->getItemDataAt(_index); if (Misc::StringUtils::ciEqual(mCurrentClassId, *classId)) return; mCurrentClassId = *classId; updateStats(); } // update widget content void PickClassDialog::updateClasses() { mClassList->removeAllItems(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); std::vector > items; // class id, class name MWWorld::Store::iterator it = store.get().begin(); for (; it != store.get().end(); ++it) { bool playable = (it->mData.mIsPlayable != 0); if (!playable) // Only display playable classes continue; items.push_back(std::make_pair(it->mId, it->mName)); } std::sort(items.begin(), items.end(), sortClasses); int index = 0; for (std::vector >::const_iterator it = items.begin(); it != items.end(); ++it) { const std::string &id = it->first; mClassList->addItem(it->second, id); if (mCurrentClassId.empty()) { mCurrentClassId = id; mClassList->setIndexSelected(index); } else if (Misc::StringUtils::ciEqual(id, mCurrentClassId)) { mClassList->setIndexSelected(index); } ++index; } } void PickClassDialog::updateStats() { if (mCurrentClassId.empty()) return; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Class *klass = store.get().search(mCurrentClassId); if (!klass) return; ESM::Class::Specialization specialization = static_cast(klass->mData.mSpecialization); static const char *specIds[3] = { "sSpecializationCombat", "sSpecializationMagic", "sSpecializationStealth" }; std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[specialization], specIds[specialization]); mSpecializationName->setCaption(specName); ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); mFavoriteAttribute[0]->setAttributeId(klass->mData.mAttribute[0]); mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]); ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); for (int i = 0; i < 5; ++i) { mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]); mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]); ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]); ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]); } mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); } /* InfoBoxDialog */ void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) { MyGUI::IntCoord inner = widget->getTextRegion(); MyGUI::IntCoord outer = widget->getCoord(); MyGUI::IntSize size = widget->getTextSize(); size.width += outer.width - inner.width; size.height += outer.height - inner.height; widget->setSize(size); } void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin) { size_t count = widget->getChildCount(); int pos = 0; pos += margin; int width = 0; for (unsigned i = 0; i < count; ++i) { MyGUI::Widget* child = widget->getChildAt(i); if (!child->getVisible()) continue; child->setPosition(child->getLeft(), pos); width = std::max(width, child->getWidth()); pos += child->getHeight() + margin; } width += margin*2; widget->setSize(width, pos); } InfoBoxDialog::InfoBoxDialog() : WindowModal("openmw_infobox.layout") { getWidget(mTextBox, "TextBox"); getWidget(mText, "Text"); mText->getSubWidgetText()->setWordWrap(true); getWidget(mButtonBar, "ButtonBar"); center(); } void InfoBoxDialog::setText(const std::string &str) { mText->setCaption(str); mTextBox->setVisible(!str.empty()); fitToText(mText); } std::string InfoBoxDialog::getText() const { return mText->getCaption(); } void InfoBoxDialog::setButtons(ButtonList &buttons) { for (std::vector::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } this->mButtons.clear(); // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget MyGUI::Button* button; MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10); ButtonList::const_iterator end = buttons.end(); for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it) { const std::string &text = *it; button = mButtonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); button->getSubWidgetText()->setWordWrap(true); button->setCaption(text); fitToText(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked); coord.top += button->getHeight(); this->mButtons.push_back(button); } } void InfoBoxDialog::open() { WindowModal::open(); // Fix layout layoutVertically(mTextBox, 4); layoutVertically(mButtonBar, 6); layoutVertically(mMainWidget, 4 + 6); center(); } void InfoBoxDialog::onButtonClicked(MyGUI::Widget* _sender) { std::vector::const_iterator end = mButtons.end(); int i = 0; for (std::vector::const_iterator it = mButtons.begin(); it != end; ++it) { if (*it == _sender) { eventButtonSelected(i); return; } ++i; } } /* ClassChoiceDialog */ ClassChoiceDialog::ClassChoiceDialog() : InfoBoxDialog() { setText(""); ButtonList buttons; buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu1", "")); buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu2", "")); buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu3", "")); buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBack", "")); setButtons(buttons); } /* CreateClassDialog */ CreateClassDialog::CreateClassDialog() : WindowModal("openmw_chargen_create_class.layout") , mSpecDialog(NULL) , mAttribDialog(NULL) , mSkillDialog(NULL) , mDescDialog(NULL) , mAffectedAttribute(NULL) , mAffectedSkill(NULL) { // Centre dialog center(); setText("SpecializationT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu1", "Specialization")); getWidget(mSpecializationName, "SpecializationName"); mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); setText("FavoriteAttributesT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); getWidget(mFavoriteAttribute0, "FavoriteAttribute0"); getWidget(mFavoriteAttribute1, "FavoriteAttribute1"); mFavoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); mFavoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); setText("MajorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMajor", "")); setText("MinorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMinor", "")); for(int i = 0; i < 5; i++) { char theIndex = '0'+i; getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); mSkills.push_back(mMajorSkill[i]); mSkills.push_back(mMinorSkill[i]); } std::vector::const_iterator end = mSkills.end(); for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) { (*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); } setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "")); getWidget(mEditName, "EditName"); // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mEditName); MyGUI::Button* descriptionButton; getWidget(descriptionButton, "DescriptionButton"); descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); MyGUI::Button* backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked); // Set default skills, attributes mFavoriteAttribute0->setAttributeId(ESM::Attribute::Strength); mFavoriteAttribute1->setAttributeId(ESM::Attribute::Agility); mMajorSkill[0]->setSkillId(ESM::Skill::Block); mMajorSkill[1]->setSkillId(ESM::Skill::Armorer); mMajorSkill[2]->setSkillId(ESM::Skill::MediumArmor); mMajorSkill[3]->setSkillId(ESM::Skill::HeavyArmor); mMajorSkill[4]->setSkillId(ESM::Skill::BluntWeapon); mMinorSkill[0]->setSkillId(ESM::Skill::LongBlade); mMinorSkill[1]->setSkillId(ESM::Skill::Axe); mMinorSkill[2]->setSkillId(ESM::Skill::Spear); mMinorSkill[3]->setSkillId(ESM::Skill::Athletics); mMinorSkill[4]->setSkillId(ESM::Skill::Enchant); setSpecialization(0); update(); } CreateClassDialog::~CreateClassDialog() { delete mSpecDialog; delete mAttribDialog; delete mSkillDialog; delete mDescDialog; } void CreateClassDialog::update() { for (int i = 0; i < 5; ++i) { ToolTips::createSkillToolTip(mMajorSkill[i], mMajorSkill[i]->getSkillId()); ToolTips::createSkillToolTip(mMinorSkill[i], mMinorSkill[i]->getSkillId()); } ToolTips::createAttributeToolTip(mFavoriteAttribute0, mFavoriteAttribute0->getAttributeId()); ToolTips::createAttributeToolTip(mFavoriteAttribute1, mFavoriteAttribute1->getAttributeId()); } std::string CreateClassDialog::getName() const { return mEditName->getCaption(); } std::string CreateClassDialog::getDescription() const { return mDescription; } ESM::Class::Specialization CreateClassDialog::getSpecializationId() const { return mSpecializationId; } std::vector CreateClassDialog::getFavoriteAttributes() const { std::vector v; v.push_back(mFavoriteAttribute0->getAttributeId()); v.push_back(mFavoriteAttribute1->getAttributeId()); return v; } std::vector CreateClassDialog::getMajorSkills() const { std::vector v; for(int i = 0; i < 5; i++) { v.push_back(mMajorSkill[i]->getSkillId()); } return v; } std::vector CreateClassDialog::getMinorSkills() const { std::vector v; for(int i=0; i < 5; i++) { v.push_back(mMinorSkill[i]->getSkillId()); } return v; } void CreateClassDialog::setNextButtonShow(bool shown) { MyGUI::Button* okButton; getWidget(okButton, "OKButton"); if (shown) okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } // widget controls void CreateClassDialog::onDialogCancel() { MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); mSpecDialog = 0; MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); mAttribDialog = 0; MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); mSkillDialog = 0; MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); mDescDialog = 0; } void CreateClassDialog::onSpecializationClicked(MyGUI::Widget* _sender) { delete mSpecDialog; mSpecDialog = new SelectSpecializationDialog(); mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); mSpecDialog->setVisible(true); } void CreateClassDialog::onSpecializationSelected() { mSpecializationId = mSpecDialog->getSpecializationId(); setSpecialization(mSpecializationId); MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); mSpecDialog = 0; } void CreateClassDialog::setSpecialization(int id) { mSpecializationId = (ESM::Class::Specialization) id; static const char *specIds[3] = { "sSpecializationCombat", "sSpecializationMagic", "sSpecializationStealth" }; std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); mSpecializationName->setCaption(specName); ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId); } void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) { delete mAttribDialog; mAttribDialog = new SelectAttributeDialog(); mAffectedAttribute = _sender; mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); mAttribDialog->setVisible(true); } void CreateClassDialog::onAttributeSelected() { ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId(); if (mAffectedAttribute == mFavoriteAttribute0) { if (mFavoriteAttribute1->getAttributeId() == id) mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId()); } else if (mAffectedAttribute == mFavoriteAttribute1) { if (mFavoriteAttribute0->getAttributeId() == id) mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId()); } mAffectedAttribute->setAttributeId(id); MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); mAttribDialog = 0; update(); } void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) { delete mSkillDialog; mSkillDialog = new SelectSkillDialog(); mAffectedSkill = _sender; mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); mSkillDialog->setVisible(true); } void CreateClassDialog::onSkillSelected() { ESM::Skill::SkillEnum id = mSkillDialog->getSkillId(); // Avoid duplicate skills by swapping any skill field that matches the selected one std::vector::const_iterator end = mSkills.end(); for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) { if (*it == mAffectedSkill) continue; if ((*it)->getSkillId() == id) { (*it)->setSkillId(mAffectedSkill->getSkillId()); break; } } mAffectedSkill->setSkillId(mSkillDialog->getSkillId()); MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); mSkillDialog = 0; update(); } void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) { mDescDialog = new DescriptionDialog(); mDescDialog->setTextInput(mDescription); mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); mDescDialog->setVisible(true); } void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow) { mDescription = mDescDialog->getTextInput(); MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); mDescDialog = 0; } void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender) { if(getName().size() <= 0) return; eventDone(this); } void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) { eventBack(); } /* SelectSpecializationDialog */ SelectSpecializationDialog::SelectSpecializationDialog() : WindowModal("openmw_chargen_select_specialization.layout") { // Centre dialog center(); getWidget(mSpecialization0, "Specialization0"); getWidget(mSpecialization1, "Specialization1"); getWidget(mSpecialization2, "Specialization2"); std::string combat = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Combat], ""); std::string magic = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Magic], ""); std::string stealth = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Stealth], ""); mSpecialization0->setCaption(combat); mSpecialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); mSpecialization1->setCaption(magic); mSpecialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); mSpecialization2->setCaption(stealth); mSpecialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); mSpecializationId = ESM::Class::Combat; ToolTips::createSpecializationToolTip(mSpecialization0, combat, ESM::Class::Combat); ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic); ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth); MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); } SelectSpecializationDialog::~SelectSpecializationDialog() { } // widget controls void SelectSpecializationDialog::onSpecializationClicked(MyGUI::Widget* _sender) { if (_sender == mSpecialization0) mSpecializationId = ESM::Class::Combat; else if (_sender == mSpecialization1) mSpecializationId = ESM::Class::Magic; else if (_sender == mSpecialization2) mSpecializationId = ESM::Class::Stealth; else return; eventItemSelected(); } void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) { exit(); } void SelectSpecializationDialog::exit() { eventCancel(); } /* SelectAttributeDialog */ SelectAttributeDialog::SelectAttributeDialog() : WindowModal("openmw_chargen_select_attribute.layout") , mAttributeId(ESM::Attribute::Strength) { // Centre dialog center(); for (int i = 0; i < 8; ++i) { Widgets::MWAttributePtr attribute; char theIndex = '0'+i; getWidget(attribute, std::string("Attribute").append(1, theIndex)); attribute->setAttributeId(ESM::Attribute::sAttributeIds[i]); attribute->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked); ToolTips::createAttributeToolTip(attribute, attribute->getAttributeId()); } MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); } SelectAttributeDialog::~SelectAttributeDialog() { } // widget controls void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) { // TODO: Change MWAttribute to set and get AttributeID enum instead of int mAttributeId = static_cast(_sender->getAttributeId()); eventItemSelected(); } void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) { exit(); } void SelectAttributeDialog::exit() { eventCancel(); } /* SelectSkillDialog */ SelectSkillDialog::SelectSkillDialog() : WindowModal("openmw_chargen_select_skill.layout") , mSkillId(ESM::Skill::Block) { // Centre dialog center(); for(int i = 0; i < 9; i++) { char theIndex = '0'+i; getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex)); getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex)); getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex)); } struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} mSkills[3][9] = { { {mCombatSkill[0], ESM::Skill::Block}, {mCombatSkill[1], ESM::Skill::Armorer}, {mCombatSkill[2], ESM::Skill::MediumArmor}, {mCombatSkill[3], ESM::Skill::HeavyArmor}, {mCombatSkill[4], ESM::Skill::BluntWeapon}, {mCombatSkill[5], ESM::Skill::LongBlade}, {mCombatSkill[6], ESM::Skill::Axe}, {mCombatSkill[7], ESM::Skill::Spear}, {mCombatSkill[8], ESM::Skill::Athletics} }, { {mMagicSkill[0], ESM::Skill::Enchant}, {mMagicSkill[1], ESM::Skill::Destruction}, {mMagicSkill[2], ESM::Skill::Alteration}, {mMagicSkill[3], ESM::Skill::Illusion}, {mMagicSkill[4], ESM::Skill::Conjuration}, {mMagicSkill[5], ESM::Skill::Mysticism}, {mMagicSkill[6], ESM::Skill::Restoration}, {mMagicSkill[7], ESM::Skill::Alchemy}, {mMagicSkill[8], ESM::Skill::Unarmored} }, { {mStealthSkill[0], ESM::Skill::Security}, {mStealthSkill[1], ESM::Skill::Sneak}, {mStealthSkill[2], ESM::Skill::Acrobatics}, {mStealthSkill[3], ESM::Skill::LightArmor}, {mStealthSkill[4], ESM::Skill::ShortBlade}, {mStealthSkill[5] ,ESM::Skill::Marksman}, {mStealthSkill[6] ,ESM::Skill::Mercantile}, {mStealthSkill[7] ,ESM::Skill::Speechcraft}, {mStealthSkill[8] ,ESM::Skill::HandToHand} } }; for (int spec = 0; spec < 3; ++spec) { for (int i = 0; i < 9; ++i) { mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId); mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId()); } } MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); } SelectSkillDialog::~SelectSkillDialog() { } // widget controls void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender) { mSkillId = _sender->getSkillId(); eventItemSelected(); } void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) { exit(); } void SelectSkillDialog::exit() { eventCancel(); } /* DescriptionDialog */ DescriptionDialog::DescriptionDialog() : WindowModal("openmw_chargen_class_description.layout") { // Centre dialog center(); getWidget(mTextEdit, "TextEdit"); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sInputMenu1", "")); // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } DescriptionDialog::~DescriptionDialog() { } // widget controls void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender) { eventDone(this); } } openmw-openmw-0.38.0/apps/openmw/mwgui/class.hpp000066400000000000000000000220341264522266000216360ustar00rootroot00000000000000#ifndef MWGUI_CLASS_H #define MWGUI_CLASS_H #include #include #include "widgets.hpp" #include "windowbase.hpp" namespace MWGui { class InfoBoxDialog : public WindowModal { public: InfoBoxDialog(); typedef std::vector ButtonList; void setText(const std::string &str); std::string getText() const; void setButtons(ButtonList &buttons); virtual void open(); // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; /** Event : Button was clicked.\n signature : void method(int index)\n */ EventHandle_Int eventButtonSelected; protected: void onButtonClicked(MyGUI::Widget* _sender); private: void fitToText(MyGUI::TextBox* widget); void layoutVertically(MyGUI::Widget* widget, int margin); MyGUI::Widget* mTextBox; MyGUI::TextBox* mText; MyGUI::Widget* mButtonBar; std::vector mButtons; }; // Lets the player choose between 3 ways of creating a class class ClassChoiceDialog : public InfoBoxDialog { public: // Corresponds to the buttons that can be clicked enum ClassChoice { Class_Generate = 0, Class_Pick = 1, Class_Create = 2, Class_Back = 3 }; ClassChoiceDialog(); }; class GenerateClassResultDialog : public WindowModal { public: GenerateClassResultDialog(); std::string getClassId() const; void setClassId(const std::string &classId); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n */ EventHandle_Void eventBack; /** Event : Dialog finished, OK button clicked.\n signature : void method()\n */ EventHandle_WindowBase eventDone; protected: void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); private: MyGUI::ImageBox* mClassImage; MyGUI::TextBox* mClassName; std::string mCurrentClassId; }; class PickClassDialog : public WindowModal { public: PickClassDialog(); const std::string &getClassId() const { return mCurrentClassId; } void setClassId(const std::string &classId); void setNextButtonShow(bool shown); virtual void open(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n */ EventHandle_Void eventBack; /** Event : Dialog finished, OK button clicked.\n signature : void method()\n */ EventHandle_WindowBase eventDone; protected: void onSelectClass(MyGUI::ListBox* _sender, size_t _index); void onAccept(MyGUI::ListBox* _sender, size_t _index); void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); private: void updateClasses(); void updateStats(); MyGUI::ImageBox* mClassImage; MyGUI::ListBox* mClassList; MyGUI::TextBox* mSpecializationName; Widgets::MWAttributePtr mFavoriteAttribute[2]; Widgets::MWSkillPtr mMajorSkill[5]; Widgets::MWSkillPtr mMinorSkill[5]; std::string mCurrentClassId; }; class SelectSpecializationDialog : public WindowModal { public: SelectSpecializationDialog(); ~SelectSpecializationDialog(); virtual void exit(); ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Cancel button clicked.\n signature : void method()\n */ EventHandle_Void eventCancel; /** Event : Dialog finished, specialization selected.\n signature : void method()\n */ EventHandle_Void eventItemSelected; protected: void onSpecializationClicked(MyGUI::Widget* _sender); void onCancelClicked(MyGUI::Widget* _sender); private: MyGUI::TextBox *mSpecialization0, *mSpecialization1, *mSpecialization2; ESM::Class::Specialization mSpecializationId; }; class SelectAttributeDialog : public WindowModal { public: SelectAttributeDialog(); ~SelectAttributeDialog(); virtual void exit(); ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Cancel button clicked.\n signature : void method()\n */ EventHandle_Void eventCancel; /** Event : Dialog finished, attribute selected.\n signature : void method()\n */ EventHandle_Void eventItemSelected; protected: void onAttributeClicked(Widgets::MWAttributePtr _sender); void onCancelClicked(MyGUI::Widget* _sender); private: ESM::Attribute::AttributeID mAttributeId; }; class SelectSkillDialog : public WindowModal { public: SelectSkillDialog(); ~SelectSkillDialog(); virtual void exit(); ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Cancel button clicked.\n signature : void method()\n */ EventHandle_Void eventCancel; /** Event : Dialog finished, skill selected.\n signature : void method()\n */ EventHandle_Void eventItemSelected; protected: void onSkillClicked(Widgets::MWSkillPtr _sender); void onCancelClicked(MyGUI::Widget* _sender); private: Widgets::MWSkillPtr mCombatSkill[9]; Widgets::MWSkillPtr mMagicSkill[9]; Widgets::MWSkillPtr mStealthSkill[9]; ESM::Skill::SkillEnum mSkillId; }; class DescriptionDialog : public WindowModal { public: DescriptionDialog(); ~DescriptionDialog(); std::string getTextInput() const { return mTextEdit->getCaption(); } void setTextInput(const std::string &text) { mTextEdit->setCaption(text); } /** Event : Dialog finished, OK button clicked.\n signature : void method()\n */ EventHandle_WindowBase eventDone; protected: void onOkClicked(MyGUI::Widget* _sender); private: MyGUI::EditBox* mTextEdit; }; class CreateClassDialog : public WindowModal { public: CreateClassDialog(); virtual ~CreateClassDialog(); std::string getName() const; std::string getDescription() const; ESM::Class::Specialization getSpecializationId() const; std::vector getFavoriteAttributes() const; std::vector getMajorSkills() const; std::vector getMinorSkills() const; void setNextButtonShow(bool shown); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n */ EventHandle_Void eventBack; /** Event : Dialog finished, OK button clicked.\n signature : void method()\n */ EventHandle_WindowBase eventDone; protected: void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); void onSpecializationClicked(MyGUI::Widget* _sender); void onSpecializationSelected(); void onAttributeClicked(Widgets::MWAttributePtr _sender); void onAttributeSelected(); void onSkillClicked(Widgets::MWSkillPtr _sender); void onSkillSelected(); void onDescriptionClicked(MyGUI::Widget* _sender); void onDescriptionEntered(WindowBase* parWindow); void onDialogCancel(); void setSpecialization(int id); void update(); private: MyGUI::EditBox* mEditName; MyGUI::TextBox* mSpecializationName; Widgets::MWAttributePtr mFavoriteAttribute0, mFavoriteAttribute1; Widgets::MWSkillPtr mMajorSkill[5]; Widgets::MWSkillPtr mMinorSkill[5]; std::vector mSkills; std::string mDescription; SelectSpecializationDialog *mSpecDialog; SelectAttributeDialog *mAttribDialog; SelectSkillDialog *mSkillDialog; DescriptionDialog *mDescDialog; ESM::Class::Specialization mSpecializationId; Widgets::MWAttributePtr mAffectedAttribute; Widgets::MWSkillPtr mAffectedSkill; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/companionitemmodel.cpp000066400000000000000000000026711264522266000244140ustar00rootroot00000000000000#include "companionitemmodel.hpp" #include "../mwworld/class.hpp" namespace { void modifyProfit(const MWWorld::Ptr& actor, int diff) { std::string script = actor.getClass().getScript(actor); if (!script.empty()) { int profit = actor.getRefData().getLocals().getIntVar(script, "minimumprofit"); profit += diff; actor.getRefData().getLocals().setVarByInt(script, "minimumprofit", profit); } } } namespace MWGui { CompanionItemModel::CompanionItemModel(const MWWorld::Ptr &actor) : InventoryItemModel(actor) { } MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) { if (hasProfit(mActor)) modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count); return InventoryItemModel::copyItem(item, count, setNewOwner); } void CompanionItemModel::removeItem (const ItemStack& item, size_t count) { if (hasProfit(mActor)) modifyProfit(mActor, -item.mBase.getClass().getValue(item.mBase) * count); InventoryItemModel::removeItem(item, count); } bool CompanionItemModel::hasProfit(const MWWorld::Ptr &actor) { std::string script = actor.getClass().getScript(actor); if (script.empty()) return false; return actor.getRefData().getLocals().hasVar(script, "minimumprofit"); } } openmw-openmw-0.38.0/apps/openmw/mwgui/companionitemmodel.hpp000066400000000000000000000012201264522266000244060ustar00rootroot00000000000000#ifndef MWGUI_COMPANION_ITEM_MODEL_H #define MWGUI_COMPANION_ITEM_MODEL_H #include "inventoryitemmodel.hpp" namespace MWGui { /// @brief The companion item model keeps track of the companion's profit by /// monitoring which items are being added to and removed from the model. class CompanionItemModel : public InventoryItemModel { public: CompanionItemModel (const MWWorld::Ptr& actor); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner); virtual void removeItem (const ItemStack& item, size_t count); bool hasProfit(const MWWorld::Ptr& actor); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/companionwindow.cpp000066400000000000000000000117751264522266000237510ustar00rootroot00000000000000#include "companionwindow.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "messagebox.hpp" #include "itemview.hpp" #include "sortfilteritemmodel.hpp" #include "companionitemmodel.hpp" #include "draganddrop.hpp" #include "countdialog.hpp" namespace { int getProfit(const MWWorld::Ptr& actor) { std::string script = actor.getClass().getScript(actor); if (!script.empty()) { return actor.getRefData().getLocals().getIntVar(script, "minimumprofit"); } return 0; } } namespace MWGui { CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* manager) : WindowBase("openmw_companion_window.layout") , mSortModel(NULL) , mModel(NULL) , mSelectedItem(-1) , mDragAndDrop(dragAndDrop) , mMessageBoxManager(manager) { getWidget(mCloseButton, "CloseButton"); getWidget(mProfitLabel, "ProfitLabel"); getWidget(mEncumbranceBar, "EncumbranceBar"); getWidget(mItemView, "ItemView"); mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &CompanionWindow::onBackgroundSelected); mItemView->eventItemClicked += MyGUI::newDelegate(this, &CompanionWindow::onItemSelected); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CompanionWindow::onCloseButtonClicked); setCoord(200,0,600,300); } void CompanionWindow::onItemSelected(int index) { if (mDragAndDrop->mIsOnDragAndDrop) { mDragAndDrop->drop(mModel, mItemView); updateEncumbranceBar(); return; } const ItemStack& item = mSortModel->getItem(index); // We can't take conjured items from a companion NPC if (item.mFlags & ItemStack::Flag_Bound) { MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}"); return; } MWWorld::Ptr object = item.mBase; int count = item.mCount; bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); if (MyGUI::InputManager::getInstance().isControlPressed()) count = 1; mSelectedItem = mSortModel->mapToSource(index); if (count > 1 && !shift) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); dialog->openCountDialog(object.getClass().getName(object), "#{sTake}", count); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem); } else dragItem (NULL, count); } void CompanionWindow::dragItem(MyGUI::Widget* sender, int count) { mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } void CompanionWindow::onBackgroundSelected() { if (mDragAndDrop->mIsOnDragAndDrop) { mDragAndDrop->drop(mModel, mItemView); updateEncumbranceBar(); } } void CompanionWindow::openCompanion(const MWWorld::Ptr& npc) { mPtr = npc; updateEncumbranceBar(); mModel = new CompanionItemModel(npc); mSortModel = new SortFilterItemModel(mModel); mItemView->setModel(mSortModel); mItemView->resetScrollBars(); setTitle(npc.getClass().getName(npc)); } void CompanionWindow::onFrame() { updateEncumbranceBar(); } void CompanionWindow::updateEncumbranceBar() { if (mPtr.isEmpty()) return; float capacity = mPtr.getClass().getCapacity(mPtr); float encumbrance = mPtr.getClass().getEncumbrance(mPtr); mEncumbranceBar->setValue(static_cast(encumbrance), static_cast(capacity)); if (mModel && mModel->hasProfit(mPtr)) { mProfitLabel->setCaptionWithReplacing("#{sProfitValue} " + MyGUI::utility::toString(getProfit(mPtr))); } else mProfitLabel->setCaption(""); } void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender) { exit(); } void CompanionWindow::exit() { if (mModel && mModel->hasProfit(mPtr) && getProfit(mPtr) < 0) { std::vector buttons; buttons.push_back("#{sCompanionWarningButtonOne}"); buttons.push_back("#{sCompanionWarningButtonTwo}"); mMessageBoxManager->createInteractiveMessageBox("#{sCompanionWarningMessage}", buttons); mMessageBoxManager->eventButtonPressed += MyGUI::newDelegate(this, &CompanionWindow::onMessageBoxButtonClicked); } else MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); } void CompanionWindow::onMessageBoxButtonClicked(int button) { if (button == 0) { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); // Important for Calvus' contract script to work properly MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } } void CompanionWindow::onReferenceUnavailable() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); } void CompanionWindow::resetReference() { ReferenceInterface::resetReference(); mItemView->setModel(NULL); mModel = NULL; mSortModel = NULL; } } openmw-openmw-0.38.0/apps/openmw/mwgui/companionwindow.hpp000066400000000000000000000024751264522266000237530ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_COMPANIONWINDOW_H #define OPENMW_MWGUI_COMPANIONWINDOW_H #include "widgets.hpp" #include "windowbase.hpp" #include "referenceinterface.hpp" namespace MWGui { class MessageBoxManager; class ItemView; class DragAndDrop; class SortFilterItemModel; class CompanionItemModel; class CompanionWindow : public WindowBase, public ReferenceInterface { public: CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager); virtual void exit(); virtual void resetReference(); void openCompanion(const MWWorld::Ptr& npc); void onFrame (); private: ItemView* mItemView; SortFilterItemModel* mSortModel; CompanionItemModel* mModel; int mSelectedItem; DragAndDrop* mDragAndDrop; MyGUI::Button* mCloseButton; MyGUI::TextBox* mProfitLabel; Widgets::MWDynamicStat* mEncumbranceBar; MessageBoxManager* mMessageBoxManager; void onItemSelected(int index); void onBackgroundSelected(); void dragItem(MyGUI::Widget* sender, int count); void onMessageBoxButtonClicked(int button); void updateEncumbranceBar(); void onCloseButtonClicked(MyGUI::Widget* _sender); virtual void onReferenceUnavailable(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/confirmationdialog.cpp000066400000000000000000000027631264522266000244030ustar00rootroot00000000000000#include "confirmationdialog.hpp" #include #include namespace MWGui { ConfirmationDialog::ConfirmationDialog() : WindowModal("openmw_confirmation_dialog.layout") { getWidget(mMessage, "Message"); getWidget(mOkButton, "OkButton"); getWidget(mCancelButton, "CancelButton"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onCancelButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onOkButtonClicked); } void ConfirmationDialog::askForConfirmation(const std::string& message, const std::string& confirmMessage, const std::string& cancelMessage) { setVisible(true); mMessage->setCaptionWithReplacing(message); mCancelButton->setCaptionWithReplacing(cancelMessage); mOkButton->setCaptionWithReplacing(confirmMessage); int height = mMessage->getTextSize().height + 72; mMainWidget->setSize(mMainWidget->getWidth(), height); mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height+24); center(); } void ConfirmationDialog::exit() { setVisible(false); eventCancelClicked(); } void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender) { exit(); } void ConfirmationDialog::onOkButtonClicked(MyGUI::Widget* _sender) { setVisible(false); eventOkClicked(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/confirmationdialog.hpp000066400000000000000000000017061264522266000244040ustar00rootroot00000000000000#ifndef MWGUI_CONFIRMATIONDIALOG_H #define MWGUI_CONFIRMATIONDIALOG_H #include "windowbase.hpp" namespace MWGui { class ConfirmationDialog : public WindowModal { public: ConfirmationDialog(); void askForConfirmation(const std::string& message, const std::string& confirmMessage="#{sOk}", const std::string& cancelMessage="#{sCancel}"); virtual void exit(); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Ok button was clicked.\n signature : void method()\n */ EventHandle_Void eventOkClicked; EventHandle_Void eventCancelClicked; private: MyGUI::EditBox* mMessage; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; void onCancelButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/console.cpp000066400000000000000000000347551264522266000222030ustar00rootroot00000000000000#include "console.hpp" #include #include #include #include #include #include "../mwscript/extensions.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" namespace MWGui { class ConsoleInterpreterContext : public MWScript::InterpreterContext { Console& mConsole; public: ConsoleInterpreterContext (Console& console, MWWorld::Ptr reference); virtual void report (const std::string& message); }; ConsoleInterpreterContext::ConsoleInterpreterContext (Console& console, MWWorld::Ptr reference) : MWScript::InterpreterContext ( reference.isEmpty() ? 0 : &reference.getRefData().getLocals(), reference), mConsole (console) {} void ConsoleInterpreterContext::report (const std::string& message) { mConsole.printOK (message); } bool Console::compile (const std::string& cmd, Compiler::Output& output) { try { ErrorHandler::reset(); std::istringstream input (cmd + '\n'); Compiler::Scanner scanner (*this, input, mCompilerContext.getExtensions()); Compiler::LineParser parser (*this, mCompilerContext, output.getLocals(), output.getLiterals(), output.getCode(), true); scanner.scan (parser); return isGood(); } catch (const Compiler::SourceException&) { // error has already been reported via error handler } catch (const std::exception& error) { printError (std::string ("Error: ") + error.what()); } return false; } void Console::report (const std::string& message, const Compiler::TokenLoc& loc, Type type) { std::ostringstream error; error << "column " << loc.mColumn << " (" << loc.mLiteral << "):"; printError (error.str()); printError ((type==ErrorMessage ? "error: " : "warning: ") + message); } void Console::report (const std::string& message, Type type) { printError ((type==ErrorMessage ? "error: " : "warning: ") + message); } void Console::listNames() { if (mNames.empty()) { // keywords std::istringstream input (""); Compiler::Scanner scanner (*this, input, mCompilerContext.getExtensions()); scanner.listKeywords (mNames); // identifier const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); for (MWWorld::ESMStore::iterator it = store.begin(); it != store.end(); ++it) { it->second->listIdentifier (mNames); } // exterior cell names aren't technically identifiers, but since the COC function accepts them, // we should list them too for (MWWorld::Store::iterator it = store.get().extBegin(); it != store.get().extEnd(); ++it) { if (!it->mName.empty()) mNames.push_back(it->mName); } // sort std::sort (mNames.begin(), mNames.end()); // remove duplicates mNames.erase( std::unique( mNames.begin(), mNames.end() ), mNames.end() ); } } Console::Console(int w, int h, bool consoleOnlyScripts) : WindowBase("openmw_console.layout"), mCompilerContext (MWScript::CompilerContext::Type_Console), mConsoleOnlyScripts (consoleOnlyScripts) { setCoord(10,10, w-10, h/2); getWidget(mCommandLine, "edit_Command"); getWidget(mHistory, "list_History"); // Set up the command line box mCommandLine->eventEditSelectAccept += newDelegate(this, &Console::acceptCommand); mCommandLine->eventKeyButtonPressed += newDelegate(this, &Console::keyPress); // Set up the log window mHistory->setOverflowToTheLeft(true); // compiler Compiler::registerExtensions (mExtensions, mConsoleOnlyScripts); mCompilerContext.setExtensions (&mExtensions); } void Console::open() { // Give keyboard focus to the combo box whenever the console is // turned on MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } void Console::close() { // Apparently, hidden widgets can retain key focus // Remove for MyGUI 3.2.2 MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); } void Console::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Console); } void Console::setFont(const std::string &fntName) { mHistory->setFontName(fntName); mCommandLine->setFontName(fntName); } void Console::print(const std::string &msg, const std::string& color) { mHistory->addText(color + MyGUI::TextIterator::toTagsString(msg)); } void Console::printOK(const std::string &msg) { print(msg + "\n", "#FF00FF"); } void Console::printError(const std::string &msg) { print(msg + "\n", "#FF2222"); } void Console::execute (const std::string& command) { // Log the command print("> " + command + "\n"); Compiler::Locals locals; Compiler::Output output (locals); if (compile (command + "\n", output)) { try { ConsoleInterpreterContext interpreterContext (*this, mPtr); Interpreter::Interpreter interpreter; MWScript::installOpcodes (interpreter, mConsoleOnlyScripts); std::vector code; output.getCode (code); interpreter.run (&code[0], code.size(), interpreterContext); } catch (const std::exception& error) { printError (std::string ("Error: ") + error.what()); } } } void Console::executeFile (const std::string& path) { namespace bfs = boost::filesystem; bfs::ifstream stream ((bfs::path(path))); if (!stream.is_open()) printError ("failed to open file: " + path); else { std::string line; while (std::getline (stream, line)) execute (line); } } void Console::keyPress(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char _char) { if( key == MyGUI::KeyCode::Tab) { std::vector matches; listNames(); std::string oldCaption = mCommandLine->getCaption(); std::string newCaption = complete( mCommandLine->getOnlyText(), matches ); mCommandLine->setCaption(newCaption); // List candidates if repeatedly pressing tab if (oldCaption == newCaption && matches.size()) { int i = 0; printOK(""); for(std::vector::iterator it=matches.begin(); it < matches.end(); ++it,++i ) { printOK( *it ); if( i == 50 ) break; } } } if(mCommandHistory.empty()) return; // Traverse history with up and down arrows if(key == MyGUI::KeyCode::ArrowUp) { // If the user was editing a string, store it for later if(mCurrent == mCommandHistory.end()) mEditString = mCommandLine->getOnlyText(); if(mCurrent != mCommandHistory.begin()) { --mCurrent; mCommandLine->setCaption(*mCurrent); } } else if(key == MyGUI::KeyCode::ArrowDown) { if(mCurrent != mCommandHistory.end()) { ++mCurrent; if(mCurrent != mCommandHistory.end()) mCommandLine->setCaption(*mCurrent); else // Restore the edit string mCommandLine->setCaption(mEditString); } } } void Console::acceptCommand(MyGUI::EditBox* _sender) { const std::string &cm = mCommandLine->getOnlyText(); if(cm.empty()) return; // Add the command to the history, and set the current pointer to // the end of the list if (mCommandHistory.empty() || mCommandHistory.back() != cm) mCommandHistory.push_back(cm); mCurrent = mCommandHistory.end(); mEditString.clear(); // Reset the command line before the command execution. // It prevents the re-triggering of the acceptCommand() event for the same command // during the actual command execution mCommandLine->setCaption(""); execute (cm); } std::string Console::complete( std::string input, std::vector &matches ) { std::string output = input; std::string tmp = input; bool has_front_quote = false; /* Does the input string contain things that don't have to be completed? If yes erase them. */ /* Are there quotation marks? */ if( tmp.find('"') != std::string::npos ) { int numquotes=0; for(std::string::iterator it=tmp.begin(); it < tmp.end(); ++it) { if( *it == '"' ) numquotes++; } /* Is it terminated?*/ if( numquotes % 2 ) { tmp.erase( 0, tmp.rfind('"')+1 ); has_front_quote = true; } else { size_t pos; if( ( ((pos = tmp.rfind(' ')) != std::string::npos ) ) && ( pos > tmp.rfind('"') ) ) { tmp.erase( 0, tmp.rfind(' ')+1); } else { tmp.clear(); } has_front_quote = false; } } /* No quotation marks. Are there spaces?*/ else { size_t rpos; if( (rpos=tmp.rfind(' ')) != std::string::npos ) { if( rpos == 0 ) { tmp.clear(); } else { tmp.erase(0, rpos+1); } } } /* Erase the input from the output string so we can easily append the completed form later. */ output.erase(output.end()-tmp.length(), output.end()); /* Is there still something in the input string? If not just display all commands and return the unchanged input. */ if( tmp.length() == 0 ) { matches=mNames; return input; } /* Iterate through the vector. */ for(std::vector::iterator it=mNames.begin(); it < mNames.end();++it) { bool string_different=false; /* Is the string shorter than the input string? If yes skip it. */ if( (*it).length() < tmp.length() ) continue; /* Is the beginning of the string different from the input string? If yes skip it. */ for( std::string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();++iter, ++iter2) { if( Misc::StringUtils::toLower(*iter) != Misc::StringUtils::toLower(*iter2) ) { string_different=true; break; } } if( string_different ) continue; /* The beginning of the string matches the input string, save it for the next test. */ matches.push_back(*it); } /* There are no matches. Return the unchanged input. */ if( matches.empty() ) { return input; } /* Only one match. We're done. */ if( matches.size() == 1 ) { /* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/ if( ( matches.front().find(' ') != std::string::npos ) ) { if( !has_front_quote ) output.append(std::string("\"")); return output.append(matches.front() + std::string("\" ")); } else if( has_front_quote ) { return output.append(matches.front() + std::string("\" ")); } else { return output.append(matches.front() + std::string(" ")); } } /* Check if all matching strings match further than input. If yes complete to this match. */ int i = tmp.length(); for(std::string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); ++iter, ++i) { for(std::vector::iterator it=matches.begin(); it < matches.end();++it) { if( Misc::StringUtils::toLower((*it)[i]) != Misc::StringUtils::toLower(*iter) ) { /* Append the longest match to the end of the output string*/ output.append(matches.front().substr( 0, i)); return output; } } } /* All keywords match with the shortest. Append it to the output string and return it. */ return output.append(matches.front()); } void Console::onResChange(int width, int height) { setCoord(10,10, width-10, height/2); } void Console::setSelectedObject(const MWWorld::Ptr& object) { if (!object.isEmpty()) { if (object == mPtr) { setTitle("#{sConsoleTitle}"); mPtr=MWWorld::Ptr(); } else { setTitle("#{sConsoleTitle} (" + object.getCellRef().getRefId() + ")"); mPtr = object; } // User clicked on an object. Restore focus to the console command line. MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } else { setTitle("#{sConsoleTitle}"); mPtr = MWWorld::Ptr(); } } void Console::onReferenceUnavailable() { setSelectedObject(MWWorld::Ptr()); } void Console::resetReference() { ReferenceInterface::resetReference(); setSelectedObject(MWWorld::Ptr()); } } openmw-openmw-0.38.0/apps/openmw/mwgui/console.hpp000066400000000000000000000062341264522266000221770ustar00rootroot00000000000000#ifndef MWGUI_CONSOLE_H #define MWGUI_CONSOLE_H #include #include #include #include #include #include #include #include #include #include #include "../mwscript/compilercontext.hpp" #include "../mwscript/interpretercontext.hpp" #include "referenceinterface.hpp" #include "windowbase.hpp" namespace MWGui { class Console : public WindowBase, private Compiler::ErrorHandler, public ReferenceInterface { public: /// Set the implicit object for script execution void setSelectedObject(const MWWorld::Ptr& object); MyGUI::EditBox* mCommandLine; MyGUI::EditBox* mHistory; typedef std::list StringList; // History of previous entered commands StringList mCommandHistory; StringList::iterator mCurrent; std::string mEditString; Console(int w, int h, bool consoleOnlyScripts); virtual void open(); virtual void close(); virtual void exit(); void setFont(const std::string &fntName); void onResChange(int width, int height); // Print a message to the console, in specified color. void print(const std::string &msg, const std::string& color = "#FFFFFF"); // These are pre-colored versions that you should use. /// Output from successful console command void printOK(const std::string &msg); /// Error message void printError(const std::string &msg); void execute (const std::string& command); void executeFile (const std::string& path); virtual void resetReference (); protected: virtual void onReferenceUnavailable(); private: void keyPress(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char _char); void acceptCommand(MyGUI::EditBox* _sender); std::string complete( std::string input, std::vector &matches ); Compiler::Extensions mExtensions; MWScript::CompilerContext mCompilerContext; std::vector mNames; bool mConsoleOnlyScripts; bool compile (const std::string& cmd, Compiler::Output& output); /// Report error to the user. virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type); /// Report a file related error virtual void report (const std::string& message, Type type); /// Write all valid identifiers and keywords into mNames and sort them. /// \note If mNames is not empty, this function is a no-op. /// \note The list may contain duplicates (if a name is a keyword and an identifier at the same /// time). void listNames(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/container.cpp000066400000000000000000000246321264522266000225140ustar00rootroot00000000000000#include "container.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/pickpocket.hpp" #include "../mwmechanics/creaturestats.hpp" #include "countdialog.hpp" #include "inventorywindow.hpp" #include "itemview.hpp" #include "itemwidget.hpp" #include "inventoryitemmodel.hpp" #include "sortfilteritemmodel.hpp" #include "pickpocketitemmodel.hpp" #include "draganddrop.hpp" namespace MWGui { ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) : WindowBase("openmw_container_window.layout") , mDragAndDrop(dragAndDrop) , mPickpocketDetected(false) , mSortModel(NULL) , mModel(NULL) , mSelectedItem(-1) { getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); getWidget(mTakeButton, "TakeButton"); getWidget(mCloseButton, "CloseButton"); getWidget(mItemView, "ItemView"); mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &ContainerWindow::onBackgroundSelected); mItemView->eventItemClicked += MyGUI::newDelegate(this, &ContainerWindow::onItemSelected); mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ContainerWindow::onKeyPressed); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); setCoord(200,0,600,300); } void ContainerWindow::onItemSelected(int index) { if (mDragAndDrop->mIsOnDragAndDrop) { if (!dynamic_cast(mModel)) dropItem(); return; } const ItemStack& item = mSortModel->getItem(index); // We can't take a conjured item from a container (some NPC we're pickpocketing, a box, etc) if (item.mFlags & ItemStack::Flag_Bound) { MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage1}"); return; } MWWorld::Ptr object = item.mBase; int count = item.mCount; bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); if (MyGUI::InputManager::getInstance().isControlPressed()) count = 1; mSelectedItem = mSortModel->mapToSource(index); if (count > 1 && !shift) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); dialog->openCountDialog(object.getClass().getName(object), "#{sTake}", count); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } else dragItem (NULL, count); } void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) { if (!onTakeItem(mModel->getItem(mSelectedItem), count)) return; mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } void ContainerWindow::dropItem() { if (mPtr.getTypeName() == typeid(ESM::Container).name()) { // check container organic flag MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->mBase->mFlags & ESM::Container::Organic) { MWBase::Environment::get().getWindowManager()-> messageBox("#{sContentsMessage2}"); return; } // check that we don't exceed container capacity MWWorld::Ptr item = mDragAndDrop->mItem.mBase; float weight = item.getClass().getWeight(item) * mDragAndDrop->mDraggedCount; if (mPtr.getClass().getCapacity(mPtr) < mPtr.getClass().getEncumbrance(mPtr) + weight) { MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); return; } } mDragAndDrop->drop(mModel, mItemView); } void ContainerWindow::onBackgroundSelected() { if (mDragAndDrop->mIsOnDragAndDrop && !dynamic_cast(mModel)) dropItem(); } void ContainerWindow::openContainer(const MWWorld::Ptr& container, bool loot) { mPickpocketDetected = false; mPtr = container; if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) { // we are stealing stuff MWWorld::Ptr player = MWMechanics::getPlayer(); mModel = new PickpocketItemModel(player, new InventoryItemModel(container), !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown()); } else mModel = new InventoryItemModel(container); mDisposeCorpseButton->setVisible(loot); mSortModel = new SortFilterItemModel(mModel); mItemView->setModel (mSortModel); mItemView->resetScrollBars(); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); setTitle(container.getClass().getName(container)); } void ContainerWindow::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) { if (_key == MyGUI::KeyCode::Space) onCloseButtonClicked(mCloseButton); if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter) onTakeAllButtonClicked(mTakeButton); } void ContainerWindow::resetReference() { ReferenceInterface::resetReference(); mItemView->setModel(NULL); mModel = NULL; mSortModel = NULL; } void ContainerWindow::close() { WindowBase::close(); if (dynamic_cast(mModel) // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) && !MWBase::Environment::get().getWindowManager()->containsMode(GM_Container) // If it was already detected while taking an item, no need to check now && !mPickpocketDetected ) { MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.finish()) { MWBase::Environment::get().getMechanicsManager()->commitCrime( player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); mPickpocketDetected = true; return; } } } void ContainerWindow::exit() { if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } } void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) { exit(); } void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) { if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { // transfer everything into the player's inventory ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); mModel->update(); for (size_t i=0; igetItemCount(); ++i) { if (i==0) { // play the sound of the first object MWWorld::Ptr item = mModel->getItem(i).mBase; std::string sound = item.getClass().getUpSoundId(item); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); } const ItemStack& item = mModel->getItem(i); if (!onTakeItem(item, item.mCount)) break; mModel->moveItem(item, item.mCount, playerModel); } MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } } void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) { if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { onTakeAllButtonClicked(mTakeButton); if (mPtr.getClass().isPersistent(mPtr)) MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); else MWBase::Environment::get().getWorld()->deleteObject(mPtr); mPtr = MWWorld::Ptr(); } } void ContainerWindow::onReferenceUnavailable() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } bool ContainerWindow::onTakeItem(const ItemStack &item, int count) { MWWorld::Ptr player = MWMechanics::getPlayer(); // TODO: move to ItemModels if (dynamic_cast(mModel) && !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown()) { MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.pick(item.mBase, count)) { int value = item.mBase.getClass().getValue(item.mBase) * count; MWBase::Environment::get().getMechanicsManager()->commitCrime( player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value, true); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); mPickpocketDetected = true; return false; } else player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1); } else { // Looting a dead corpse is considered OK if (mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead()) return true; else MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, mPtr, count); } return true; } } openmw-openmw-0.38.0/apps/openmw/mwgui/container.hpp000066400000000000000000000031731264522266000225160ustar00rootroot00000000000000#ifndef MGUI_CONTAINER_H #define MGUI_CONTAINER_H #include "windowbase.hpp" #include "referenceinterface.hpp" #include "itemmodel.hpp" namespace MWWorld { class Environment; } namespace MyGUI { class Gui; class Widget; } namespace MWGui { class WindowManager; class ContainerWindow; class ItemView; class SortFilterItemModel; } namespace MWGui { class ContainerWindow : public WindowBase, public ReferenceInterface { public: ContainerWindow(DragAndDrop* dragAndDrop); void openContainer(const MWWorld::Ptr& container, bool loot=false); virtual void close(); virtual void resetReference(); virtual void exit(); private: DragAndDrop* mDragAndDrop; bool mPickpocketDetected; MWGui::ItemView* mItemView; SortFilterItemModel* mSortModel; ItemModel* mModel; int mSelectedItem; MyGUI::Button* mDisposeCorpseButton; MyGUI::Button* mTakeButton; MyGUI::Button* mCloseButton; void onItemSelected(int index); void onBackgroundSelected(); void dragItem(MyGUI::Widget* sender, int count); void dropItem(); void onCloseButtonClicked(MyGUI::Widget* _sender); void onTakeAllButtonClicked(MyGUI::Widget* _sender); void onDisposeCorpseButtonClicked(MyGUI::Widget* sender); void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char); /// @return is taking the item allowed? bool onTakeItem(const ItemStack& item, int count); virtual void onReferenceUnavailable(); }; } #endif // CONTAINER_H openmw-openmw-0.38.0/apps/openmw/mwgui/containeritemmodel.cpp000066400000000000000000000124351264522266000244120ustar00rootroot00000000000000#include "containeritemmodel.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" namespace { bool stacks (const MWWorld::Ptr& left, const MWWorld::Ptr& right) { if (left == right) return true; // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure if (left.getContainerStore() && right.getContainerStore()) return left.getContainerStore()->stacks(left, right) && right.getContainerStore()->stacks(left, right); if (left.getContainerStore()) return left.getContainerStore()->stacks(left, right); if (right.getContainerStore()) return right.getContainerStore()->stacks(left, right); MWWorld::ContainerStore store; return store.stacks(left, right); } } namespace MWGui { ContainerItemModel::ContainerItemModel(const std::vector& itemSources, const std::vector& worldItems) : mItemSources(itemSources) , mWorldItems(worldItems) { assert (mItemSources.size()); } ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source) { mItemSources.push_back(source); } ItemStack ContainerItemModel::getItem (ModelIndex index) { if (index < 0) throw std::runtime_error("Invalid index supplied"); if (mItems.size() <= static_cast(index)) throw std::runtime_error("Item index out of range"); return mItems[index]; } size_t ContainerItemModel::getItemCount() { return mItems.size(); } ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item) { size_t i = 0; for (std::vector::iterator it = mItems.begin(); it != mItems.end(); ++it) { if (*it == item) return i; ++i; } return -1; } MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) { const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) throw std::runtime_error("Item to copy needs to be from a different container!"); return *source.getClass().getContainerStore(source).add(item.mBase, count, source); } void ContainerItemModel::removeItem (const ItemStack& item, size_t count) { int toRemove = count; for (std::vector::iterator source = mItemSources.begin(); source != mItemSources.end(); ++source) { MWWorld::ContainerStore& store = source->getClass().getContainerStore(*source); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { if (stacks(*it, item.mBase)) { toRemove -= store.remove(*it, toRemove, *source); if (toRemove <= 0) return; } } } for (std::vector::iterator source = mWorldItems.begin(); source != mWorldItems.end(); ++source) { if (stacks(*source, item.mBase)) { int refCount = source->getRefData().getCount(); if (refCount - toRemove <= 0) MWBase::Environment::get().getWorld()->deleteObject(*source); else source->getRefData().setCount(std::max(0, refCount - toRemove)); toRemove -= refCount; if (toRemove <= 0) return; } } throw std::runtime_error("Not enough items to remove could be found"); } void ContainerItemModel::update() { mItems.clear(); for (std::vector::iterator source = mItemSources.begin(); source != mItemSources.end(); ++source) { MWWorld::ContainerStore& store = source->getClass().getContainerStore(*source); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { std::vector::iterator itemStack = mItems.begin(); for (; itemStack != mItems.end(); ++itemStack) { if (stacks(*it, itemStack->mBase)) { // we already have an item stack of this kind, add to it itemStack->mCount += it->getRefData().getCount(); break; } } if (itemStack == mItems.end()) { // no stack yet, create one ItemStack newItem (*it, this, it->getRefData().getCount()); mItems.push_back(newItem); } } } for (std::vector::iterator source = mWorldItems.begin(); source != mWorldItems.end(); ++source) { std::vector::iterator itemStack = mItems.begin(); for (; itemStack != mItems.end(); ++itemStack) { if (stacks(*source, itemStack->mBase)) { // we already have an item stack of this kind, add to it itemStack->mCount += source->getRefData().getCount(); break; } } if (itemStack == mItems.end()) { // no stack yet, create one ItemStack newItem (*source, this, source->getRefData().getCount()); mItems.push_back(newItem); } } } } openmw-openmw-0.38.0/apps/openmw/mwgui/containeritemmodel.hpp000066400000000000000000000023221264522266000244110ustar00rootroot00000000000000#ifndef MWGUI_CONTAINER_ITEM_MODEL_H #define MWGUI_CONTAINER_ITEM_MODEL_H #include "itemmodel.hpp" namespace MWGui { /// @brief The container item model supports multiple item sources, which are needed for /// making NPCs sell items from containers owned by them class ContainerItemModel : public ItemModel { public: ContainerItemModel (const std::vector& itemSources, const std::vector& worldItems); ///< @note The order of elements \a itemSources matters here. The first element has the highest priority for removal, /// while the last element will be used to add new items to. ContainerItemModel (const MWWorld::Ptr& source); virtual ItemStack getItem (ModelIndex index); virtual ModelIndex getIndex (ItemStack item); virtual size_t getItemCount(); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); virtual void update(); private: std::vector mItemSources; std::vector mWorldItems; std::vector mItems; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/controllers.cpp000066400000000000000000000031361264522266000230740ustar00rootroot00000000000000#include "controllers.hpp" #include #include namespace MWGui { namespace Controllers { ControllerRepeatEvent::ControllerRepeatEvent() : mInit(0.5f), mStep(0.1f), mEnabled(true), mTimeLeft(0) { } ControllerRepeatEvent::~ControllerRepeatEvent() { } bool ControllerRepeatEvent::addTime(MyGUI::Widget* _widget, float _time) { if(mTimeLeft == 0) mTimeLeft = mInit; mTimeLeft -= _time; while (mTimeLeft <= 0) { mTimeLeft += mStep; eventRepeatClick(_widget, this); } return true; } void ControllerRepeatEvent::setRepeat(float init, float step) { mInit = init; mStep = step; } void ControllerRepeatEvent::setEnabled(bool enable) { mEnabled = enable; } void ControllerRepeatEvent::setProperty(const std::string& _key, const std::string& _value) { } void ControllerRepeatEvent::prepareItem(MyGUI::Widget* _widget) { } // ------------------------------------------------------------- void ControllerFollowMouse::prepareItem(MyGUI::Widget *_widget) { } bool ControllerFollowMouse::addTime(MyGUI::Widget *_widget, float _time) { _widget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); return true; } } } openmw-openmw-0.38.0/apps/openmw/mwgui/controllers.hpp000066400000000000000000000033111264522266000230740ustar00rootroot00000000000000#ifndef MWGUI_CONTROLLERS_H #define MWGUI_CONTROLLERS_H #include #include namespace MyGUI { class Widget; } namespace MWGui { namespace Controllers { // Should be removed when upgrading to MyGUI 3.2.2 (current git), it has ControllerRepeatClick class ControllerRepeatEvent : public MyGUI::ControllerItem { MYGUI_RTTI_DERIVED( ControllerRepeatEvent ) public: ControllerRepeatEvent(); virtual ~ControllerRepeatEvent(); void setRepeat(float init, float step); void setEnabled(bool enable); virtual void setProperty(const std::string& _key, const std::string& _value); // Events typedef MyGUI::delegates::CMultiDelegate2 EventHandle_RepeatClickVoid; /** Event : Repeat Click.\n signature : void method(MyGUI::Widget* _sender, MyGUI::ControllerItem *_controller)\n */ EventHandle_RepeatClickVoid eventRepeatClick; private: bool addTime(MyGUI::Widget* _widget, float _time); void prepareItem(MyGUI::Widget* _widget); private: float mInit; float mStep; bool mEnabled; float mTimeLeft; }; /// Automatically positions a widget below the mouse cursor. class ControllerFollowMouse : public MyGUI::ControllerItem { MYGUI_RTTI_DERIVED( ControllerFollowMouse ) private: bool addTime(MyGUI::Widget* _widget, float _time); void prepareItem(MyGUI::Widget* _widget); }; } } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/countdialog.cpp000066400000000000000000000060751264522266000230430ustar00rootroot00000000000000#include "countdialog.hpp" #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" namespace MWGui { CountDialog::CountDialog() : WindowModal("openmw_count_window.layout") { getWidget(mSlider, "CountSlider"); getWidget(mItemEdit, "ItemEdit"); getWidget(mItemText, "ItemText"); getWidget(mLabelText, "LabelText"); getWidget(mOkButton, "OkButton"); getWidget(mCancelButton, "CancelButton"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CountDialog::onCancelButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CountDialog::onOkButtonClicked); mItemEdit->eventValueChanged += MyGUI::newDelegate(this, &CountDialog::onEditValueChanged); mSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &CountDialog::onSliderMoved); // make sure we read the enter key being pressed to accept multiple items mItemEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &CountDialog::onEnterKeyPressed); } void CountDialog::openCountDialog(const std::string& item, const std::string& message, const int maxCount) { setVisible(true); mLabelText->setCaptionWithReplacing(message); MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); mSlider->setScrollRange(maxCount); mItemText->setCaption(item); int width = std::max(mItemText->getTextSize().width + 128, 320); setCoord(viewSize.width/2 - width/2, viewSize.height/2 - mMainWidget->getHeight()/2, width, mMainWidget->getHeight()); // by default, the text edit field has the focus of the keyboard MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mItemEdit); mSlider->setScrollPosition(maxCount-1); mItemEdit->setMinValue(1); mItemEdit->setMaxValue(maxCount); mItemEdit->setValue(maxCount); } void CountDialog::cancel() //Keeping this here as I don't know if anything else relies on it. { exit(); } void CountDialog::exit() { setVisible(false); } void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) { cancel(); } void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) { eventOkClicked(NULL, mSlider->getScrollPosition()+1); setVisible(false); } // essentially duplicating what the OK button does if user presses // Enter key void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) { eventOkClicked(NULL, mSlider->getScrollPosition()+1); setVisible(false); } void CountDialog::onEditValueChanged(int value) { mSlider->setScrollPosition(value-1); } void CountDialog::onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position) { mItemEdit->setValue(_position+1); } } openmw-openmw-0.38.0/apps/openmw/mwgui/countdialog.hpp000066400000000000000000000024071264522266000230430ustar00rootroot00000000000000#ifndef MWGUI_COUNTDIALOG_H #define MWGUI_COUNTDIALOG_H #include "windowbase.hpp" namespace Gui { class NumericEditBox; } namespace MWGui { class CountDialog : public WindowModal { public: CountDialog(); void openCountDialog(const std::string& item, const std::string& message, const int maxCount); void cancel(); virtual void exit(); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_WidgetInt; /** Event : Ok button was clicked.\n signature : void method(MyGUI::Widget* _sender, int _count)\n */ EventHandle_WidgetInt eventOkClicked; private: MyGUI::ScrollBar* mSlider; Gui::NumericEditBox* mItemEdit; MyGUI::TextBox* mItemText; MyGUI::TextBox* mLabelText; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; void onCancelButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender); void onEditValueChanged(int value); void onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position); void onEnterKeyPressed(MyGUI::EditBox* _sender); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/cursor.cpp000066400000000000000000000037751264522266000220540ustar00rootroot00000000000000#include "cursor.hpp" #include #include #include #include namespace MWGui { ResourceImageSetPointerFix::ResourceImageSetPointerFix() : mImageSet(NULL) , mRotation(0) { } ResourceImageSetPointerFix::~ResourceImageSetPointerFix() { } void ResourceImageSetPointerFix::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) { Base::deserialization(_node, _version); MyGUI::xml::ElementEnumerator info = _node->getElementEnumerator(); while (info.next("Property")) { const std::string& key = info->findAttribute("key"); const std::string& value = info->findAttribute("value"); if (key == "Point") mPoint = MyGUI::IntPoint::parse(value); else if (key == "Size") mSize = MyGUI::IntSize::parse(value); else if (key == "Rotation") mRotation = MyGUI::utility::parseInt(value); else if (key == "Resource") mImageSet = MyGUI::ResourceManager::getInstance().getByName(value)->castType(); } } int ResourceImageSetPointerFix::getRotation() { return mRotation; } void ResourceImageSetPointerFix::setImage(MyGUI::ImageBox* _image) { if (mImageSet != NULL) _image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0)); } void ResourceImageSetPointerFix::setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point) { _image->setCoord(_point.left - mPoint.left, _point.top - mPoint.top, mSize.width, mSize.height); } MyGUI::ResourceImageSetPtr ResourceImageSetPointerFix:: getImageSet() { return mImageSet; } MyGUI::IntPoint ResourceImageSetPointerFix::getHotSpot() { return mPoint; } MyGUI::IntSize ResourceImageSetPointerFix::getSize() { return mSize; } } openmw-openmw-0.38.0/apps/openmw/mwgui/cursor.hpp000066400000000000000000000026051264522266000220500ustar00rootroot00000000000000#ifndef MWGUI_CURSOR_H #define MWGUI_CURSOR_H #include #include namespace MWGui { /// \brief Allows us to get the members of /// ResourceImageSetPointer that we need. /// \example MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); /// MyGUI::ResourceManager::getInstance().load("core.xml"); class ResourceImageSetPointerFix : public MyGUI::IPointer { MYGUI_RTTI_DERIVED( ResourceImageSetPointerFix ) public: ResourceImageSetPointerFix(); virtual ~ResourceImageSetPointerFix(); virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version); virtual void setImage(MyGUI::ImageBox* _image); virtual void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point); //and now for the whole point of this class, allow us to get //the hot spot, the image and the size of the cursor. virtual MyGUI::ResourceImageSetPtr getImageSet(); virtual MyGUI::IntPoint getHotSpot(); virtual MyGUI::IntSize getSize(); virtual int getRotation(); private: MyGUI::IntPoint mPoint; MyGUI::IntSize mSize; MyGUI::ResourceImageSetPtr mImageSet; int mRotation; // rotation in degrees }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/debugwindow.cpp000066400000000000000000000076621264522266000230540ustar00rootroot00000000000000#include "debugwindow.hpp" #include #include #include #include #include namespace { void bulletDumpRecursive(CProfileIterator* pit, int spacing, std::stringstream& os) { pit->First(); if (pit->Is_Done()) return; float accumulated_time=0,parent_time = pit->Is_Root() ? CProfileManager::Get_Time_Since_Reset() : pit->Get_Current_Parent_Total_Time(); int i,j; int frames_since_reset = CProfileManager::Get_Frame_Count_Since_Reset(); for (i=0;iGet_Current_Parent_Name())+" (total running time: "+MyGUI::utility::toString(parent_time,3)+" ms) ---\n"; os << s; //float totalTime = 0.f; int numChildren = 0; for (i = 0; !pit->Is_Done(); i++,pit->Next()) { numChildren++; float current_total_time = pit->Get_Current_Total_Time(); accumulated_time += current_total_time; float fraction = parent_time > SIMD_EPSILON ? (current_total_time / parent_time) * 100 : 0.f; for (j=0;jGet_Current_Name()+" ("+MyGUI::utility::toString(fraction,2)+" %) :: "+MyGUI::utility::toString(ms,3)+" ms / frame ("+MyGUI::utility::toString(pit->Get_Current_Total_Calls())+" calls)\n"; os << s; //totalTime += current_total_time; //recurse into children } if (parent_time < accumulated_time) { os << "what's wrong\n"; } for (i=0;i SIMD_EPSILON ? ((parent_time - accumulated_time) / parent_time) * 100 : 0.f; s = "Unaccounted: ("+MyGUI::utility::toString(unaccounted,3)+" %) :: "+MyGUI::utility::toString(parent_time - accumulated_time,3)+" ms\n"; os << s; for (i=0;iEnter_Child(i); bulletDumpRecursive(pit, spacing+3, os); pit->Enter_Parent(); } } void bulletDumpAll(std::stringstream& os) { CProfileIterator* profileIterator = 0; profileIterator = CProfileManager::Get_Iterator(); bulletDumpRecursive(profileIterator, 0, os); CProfileManager::Release_Iterator(profileIterator); } } namespace MWGui { DebugWindow::DebugWindow() : WindowBase("openmw_debug_window.layout") { getWidget(mTabControl, "TabControl"); // Ideas for other tabs: // - Texture / compositor texture viewer // - Log viewer // - Material editor // - Shader editor MyGUI::TabItem* item = mTabControl->addItem("Physics Profiler"); mBulletProfilerEdit = item->createWidgetReal ("LogEdit", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Stretch); MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); mMainWidget->setSize(viewSize); } void DebugWindow::onFrame(float dt) { if (!isVisible()) return; static float timer = 0; timer -= dt; if (timer > 0) return; timer = 1; std::stringstream stream; bulletDumpAll(stream); if (mBulletProfilerEdit->isTextSelection()) // pause updating while user is trying to copy text return; size_t previousPos = mBulletProfilerEdit->getVScrollPosition(); mBulletProfilerEdit->setCaption(stream.str()); mBulletProfilerEdit->setVScrollPosition(std::min(previousPos, mBulletProfilerEdit->getVScrollRange()-1)); } } openmw-openmw-0.38.0/apps/openmw/mwgui/debugwindow.hpp000066400000000000000000000005371264522266000230530ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_DEBUGWINDOW_H #define OPENMW_MWGUI_DEBUGWINDOW_H #include "windowbase.hpp" namespace MWGui { class DebugWindow : public WindowBase { public: DebugWindow(); void onFrame(float dt); private: MyGUI::TabControl* mTabControl; MyGUI::EditBox* mBulletProfilerEdit; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/dialogue.cpp000066400000000000000000000620121264522266000223150ustar00rootroot00000000000000#include "dialogue.hpp" #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "widgets.hpp" #include "bookpage.hpp" #include "journalbooks.hpp" // to_utf8_span namespace { MyGUI::Colour getDialogueTextColour (const std::string& type) { return MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=" + type + "}")); } } namespace MWGui { PersuasionDialog::PersuasionDialog() : WindowModal("openmw_persuasion_dialog.layout") { getWidget(mCancelButton, "CancelButton"); getWidget(mAdmireButton, "AdmireButton"); getWidget(mIntimidateButton, "IntimidateButton"); getWidget(mTauntButton, "TauntButton"); getWidget(mBribe10Button, "Bribe10Button"); getWidget(mBribe100Button, "Bribe100Button"); getWidget(mBribe1000Button, "Bribe1000Button"); getWidget(mGoldLabel, "GoldLabel"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel); mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); } void PersuasionDialog::onCancel(MyGUI::Widget *sender) { exit(); } void PersuasionDialog::onPersuade(MyGUI::Widget *sender) { MWBase::MechanicsManager::PersuasionType type; if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; else if (sender == mBribe10Button) type = MWBase::MechanicsManager::PT_Bribe10; else if (sender == mBribe100Button) type = MWBase::MechanicsManager::PT_Bribe100; else /*if (sender == mBribe1000Button)*/ type = MWBase::MechanicsManager::PT_Bribe1000; MWBase::Environment::get().getDialogueManager()->persuade(type); setVisible(false); } void PersuasionDialog::open() { WindowModal::open(); center(); MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); mBribe10Button->setEnabled (playerGold >= 10); mBribe100Button->setEnabled (playerGold >= 100); mBribe1000Button->setEnabled (playerGold >= 1000); mGoldLabel->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); } void PersuasionDialog::exit() { setVisible(false); } // -------------------------------------------------------------------------------------------------- Response::Response(const std::string &text, const std::string &title) : mTitle(title) { mText = text; } void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const { BookTypesetter::Style* title = typesetter->createStyle("", getDialogueTextColour("header")); typesetter->sectionBreak(9); if (mTitle != "") typesetter->write(title, to_utf8_span(mTitle.c_str())); typesetter->sectionBreak(9); typedef std::pair Range; std::map hyperLinks; // We need this copy for when @# hyperlinks are replaced std::string text = mText; size_t pos_end; for(;;) { size_t pos_begin = text.find('@'); if (pos_begin != std::string::npos) pos_end = text.find('#', pos_begin); if (pos_begin != std::string::npos && pos_end != std::string::npos) { std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1); const char specialPseudoAsteriskCharacter = 127; std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*'); std::string topicName = MWBase::Environment::get().getWindowManager()-> getTranslationDataStorage().topicStandardForm(link); std::string displayName = link; while (displayName[displayName.size()-1] == '*') displayName.erase(displayName.size()-1, 1); text.replace(pos_begin, pos_end+1-pos_begin, displayName); if (topicLinks.find(Misc::StringUtils::lowerCase(topicName)) != topicLinks.end()) hyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = intptr_t(topicLinks[Misc::StringUtils::lowerCase(topicName)]); } else break; } typesetter->addContent(to_utf8_span(text.c_str())); if (hyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) { BookTypesetter::Style* style = typesetter->createStyle("", getDialogueTextColour("normal")); size_t formatted = 0; // points to the first character that is not laid out yet for (std::map::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it) { intptr_t topicId = it->second; const MyGUI::Colour linkHot(getDialogueTextColour("link_over")); const MyGUI::Colour linkNormal(getDialogueTextColour("link")); const MyGUI::Colour linkActive(getDialogueTextColour("link_pressed")); BookTypesetter::Style* hotStyle = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId); if (formatted < it->first.first) typesetter->write(style, formatted, it->first.first); typesetter->write(hotStyle, it->first.first, it->first.second); formatted = it->first.second; } if (formatted < text.size()) typesetter->write(style, formatted, text.size()); } else { std::vector matches; keywordSearch->highlightKeywords(text.begin(), text.end(), matches); std::string::const_iterator i = text.begin (); for (std::vector::iterator it = matches.begin(); it != matches.end(); ++it) { KeywordSearchT::Match match = *it; if (i != match.mBeg) addTopicLink (typesetter, 0, i - text.begin (), match.mBeg - text.begin ()); addTopicLink (typesetter, match.mValue, match.mBeg - text.begin (), match.mEnd - text.begin ()); i = match.mEnd; } if (i != text.end ()) addTopicLink (typesetter, 0, i - text.begin (), text.size ()); } } void Response::addTopicLink(BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const { BookTypesetter::Style* style = typesetter->createStyle("", getDialogueTextColour("normal")); const MyGUI::Colour linkHot(getDialogueTextColour("link_over")); const MyGUI::Colour linkNormal(getDialogueTextColour("link")); const MyGUI::Colour linkActive(getDialogueTextColour("link_pressed")); if (topicId) style = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId); typesetter->write (style, begin, end); } Message::Message(const std::string& text) { mText = text; } void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const { BookTypesetter::Style* title = typesetter->createStyle("", getDialogueTextColour("notify")); typesetter->sectionBreak(9); typesetter->write(title, to_utf8_span(mText.c_str())); } // -------------------------------------------------------------------------------------------------- void Choice::activated() { MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.0, 1.0); MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId); } void Topic::activated() { MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(mTopicId)); } void Goodbye::activated() { MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); } // -------------------------------------------------------------------------------------------------- DialogueWindow::DialogueWindow() : WindowBase("openmw_dialogue_window.layout") , mServices(0) , mEnabled(false) , mGoodbye(false) , mPersuasionDialog() { // Centre dialog center(); mPersuasionDialog.setVisible(false); //History view getWidget(mHistory, "History"); //Topics list getWidget(mTopicsList, "TopicsList"); mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); MyGUI::Button* byeButton; getWidget(byeButton, "ByeButton"); byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); getWidget(mDispositionBar, "Disposition"); getWidget(mDispositionText,"DispositionText"); getWidget(mScrollBar, "VScroll"); mScrollBar->eventScrollChangePosition += MyGUI::newDelegate(this, &DialogueWindow::onScrollbarMoved); mHistory->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel); BookPage::ClickCallback callback = boost::bind (&DialogueWindow::notifyLinkClicked, this, _1); mHistory->adviseLinkClicked(callback); mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); } void DialogueWindow::exit() { if ((!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) && !mGoodbye) { // in choice, not allowed to escape, but give access to main menu to allow loading other saves MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } else { MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); mTopicsList->scrollToTop(); } } void DialogueWindow::onWindowResize(MyGUI::Window* _sender) { mTopicsList->adjustSize(); updateHistory(); } void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (!mScrollBar->getVisible()) return; mScrollBar->setScrollPosition(std::min(static_cast(mScrollBar->getScrollRange()-1), std::max(0, static_cast(mScrollBar->getScrollPosition() - _rel*0.3)))); onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition()); } void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) { exit(); } void DialogueWindow::onSelectTopic(const std::string& topic, int id) { if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) return; int separatorPos = 0; for (unsigned int i=0; igetItemCount(); ++i) { if (mTopicsList->getItemNameAt(i) == "") separatorPos = i; } if (id >= separatorPos) MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(topic)); else { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); if (topic == gmst.find("sPersuasion")->getString()) mPersuasionDialog.setVisible(true); else if (topic == gmst.find("sCompanionShare")->getString()) MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr); else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused()) { if (topic == gmst.find("sBarter")->getString()) MWBase::Environment::get().getWindowManager()->startTrade(mPtr); else if (topic == gmst.find("sSpells")->getString()) MWBase::Environment::get().getWindowManager()->startSpellBuying(mPtr); else if (topic == gmst.find("sTravel")->getString()) MWBase::Environment::get().getWindowManager()->startTravel(mPtr); else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr); else if (topic == gmst.find("sEnchanting")->getString()) MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr); else if (topic == gmst.find("sServiceTrainingTitle")->getString()) MWBase::Environment::get().getWindowManager()->startTraining (mPtr); else if (topic == gmst.find("sRepair")->getString()) MWBase::Environment::get().getWindowManager()->startRepair (mPtr); } } } void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory) { mGoodbye = false; mEnabled = true; bool sameActor = (mPtr == actor); mPtr = actor; mTopicsList->setEnabled(true); setTitle(npcName); clearChoices(); mTopicsList->clear(); if (resetHistory || !sameActor) { for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) delete (*it); mHistoryContents.clear(); } for (std::vector::iterator it = mLinks.begin(); it != mLinks.end(); ++it) delete (*it); mLinks.clear(); updateOptions(); restock(); } void DialogueWindow::restock() { MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getFloat(); // Gold is restocked every 24h if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay) { sellerStats.setGoldPool(mPtr.getClass().getBaseGold(mPtr)); sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp()); } } void DialogueWindow::setKeywords(std::list keyWords) { mTopicsList->clear(); for (std::map::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it) delete it->second; mTopicLinks.clear(); mKeywordSearch.clear(); bool isCompanion = !mPtr.getClass().getScript(mPtr).empty() && mPtr.getRefData().getLocals().getIntVar(mPtr.getClass().getScript(mPtr), "companion"); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); if (mPtr.getTypeName() == typeid(ESM::NPC).name()) mTopicsList->addItem(gmst.find("sPersuasion")->getString()); if (mServices & Service_Trade) mTopicsList->addItem(gmst.find("sBarter")->getString()); if (mServices & Service_BuySpells) mTopicsList->addItem(gmst.find("sSpells")->getString()); if (mServices & Service_Travel) mTopicsList->addItem(gmst.find("sTravel")->getString()); if (mServices & Service_CreateSpells) mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); if (mServices & Service_Enchant) mTopicsList->addItem(gmst.find("sEnchanting")->getString()); if (mServices & Service_Training) mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); if (mServices & Service_Repair) mTopicsList->addItem(gmst.find("sRepair")->getString()); if (isCompanion) mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); if (mTopicsList->getItemCount() > 0) mTopicsList->addSeparator(); for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) { mTopicsList->addItem(*it); Topic* t = new Topic(*it); mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t; mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t)); } mTopicsList->adjustSize(); updateHistory(); } void DialogueWindow::updateHistory(bool scrollbar) { if (!scrollbar && mScrollBar->getVisible()) { mHistory->setSize(mHistory->getSize()+MyGUI::IntSize(mScrollBar->getWidth(),0)); mScrollBar->setVisible(false); } if (scrollbar && !mScrollBar->getVisible()) { mHistory->setSize(mHistory->getSize()-MyGUI::IntSize(mScrollBar->getWidth(),0)); mScrollBar->setVisible(true); } BookTypesetter::Ptr typesetter = BookTypesetter::create (mHistory->getWidth(), std::numeric_limits::max()); for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) (*it)->write(typesetter, &mKeywordSearch, mTopicLinks); BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White); typesetter->sectionBreak(9); // choices const MyGUI::Colour linkHot(getDialogueTextColour("answer_over")); const MyGUI::Colour linkNormal(getDialogueTextColour("answer")); const MyGUI::Colour linkActive(getDialogueTextColour("answer_pressed")); for (std::vector >::iterator it = mChoices.begin(); it != mChoices.end(); ++it) { Choice* link = new Choice(it->second); mLinks.push_back(link); typesetter->lineBreak(); BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive, TypesetBook::InteractiveId(link)); typesetter->write(questionStyle, to_utf8_span(it->first.c_str())); } if (mGoodbye) { Goodbye* link = new Goodbye(); mLinks.push_back(link); std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString(); BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive, TypesetBook::InteractiveId(link)); typesetter->lineBreak(); typesetter->write(questionStyle, to_utf8_span(goodbye.c_str())); } TypesetBook::Ptr book = typesetter->complete(); mHistory->showPage(book, 0); size_t viewHeight = mHistory->getParent()->getHeight(); if (!scrollbar && book->getSize().second > viewHeight) updateHistory(true); else if (scrollbar) { mHistory->setSize(MyGUI::IntSize(mHistory->getWidth(), book->getSize().second)); size_t range = book->getSize().second - viewHeight; mScrollBar->setScrollRange(range); mScrollBar->setScrollPosition(range-1); mScrollBar->setTrackSize(static_cast(viewHeight / static_cast(book->getSize().second) * mScrollBar->getLineSize())); onScrollbarMoved(mScrollBar, range-1); } else { // no scrollbar onScrollbarMoved(mScrollBar, 0); } MyGUI::Button* byeButton; getWidget(byeButton, "ByeButton"); bool goodbyeEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() || mGoodbye; byeButton->setEnabled(goodbyeEnabled); bool topicsEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() && !mGoodbye; mTopicsList->setEnabled(topicsEnabled); } void DialogueWindow::notifyLinkClicked (TypesetBook::InteractiveId link) { reinterpret_cast(link)->activated(); } void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos) { mHistory->setPosition(0, pos * -1); } void DialogueWindow::addResponse(const std::string &text, const std::string &title) { // This is called from the dialogue manager, so text is // case-smashed - thus we have to retrieve the correct case // of the title through the topic list. std::string realTitle = title; if (realTitle != "") { for (size_t i=0; igetItemCount(); ++i) { std::string item = mTopicsList->getItemNameAt(i); if (Misc::StringUtils::ciEqual(item, title)) { realTitle = item; break; } } } mHistoryContents.push_back(new Response(text, realTitle)); updateHistory(); } void DialogueWindow::addMessageBox(const std::string& text) { mHistoryContents.push_back(new Message(text)); updateHistory(); } void DialogueWindow::addChoice(const std::string& choice, int id) { mChoices.push_back(std::make_pair(choice, id)); updateHistory(); } void DialogueWindow::clearChoices() { mChoices.clear(); updateHistory(); } void DialogueWindow::updateOptions() { //Clear the list of topics mTopicsList->clear(); bool dispositionVisible = false; if (mPtr.getClass().isNpc()) { dispositionVisible = true; mDispositionBar->setProgressRange(100); mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); mDispositionText->setCaption(MyGUI::utility::toString(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")); } bool dispositionWasVisible = mDispositionBar->getVisible(); if (dispositionVisible && !dispositionWasVisible) { mDispositionBar->setVisible(true); int offset = mDispositionBar->getHeight()+5; mTopicsList->setCoord(mTopicsList->getCoord() + MyGUI::IntCoord(0,offset,0,-offset)); mTopicsList->adjustSize(); } else if (!dispositionVisible && dispositionWasVisible) { mDispositionBar->setVisible(false); int offset = mDispositionBar->getHeight()+5; mTopicsList->setCoord(mTopicsList->getCoord() - MyGUI::IntCoord(0,offset,0,-offset)); mTopicsList->adjustSize(); } } void DialogueWindow::goodbye() { mGoodbye = true; mEnabled = false; updateHistory(); } void DialogueWindow::onReferenceUnavailable() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } void DialogueWindow::onFrame() { if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) { int disp = std::max(0, std::min(100, MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange())); mDispositionBar->setProgressRange(100); mDispositionBar->setProgressPosition(disp); mDispositionText->setCaption(MyGUI::utility::toString(disp)+std::string("/100")); } } } openmw-openmw-0.38.0/apps/openmw/mwgui/dialogue.hpp000066400000000000000000000106641264522266000223300ustar00rootroot00000000000000#ifndef MWGUI_DIALOGE_H #define MWGUI_DIALOGE_H #include "windowbase.hpp" #include "referenceinterface.hpp" #include "bookpage.hpp" #include "../mwdialogue/keywordsearch.hpp" namespace Gui { class MWList; } namespace MWGui { class WindowManager; } namespace MWGui { class DialogueHistoryViewModel; class BookPage; class PersuasionDialog : public WindowModal { public: PersuasionDialog(); virtual void open(); virtual void exit(); private: MyGUI::Button* mCancelButton; MyGUI::Button* mAdmireButton; MyGUI::Button* mIntimidateButton; MyGUI::Button* mTauntButton; MyGUI::Button* mBribe10Button; MyGUI::Button* mBribe100Button; MyGUI::Button* mBribe1000Button; MyGUI::TextBox* mGoldLabel; void onCancel (MyGUI::Widget* sender); void onPersuade (MyGUI::Widget* sender); }; struct Link { virtual ~Link() {} virtual void activated () = 0; }; struct Topic : Link { Topic(const std::string& id) : mTopicId(id) {} std::string mTopicId; virtual void activated (); }; struct Choice : Link { Choice(int id) : mChoiceId(id) {} int mChoiceId; virtual void activated (); }; struct Goodbye : Link { virtual void activated (); }; typedef MWDialogue::KeywordSearch KeywordSearchT; struct DialogueText { virtual ~DialogueText() {} virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const = 0; std::string mText; }; struct Response : DialogueText { Response(const std::string& text, const std::string& title = ""); virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const; void addTopicLink (BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const; std::string mTitle; }; struct Message : DialogueText { Message(const std::string& text); virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const; }; class DialogueWindow: public WindowBase, public ReferenceInterface { public: DialogueWindow(); virtual void exit(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; void notifyLinkClicked (TypesetBook::InteractiveId link); void startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory); void setKeywords(std::list keyWord); void addResponse (const std::string& text, const std::string& title=""); void addMessageBox(const std::string& text); void addChoice(const std::string& choice, int id); void clearChoices(); void goodbye(); void onFrame(); // make sure to call these before setKeywords() void setServices(int services) { mServices = services; } enum Services { Service_Trade = 0x01, Service_BuySpells = 0x02, Service_CreateSpells = 0x04, Service_Enchant = 0x08, Service_Training = 0x10, Service_Travel = 0x20, Service_Repair = 0x40 }; protected: void onSelectTopic(const std::string& topic, int id); void onByeClicked(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onWindowResize(MyGUI::Window* _sender); void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos); void updateHistory(bool scrollbar=false); virtual void onReferenceUnavailable(); private: void updateOptions(); void restock(); int mServices; bool mEnabled; bool mGoodbye; std::vector mHistoryContents; std::vector > mChoices; std::vector mLinks; std::map mTopicLinks; KeywordSearchT mKeywordSearch; BookPage* mHistory; Gui::MWList* mTopicsList; MyGUI::ScrollBar* mScrollBar; MyGUI::ProgressBar* mDispositionBar; MyGUI::EditBox* mDispositionText; PersuasionDialog mPersuasionDialog; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/draganddrop.cpp000066400000000000000000000104411264522266000230100ustar00rootroot00000000000000#include "draganddrop.hpp" #include #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "sortfilteritemmodel.hpp" #include "inventorywindow.hpp" #include "itemwidget.hpp" #include "itemview.hpp" #include "controllers.hpp" namespace MWGui { DragAndDrop::DragAndDrop() : mIsOnDragAndDrop(false) , mDraggedWidget(NULL) , mSourceModel(NULL) , mSourceView(NULL) , mSourceSortModel(NULL) , mDraggedCount(0) { } void DragAndDrop::startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count) { mItem = sourceModel->getItem(index); mDraggedCount = count; mSourceModel = sourceModel; mSourceView = sourceView; mSourceSortModel = sortModel; mIsOnDragAndDrop = true; // If picking up an item that isn't from the player's inventory, the item gets added to player inventory backend // immediately, even though it's still floating beneath the mouse cursor. A bit counterintuitive, // but this is how it works in vanilla, and not doing so would break quests (BM_beasts for instance). ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); if (mSourceModel != playerModel) { MWWorld::Ptr item = mSourceModel->moveItem(mItem, mDraggedCount, playerModel); playerModel->update(); ItemModel::ModelIndex newIndex = -1; for (unsigned int i=0; igetItemCount(); ++i) { if (playerModel->getItem(i).mBase == item) { newIndex = i; break; } } mItem = playerModel->getItem(newIndex); mSourceModel = playerModel; SortFilterItemModel* playerFilterModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getSortFilterModel(); mSourceSortModel = playerFilterModel; } std::string sound = mItem.mBase.getClass().getUpSoundId(mItem.mBase); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); if (mSourceSortModel) { mSourceSortModel->clearDragItems(); mSourceSortModel->addDragItem(mItem.mBase, count); } ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget("MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop"); Controllers::ControllerFollowMouse* controller = MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerFollowMouse::getClassTypeName()) ->castType(); MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller); mDraggedWidget = baseWidget; baseWidget->setItem(mItem.mBase); baseWidget->setNeedMouseFocus(false); baseWidget->setCount(count); sourceView->update(); MWBase::Environment::get().getWindowManager()->setDragDrop(true); } void DragAndDrop::drop(ItemModel *targetModel, ItemView *targetView) { std::string sound = mItem.mBase.getClass().getDownSoundId(mItem.mBase); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); // We can't drop a conjured item to the ground; the target container should always be the source container if (mItem.mFlags & ItemStack::Flag_Bound && targetModel != mSourceModel) { MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}"); return; } // If item is dropped where it was taken from, we don't need to do anything - // otherwise, do the transfer if (targetModel != mSourceModel) { mSourceModel->moveItem(mItem, mDraggedCount, targetModel); } mSourceModel->update(); finish(); if (targetView) targetView->update(); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); // We need to update the view since an other item could be auto-equipped. mSourceView->update(); } void DragAndDrop::finish() { mIsOnDragAndDrop = false; mSourceSortModel->clearDragItems(); MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); mDraggedWidget = 0; MWBase::Environment::get().getWindowManager()->setDragDrop(false); } } openmw-openmw-0.38.0/apps/openmw/mwgui/draganddrop.hpp000066400000000000000000000013421264522266000230150ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_DRAGANDDROP_H #define OPENMW_MWGUI_DRAGANDDROP_H #include "itemmodel.hpp" namespace MyGUI { class Widget; } namespace MWGui { class ItemView; class SortFilterItemModel; class DragAndDrop { public: bool mIsOnDragAndDrop; MyGUI::Widget* mDraggedWidget; ItemModel* mSourceModel; ItemView* mSourceView; SortFilterItemModel* mSourceSortModel; ItemStack mItem; int mDraggedCount; DragAndDrop(); void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); void drop (ItemModel* targetModel, ItemView* targetView); void finish(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/enchantingdialog.cpp000066400000000000000000000316421264522266000240270ustar00rootroot00000000000000#include "enchantingdialog.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/actorutil.hpp" #include "itemselection.hpp" #include "itemwidget.hpp" #include "sortfilteritemmodel.hpp" namespace MWGui { EnchantingDialog::EnchantingDialog() : WindowBase("openmw_enchanting_dialog.layout") , EffectEditorBase(EffectEditorBase::Enchanting) , mItemSelectionDialog(NULL) { getWidget(mName, "NameEdit"); getWidget(mCancelButton, "CancelButton"); getWidget(mAvailableEffectsList, "AvailableEffects"); getWidget(mUsedEffectsView, "UsedEffects"); getWidget(mItemBox, "ItemBox"); getWidget(mSoulBox, "SoulBox"); getWidget(mEnchantmentPoints, "Enchantment"); getWidget(mCastCost, "CastCost"); getWidget(mCharge, "Charge"); getWidget(mTypeButton, "TypeButton"); getWidget(mBuyButton, "BuyButton"); getWidget(mPrice, "PriceLabel"); getWidget(mPriceText, "PriceTextLabel"); setWidgets(mAvailableEffectsList, mUsedEffectsView); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onCancelButtonClicked); mItemBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectItem); mSoulBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectSoul); mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onBuyButtonClicked); mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked); } EnchantingDialog::~EnchantingDialog() { delete mItemSelectionDialog; } void EnchantingDialog::open() { center(); } void EnchantingDialog::setSoulGem(const MWWorld::Ptr &gem) { if (gem.isEmpty()) { mSoulBox->setItem(MWWorld::Ptr()); mSoulBox->clearUserStrings(); mEnchanting.setSoulGem(MWWorld::Ptr()); } else { mSoulBox->setItem(gem); mSoulBox->setUserString ("ToolTipType", "ItemPtr"); mSoulBox->setUserData(gem); mEnchanting.setSoulGem(gem); } } void EnchantingDialog::setItem(const MWWorld::Ptr &item) { if (item.isEmpty()) { mItemBox->setItem(MWWorld::Ptr()); mItemBox->clearUserStrings(); mEnchanting.setOldItem(MWWorld::Ptr()); } else { mName->setCaption(item.getClass().getName(item)); mItemBox->setItem(item); mItemBox->setUserString ("ToolTipType", "ItemPtr"); mItemBox->setUserData(item); mEnchanting.setOldItem(item); } } void EnchantingDialog::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); } void EnchantingDialog::updateLabels() { std::stringstream enchantCost; enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantPoints(); mEnchantmentPoints->setCaption(enchantCost.str() + " / " + MyGUI::utility::toString(mEnchanting.getMaxEnchantValue())); mCharge->setCaption(MyGUI::utility::toString(mEnchanting.getGemCharge())); std::stringstream castCost; castCost << mEnchanting.getEffectiveCastCost(); mCastCost->setCaption(castCost.str()); mPrice->setCaption(MyGUI::utility::toString(mEnchanting.getEnchantPrice())); switch(mEnchanting.getCastStyle()) { case ESM::Enchantment::CastOnce: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once")); setConstantEffect(false); break; case ESM::Enchantment::WhenStrikes: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes")); setConstantEffect(false); break; case ESM::Enchantment::WhenUsed: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used")); setConstantEffect(false); break; case ESM::Enchantment::ConstantEffect: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant")); setConstantEffect(true); break; } } void EnchantingDialog::startEnchanting (MWWorld::Ptr actor) { mEnchanting.setSelfEnchanting(false); mEnchanting.setEnchanter(actor); mBuyButton->setCaptionWithReplacing("#{sBuy}"); mPtr = actor; setSoulGem(MWWorld::Ptr()); setItem(MWWorld::Ptr()); startEditing (); mPrice->setVisible(true); mPriceText->setVisible(true); updateLabels(); } void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem) { MWWorld::Ptr player = MWMechanics::getPlayer(); mEnchanting.setSelfEnchanting(true); mEnchanting.setEnchanter(player); mBuyButton->setCaptionWithReplacing("#{sCreate}"); mPtr = player; startEditing(); setSoulGem(soulgem); setItem(MWWorld::Ptr()); mPrice->setVisible(false); mPriceText->setVisible(false); updateLabels(); } void EnchantingDialog::onReferenceUnavailable () { MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); resetReference(); } void EnchantingDialog::resetReference() { ReferenceInterface::resetReference(); setItem(MWWorld::Ptr()); setSoulGem(MWWorld::Ptr()); mPtr = MWWorld::Ptr(); mEnchanting.setEnchanter(MWWorld::Ptr()); } void EnchantingDialog::onCancelButtonClicked(MyGUI::Widget* sender) { exit(); } void EnchantingDialog::onSelectItem(MyGUI::Widget *sender) { if (mEnchanting.getOldItem().isEmpty()) { delete mItemSelectionDialog; mItemSelectionDialog = new ItemSelectionDialog("#{sEnchantItems}"); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onItemSelected); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onItemCancel); mItemSelectionDialog->setVisible(true); mItemSelectionDialog->openContainer(MWMechanics::getPlayer()); mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyEnchantable); } else { setItem(MWWorld::Ptr()); updateLabels(); } } void EnchantingDialog::onItemSelected(MWWorld::Ptr item) { mItemSelectionDialog->setVisible(false); setItem(item); MWBase::Environment::get().getSoundManager()->playSound(item.getClass().getDownSoundId(item), 1, 1); mEnchanting.nextCastStyle(); updateLabels(); } void EnchantingDialog::onItemCancel() { mItemSelectionDialog->setVisible(false); } void EnchantingDialog::onSoulSelected(MWWorld::Ptr item) { mItemSelectionDialog->setVisible(false); mEnchanting.setSoulGem(item); if(mEnchanting.getGemCharge()==0) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage32}"); return; } setSoulGem(item); MWBase::Environment::get().getSoundManager()->playSound(item.getClass().getDownSoundId(item), 1, 1); updateLabels(); } void EnchantingDialog::onSoulCancel() { mItemSelectionDialog->setVisible(false); } void EnchantingDialog::onSelectSoul(MyGUI::Widget *sender) { if (mEnchanting.getGem().isEmpty()) { delete mItemSelectionDialog; mItemSelectionDialog = new ItemSelectionDialog("#{sSoulGemsWithSouls}"); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onSoulSelected); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onSoulCancel); mItemSelectionDialog->setVisible(true); mItemSelectionDialog->openContainer(MWMechanics::getPlayer()); mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyChargedSoulstones); //MWBase::Environment::get().getWindowManager()->messageBox("#{sInventorySelectNoSoul}"); } else { setSoulGem(MWWorld::Ptr()); updateLabels(); } } void EnchantingDialog::notifyEffectsChanged () { mEffectList.mList = mEffects; mEnchanting.setEffect(mEffectList); updateLabels(); } void EnchantingDialog::onTypeButtonClicked(MyGUI::Widget* sender) { mEnchanting.nextCastStyle(); updateLabels(); updateEffectsView(); } void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender) { if (mEffects.size() <= 0) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu11}"); return; } if (mName->getCaption ().empty()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage10}"); return; } if (mEnchanting.soulEmpty()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage52}"); return; } if (mEnchanting.itemEmpty()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage11}"); return; } if (mEnchanting.getEnchantPoints() > mEnchanting.getMaxEnchantValue()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage29}"); return; } mEnchanting.setNewItemName(mName->getCaption()); mEnchanting.setEffect(mEffectList); MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); if (mPtr != player && mEnchanting.getEnchantPrice() > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; } // check if the player is attempting to use a soulstone or item that was stolen from this actor if (mPtr != player) { for (int i=0; i<2; ++i) { MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem(); if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(), mPtr.getCellRef().getRefId())) { std::string msg = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage49")->getString(); if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, item.getClass().getName(item)); MWBase::Environment::get().getWindowManager()->messageBox(msg); MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, item.getClass().getValue(item), true); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); return; } } } int result = mEnchanting.create(); if(result==1) { MWBase::Environment::get().getSoundManager()->playSound("enchant success", 1.f, 1.f); MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu12}"); } else { MWBase::Environment::get().getSoundManager()->playSound("enchant fail", 1.f, 1.f); MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage34}"); } MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); } } openmw-openmw-0.38.0/apps/openmw/mwgui/enchantingdialog.hpp000066400000000000000000000034741264522266000240360ustar00rootroot00000000000000#ifndef MWGUI_ENCHANTINGDIALOG_H #define MWGUI_ENCHANTINGDIALOG_H #include "spellcreationdialog.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/enchanting.hpp" namespace MWGui { class ItemSelectionDialog; class ItemWidget; class EnchantingDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase { public: EnchantingDialog(); virtual ~EnchantingDialog(); virtual void open(); virtual void exit(); void setSoulGem (const MWWorld::Ptr& gem); void setItem (const MWWorld::Ptr& item); void startEnchanting(MWWorld::Ptr actor); void startSelfEnchanting(MWWorld::Ptr soulgem); virtual void resetReference(); protected: virtual void onReferenceUnavailable(); virtual void notifyEffectsChanged (); void onCancelButtonClicked(MyGUI::Widget* sender); void onSelectItem (MyGUI::Widget* sender); void onSelectSoul (MyGUI::Widget* sender); void onItemSelected(MWWorld::Ptr item); void onItemCancel(); void onSoulSelected(MWWorld::Ptr item); void onSoulCancel(); void onBuyButtonClicked(MyGUI::Widget* sender); void updateLabels(); void onTypeButtonClicked(MyGUI::Widget* sender); ItemSelectionDialog* mItemSelectionDialog; MyGUI::Button* mCancelButton; ItemWidget* mItemBox; ItemWidget* mSoulBox; MyGUI::Button* mTypeButton; MyGUI::Button* mBuyButton; MyGUI::TextBox* mName; MyGUI::TextBox* mEnchantmentPoints; MyGUI::TextBox* mCastCost; MyGUI::TextBox* mCharge; MyGUI::TextBox* mPrice; MyGUI::TextBox* mPriceText; MWMechanics::Enchanting mEnchanting; ESM::EffectList mEffectList; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/exposedwindow.cpp000066400000000000000000000012011264522266000234140ustar00rootroot00000000000000#include "exposedwindow.hpp" namespace MWGui { MyGUI::VectorWidgetPtr ExposedWindow::getSkinWidgetsByName (const std::string &name) { return MyGUI::Widget::getSkinWidgetsByName (name); } MyGUI::Widget* ExposedWindow::getSkinWidget(const std::string & _name, bool _throw) { MyGUI::VectorWidgetPtr widgets = getSkinWidgetsByName (_name); if (widgets.empty()) { MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' not found in skin of layout '" << getName() << "'"); return NULL; } else { return widgets[0]; } } } openmw-openmw-0.38.0/apps/openmw/mwgui/exposedwindow.hpp000066400000000000000000000010461264522266000234300ustar00rootroot00000000000000#ifndef MWGUI_EXPOSEDWINDOW_H #define MWGUI_EXPOSEDWINDOW_H #include namespace MWGui { /** * @brief subclass to provide access to some Widget internals. */ class ExposedWindow : public MyGUI::Window { MYGUI_RTTI_DERIVED(ExposedWindow) public: MyGUI::VectorWidgetPtr getSkinWidgetsByName (const std::string &name); MyGUI::Widget* getSkinWidget(const std::string & _name, bool _throw = true); ///< Get a widget defined in the inner skin of this window. }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/formatting.cpp000066400000000000000000000434101264522266000226770ustar00rootroot00000000000000#include "formatting.hpp" #include #include #include #include #include // correctBookartPath #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include #include #include #include #include "../mwscript/interpretercontext.hpp" namespace MWGui { namespace Formatting { /* BookTextParser */ BookTextParser::BookTextParser(const std::string & text) : mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true), mClosingTag(false) { MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor mText = Interpreter::fixDefinesBook(mText, interpreterContext); boost::algorithm::replace_all(mText, "\r", ""); registerTag("br", Event_BrTag); registerTag("p", Event_PTag); registerTag("img", Event_ImgTag); registerTag("div", Event_DivTag); registerTag("font", Event_FontTag); } void BookTextParser::registerTag(const std::string & tag, BookTextParser::Events type) { mTagTypes[tag] = type; } std::string BookTextParser::getReadyText() const { return mReadyText; } BookTextParser::Events BookTextParser::next() { while (mIndex < mText.size()) { char ch = mText[mIndex]; if (ch == '<') { const size_t tagStart = mIndex + 1; const size_t tagEnd = mText.find('>', tagStart); if (tagEnd == std::string::npos) throw std::runtime_error("BookTextParser Error: Tag is not terminated"); parseTag(mText.substr(tagStart, tagEnd - tagStart)); mIndex = tagEnd; if (mTagTypes.find(mTag) != mTagTypes.end()) { Events type = mTagTypes.at(mTag); if (type == Event_BrTag || type == Event_PTag) { if (!mIgnoreNewlineTags) { if (type == Event_BrTag) mBuffer.push_back('\n'); else { mBuffer.append("\n\n"); } } mIgnoreLineEndings = true; } else flushBuffer(); if (type == Event_ImgTag) { mIgnoreNewlineTags = false; } ++mIndex; return type; } } else { if (!mIgnoreLineEndings || ch != '\n') { mBuffer.push_back(ch); mIgnoreLineEndings = false; mIgnoreNewlineTags = false; } } ++mIndex; } flushBuffer(); return Event_EOF; } void BookTextParser::flushBuffer() { mReadyText = mBuffer; mBuffer.clear(); } const BookTextParser::Attributes & BookTextParser::getAttributes() const { return mAttributes; } bool BookTextParser::isClosingTag() const { return mClosingTag; } void BookTextParser::parseTag(std::string tag) { size_t tagNameEndPos = tag.find(' '); mAttributes.clear(); mTag = tag.substr(0, tagNameEndPos); Misc::StringUtils::lowerCaseInPlace(mTag); if (mTag.empty()) return; mClosingTag = (mTag[0] == '/'); if (mClosingTag) { mTag.erase(mTag.begin()); return; } if (tagNameEndPos == std::string::npos) return; tag.erase(0, tagNameEndPos+1); while (!tag.empty()) { size_t sepPos = tag.find('='); if (sepPos == std::string::npos) return; std::string key = tag.substr(0, sepPos); Misc::StringUtils::lowerCaseInPlace(key); tag.erase(0, sepPos+1); std::string value; if (tag.empty()) return; if (tag[0] == '"') { size_t quoteEndPos = tag.find('"', 1); if (quoteEndPos == std::string::npos) throw std::runtime_error("BookTextParser Error: Missing end quote in tag"); value = tag.substr(1, quoteEndPos-1); tag.erase(0, quoteEndPos+2); } else { size_t valEndPos = tag.find(' '); if (valEndPos == std::string::npos) { value = tag; tag.erase(); } else { value = tag.substr(0, valEndPos); tag.erase(0, valEndPos+1); } } mAttributes[key] = value; } } /* BookFormatter */ Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight) { Paginator pag(pageWidth, pageHeight); while (parent->getChildCount()) { MyGUI::Gui::getInstance().destroyWidget(parent->getChildAt(0)); } mTextStyle = TextStyle(); mBlockStyle = BlockStyle(); MyGUI::Widget * paper = parent->createWidget("Widget", MyGUI::IntCoord(0, 0, pag.getPageWidth(), pag.getPageHeight()), MyGUI::Align::Left | MyGUI::Align::Top); paper->setNeedMouseFocus(false); BookTextParser parser(markup); bool brBeforeLastTag = false; bool isPrevImg = false; for (;;) { BookTextParser::Events event = parser.next(); if (event == BookTextParser::Event_BrTag || event == BookTextParser::Event_PTag) continue; std::string plainText = parser.getReadyText(); // for cases when linebreaks are used to cause a shift to the next page // if the split text block ends in an empty line, proceeding text block(s) should have leading empty lines removed if (pag.getIgnoreLeadingEmptyLines()) { while (!plainText.empty()) { if (plainText[0] == '\n') plainText.erase(plainText.begin()); else { pag.setIgnoreLeadingEmptyLines(false); break; } } } if (plainText.empty()) brBeforeLastTag = true; else { // Each block of text (between two tags / boundary and tag) will be displayed in a separate editbox widget, // which means an additional linebreak will be created between them. // ^ This is not what vanilla MW assumes, so we must deal with line breaks around tags appropriately. bool brAtStart = (plainText[0] == '\n'); bool brAtEnd = (plainText[plainText.size()-1] == '\n'); if (brAtStart && !brBeforeLastTag && !isPrevImg) plainText.erase(plainText.begin()); if (plainText.size() && brAtEnd) plainText.erase(plainText.end()-1); #if (MYGUI_VERSION < MYGUI_DEFINE_VERSION(3, 2, 2)) // splitting won't be fully functional until 3.2.2 (see TextElement::pageSplit()) // hack: prevent newlines at the end of the book possibly creating unnecessary pages if (event == BookTextParser::Event_EOF) { while (plainText.size() && plainText[plainText.size()-1] == '\n') plainText.erase(plainText.end()-1); } #endif if (!plainText.empty() || brBeforeLastTag || isPrevImg) { TextElement elem(paper, pag, mBlockStyle, mTextStyle, plainText); elem.paginate(); } brBeforeLastTag = brAtEnd; } if (event == BookTextParser::Event_EOF) break; isPrevImg = (event == BookTextParser::Event_ImgTag); switch (event) { case BookTextParser::Event_ImgTag: { pag.setIgnoreLeadingEmptyLines(false); const BookTextParser::Attributes & attr = parser.getAttributes(); if (attr.find("src") == attr.end() || attr.find("width") == attr.end() || attr.find("height") == attr.end()) continue; std::string src = attr.at("src"); int width = MyGUI::utility::parseInt(attr.at("width")); int height = MyGUI::utility::parseInt(attr.at("height")); ImageElement elem(paper, pag, mBlockStyle, src, width, height); elem.paginate(); break; } case BookTextParser::Event_FontTag: if (parser.isClosingTag()) resetFontProperties(); else handleFont(parser.getAttributes()); break; case BookTextParser::Event_DivTag: handleDiv(parser.getAttributes()); break; default: break; } } // insert last page if (pag.getStartTop() != pag.getCurrentTop()) pag << Paginator::Page(pag.getStartTop(), pag.getStartTop() + pag.getPageHeight()); paper->setSize(paper->getWidth(), pag.getCurrentTop()); return pag.getPages(); } Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, const std::string & markup) { return markupToWidget(parent, markup, parent->getWidth(), parent->getHeight()); } void BookFormatter::resetFontProperties() { mTextStyle = TextStyle(); } void BookFormatter::handleDiv(const BookTextParser::Attributes & attr) { if (attr.find("align") == attr.end()) return; std::string align = attr.at("align"); if (Misc::StringUtils::ciEqual(align, "center")) mBlockStyle.mAlign = MyGUI::Align::HCenter; else if (Misc::StringUtils::ciEqual(align, "left")) mBlockStyle.mAlign = MyGUI::Align::Left; else if (Misc::StringUtils::ciEqual(align, "right")) mBlockStyle.mAlign = MyGUI::Align::Right; } void BookFormatter::handleFont(const BookTextParser::Attributes & attr) { if (attr.find("color") != attr.end()) { unsigned int color; std::stringstream ss; ss << attr.at("color"); ss >> std::hex >> color; mTextStyle.mColour = MyGUI::Colour( (color>>16 & 0xFF) / 255.f, (color>>8 & 0xFF) / 255.f, (color & 0xFF) / 255.f); } if (attr.find("face") != attr.end()) { std::string face = attr.at("face"); mTextStyle.mFont = face; } if (attr.find("size") != attr.end()) { /// \todo } } /* GraphicElement */ GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle) : mParent(parent), mPaginator(pag), mBlockStyle(blockStyle) { } void GraphicElement::paginate() { int newTop = mPaginator.getCurrentTop() + getHeight(); while (newTop-mPaginator.getStartTop() > mPaginator.getPageHeight()) { int newStartTop = pageSplit(); mPaginator << Paginator::Page(mPaginator.getStartTop(), newStartTop); mPaginator.setStartTop(newStartTop); } mPaginator.setCurrentTop(newTop); } int GraphicElement::pageSplit() { return mPaginator.getStartTop() + mPaginator.getPageHeight(); } /* TextElement */ TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle, const TextStyle & textStyle, const std::string & text) : GraphicElement(parent, pag, blockStyle), mTextStyle(textStyle) { MyGUI::EditBox* box = parent->createWidget("NormalText", MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, parent->getName() + MyGUI::utility::toString(parent->getChildCount())); box->setProperty("Static", "true"); box->setProperty("MultiLine", "true"); box->setProperty("WordWrap", "true"); box->setProperty("NeedMouse", "false"); box->setMaxTextLength(text.size()); box->setTextAlign(mBlockStyle.mAlign); box->setTextColour(mTextStyle.mColour); box->setFontName(mTextStyle.mFont); box->setCaption(MyGUI::TextIterator::toTagsString(text)); box->setSize(box->getSize().width, box->getTextSize().height); mEditBox = box; } int TextElement::currentFontHeight() const { std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); } int TextElement::getHeight() { return mEditBox->getTextSize().height; } int TextElement::pageSplit() { // split lines const int lineHeight = currentFontHeight(); unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop()) / lineHeight; int ret = mPaginator.getCurrentTop() + lastLine * lineHeight; // first empty lines that would go to the next page should be ignored // unfortunately, getLineInfo method won't be available until 3.2.2 #if (MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3, 2, 2)) mPaginator.setIgnoreLeadingEmptyLines(true); const MyGUI::VectorLineInfo & lines = mEditBox->getSubWidgetText()->castType()->getLineInfo(); for (unsigned int i = lastLine; i < lines.size(); ++i) { if (lines[i].width == 0) ret += lineHeight; else { mPaginator.setIgnoreLeadingEmptyLines(false); break; } } #endif return ret; } /* ImageElement */ ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle, const std::string & src, int width, int height) : GraphicElement(parent, pag, blockStyle), mImageHeight(height) { int left = 0; if (mBlockStyle.mAlign.isHCenter()) left += (pag.getPageWidth() - width) / 2; else if (mBlockStyle.mAlign.isLeft()) left = 0; else if (mBlockStyle.mAlign.isRight()) left += pag.getPageWidth() - width; mImageBox = parent->createWidget ("ImageBox", MyGUI::IntCoord(left, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top, parent->getName() + MyGUI::utility::toString(parent->getChildCount())); std::string image = MWBase::Environment::get().getWindowManager()->correctBookartPath(src, width, mImageHeight); mImageBox->setImageTexture(image); mImageBox->setProperty("NeedMouse", "false"); } int ImageElement::getHeight() { return mImageHeight; } int ImageElement::pageSplit() { // if the image is larger than the page, fall back to the default pageSplit implementation if (mImageHeight > mPaginator.getPageHeight()) return GraphicElement::pageSplit(); return mPaginator.getCurrentTop(); } } } openmw-openmw-0.38.0/apps/openmw/mwgui/formatting.hpp000066400000000000000000000125601264522266000227060ustar00rootroot00000000000000#ifndef MWGUI_FORMATTING_H #define MWGUI_FORMATTING_H #include #include namespace MWGui { namespace Formatting { struct TextStyle { TextStyle() : mColour(0,0,0) , mFont("Default") , mTextSize(16) { } MyGUI::Colour mColour; std::string mFont; int mTextSize; }; struct BlockStyle { BlockStyle() : mAlign(MyGUI::Align::Left | MyGUI::Align::Top) { } MyGUI::Align mAlign; }; class BookTextParser { public: typedef std::map Attributes; enum Events { Event_None = -2, Event_EOF = -1, Event_BrTag, Event_PTag, Event_ImgTag, Event_DivTag, Event_FontTag }; BookTextParser(const std::string & text); Events next(); const Attributes & getAttributes() const; std::string getReadyText() const; bool isClosingTag() const; private: void registerTag(const std::string & tag, Events type); void flushBuffer(); void parseTag(std::string tag); size_t mIndex; std::string mText; std::string mReadyText; bool mIgnoreNewlineTags; bool mIgnoreLineEndings; Attributes mAttributes; std::string mTag; bool mClosingTag; std::map mTagTypes; std::string mBuffer; }; class Paginator { public: typedef std::pair Page; typedef std::vector Pages; Paginator(int pageWidth, int pageHeight) : mStartTop(0), mCurrentTop(0), mPageWidth(pageWidth), mPageHeight(pageHeight), mIgnoreLeadingEmptyLines(false) { } int getStartTop() const { return mStartTop; } int getCurrentTop() const { return mCurrentTop; } int getPageWidth() const { return mPageWidth; } int getPageHeight() const { return mPageHeight; } bool getIgnoreLeadingEmptyLines() const { return mIgnoreLeadingEmptyLines; } Pages getPages() const { return mPages; } void setStartTop(int top) { mStartTop = top; } void setCurrentTop(int top) { mCurrentTop = top; } void setIgnoreLeadingEmptyLines(bool ignore) { mIgnoreLeadingEmptyLines = ignore; } Paginator & operator<<(const Page & page) { mPages.push_back(page); return *this; } private: int mStartTop, mCurrentTop; int mPageWidth, mPageHeight; bool mIgnoreLeadingEmptyLines; Pages mPages; }; /// \brief utilities for parsing book/scroll text as mygui widgets class BookFormatter { public: Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight); Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup); private: void resetFontProperties(); void handleDiv(const BookTextParser::Attributes & attr); void handleFont(const BookTextParser::Attributes & attr); TextStyle mTextStyle; BlockStyle mBlockStyle; }; class GraphicElement { public: GraphicElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle); virtual int getHeight() = 0; virtual void paginate(); virtual int pageSplit(); protected: virtual ~GraphicElement() {} MyGUI::Widget * mParent; Paginator & mPaginator; BlockStyle mBlockStyle; }; class TextElement : public GraphicElement { public: TextElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle, const TextStyle & textStyle, const std::string & text); virtual int getHeight(); virtual int pageSplit(); private: int currentFontHeight() const; TextStyle mTextStyle; MyGUI::EditBox * mEditBox; }; class ImageElement : public GraphicElement { public: ImageElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle, const std::string & src, int width, int height); virtual int getHeight(); virtual int pageSplit(); private: int mImageHeight; MyGUI::ImageBox * mImageBox; }; } } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/hud.cpp000066400000000000000000000536501264522266000213140ustar00rootroot00000000000000#include "hud.hpp" #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "inventorywindow.hpp" #include "spellicons.hpp" #include "itemmodel.hpp" #include "draganddrop.hpp" #include "itemmodel.hpp" #include "itemwidget.hpp" namespace MWGui { /** * Makes it possible to use ItemModel::moveItem to move an item from an inventory to the world. */ class WorldItemModel : public ItemModel { public: WorldItemModel(float left, float top) : mLeft(left), mTop(top) {} virtual ~WorldItemModel() {} virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) { MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr dropped; if (world->canPlaceObject(mLeft, mTop)) dropped = world->placeObject(item.mBase, mLeft, mTop, count); else dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count); if (setNewOwner) dropped.getCellRef().setOwner(""); return dropped; } virtual void removeItem (const ItemStack& item, size_t count) { throw std::runtime_error("removeItem not implemented"); } virtual ModelIndex getIndex (ItemStack item) { throw std::runtime_error("getIndex not implemented"); } virtual void update() {} virtual size_t getItemCount() { return 0; } virtual ItemStack getItem (ModelIndex index) { throw std::runtime_error("getItem not implemented"); } private: // Where to drop the item float mLeft; float mTop; }; HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender) : Layout("openmw_hud.layout") , LocalMapBase(customMarkers, localMapRender) , mHealth(NULL) , mMagicka(NULL) , mStamina(NULL) , mDrowning(NULL) , mWeapImage(NULL) , mSpellImage(NULL) , mWeapStatus(NULL) , mSpellStatus(NULL) , mEffectBox(NULL) , mMinimap(NULL) , mCompass(NULL) , mCrosshair(NULL) , mCellNameBox(NULL) , mDrowningFrame(NULL) , mDrowningFlash(NULL) , mHealthManaStaminaBaseLeft(0) , mWeapBoxBaseLeft(0) , mSpellBoxBaseLeft(0) , mMinimapBoxBaseRight(0) , mEffectBoxBaseRight(0) , mDragAndDrop(dragAndDrop) , mCellNameTimer(0.0f) , mWeaponSpellTimer(0.f) , mMapVisible(true) , mWeaponVisible(true) , mSpellVisible(true) , mWorldMouseOver(false) , mEnemyActorId(-1) , mEnemyHealthTimer(-1) , mIsDrowning(false) , mDrowningFlashTheta(0.f) { mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); // Energy bars getWidget(mHealthFrame, "HealthFrame"); getWidget(mHealth, "Health"); getWidget(mMagicka, "Magicka"); getWidget(mStamina, "Stamina"); getWidget(mEnemyHealth, "EnemyHealth"); mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; getWidget(healthFrame, "HealthFrame"); getWidget(magickaFrame, "MagickaFrame"); getWidget(fatigueFrame, "FatigueFrame"); healthFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); //Drowning bar getWidget(mDrowningFrame, "DrowningFrame"); getWidget(mDrowning, "Drowning"); getWidget(mDrowningFlash, "Flash"); mDrowning->setProgressRange(200); const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); // Item and spell images and status bars getWidget(mWeapBox, "WeapBox"); getWidget(mWeapImage, "WeapImage"); getWidget(mWeapStatus, "WeapStatus"); mWeapBoxBaseLeft = mWeapBox->getLeft(); mWeapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked); getWidget(mSpellBox, "SpellBox"); getWidget(mSpellImage, "SpellImage"); getWidget(mSpellStatus, "SpellStatus"); mSpellBoxBaseLeft = mSpellBox->getLeft(); mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); getWidget(mSneakBox, "SneakBox"); mSneakBoxBaseLeft = mSneakBox->getLeft(); getWidget(mEffectBox, "EffectBox"); mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight(); getWidget(mMinimapBox, "MiniMapBox"); mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); getWidget(mMinimap, "MiniMap"); getWidget(mCompass, "Compass"); getWidget(mMinimapButton, "MiniMapButton"); mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); getWidget(mCellNameBox, "CellName"); getWidget(mWeaponSpellBox, "WeaponSpellName"); getWidget(mCrosshair, "Crosshair"); LocalMapBase::init(mMinimap, mCompass, Settings::Manager::getInt("local map hud widget size", "Map")); mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); mMainWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &HUD::onWorldMouseLostFocus); mSpellIcons = new SpellIcons(); } HUD::~HUD() { mMainWidget->eventMouseLostFocus.clear(); mMainWidget->eventMouseMove.clear(); mMainWidget->eventMouseButtonClick.clear(); delete mSpellIcons; } void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) { int current = std::max(0, static_cast(value.getCurrent())); int modified = static_cast(value.getModified()); MyGUI::Widget* w; std::string valStr = MyGUI::utility::toString(current) + "/" + MyGUI::utility::toString(modified); if (id == "HBar") { mHealth->setProgressRange(modified); mHealth->setProgressPosition(current); getWidget(w, "HealthFrame"); w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); } else if (id == "MBar") { mMagicka->setProgressRange (modified); mMagicka->setProgressPosition (current); getWidget(w, "MagickaFrame"); w->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr); } else if (id == "FBar") { mStamina->setProgressRange (modified); mStamina->setProgressPosition (current); getWidget(w, "FatigueFrame"); w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); } } void HUD::setDrowningTimeLeft(float time, float maxTime) { size_t progress = static_cast(time / maxTime * 200); mDrowning->setProgressPosition(progress); bool isDrowning = (progress == 0); if (isDrowning && !mIsDrowning) // Just started drowning mDrowningFlashTheta = 0.0f; // Start out on bright red every time. mDrowningFlash->setVisible(isDrowning); mIsDrowning = isDrowning; } void HUD::setDrowningBarVisible(bool visible) { mDrowningFrame->setVisible(visible); } void HUD::onWorldClicked(MyGUI::Widget* _sender) { if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) return; if (mDragAndDrop->mIsOnDragAndDrop) { // drop item into the gameworld MWBase::Environment::get().getWorld()->breakInvisibility( MWMechanics::getPlayer()); MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); float mouseX = cursorPosition.left / float(viewSize.width); float mouseY = cursorPosition.top / float(viewSize.height); WorldItemModel drop (mouseX, mouseY); mDragAndDrop->drop(&drop, NULL); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); } else { GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) return; MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); if (mode == GM_Console) MWBase::Environment::get().getWindowManager()->setConsoleSelectedObject(object); else if ((mode == GM_Container) || (mode == GM_Inventory)) { // pick up object if (!object.isEmpty()) MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); } } } void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) { if (mDragAndDrop->mIsOnDragAndDrop) { mWorldMouseOver = false; MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); float mouseX = cursorPosition.left / float(viewSize.width); float mouseY = cursorPosition.top / float(viewSize.height); MWBase::World* world = MWBase::Environment::get().getWorld(); // if we can't drop the object at the wanted position, show the "drop on ground" cursor. bool canDrop = world->canPlaceObject(mouseX, mouseY); if (!canDrop) MWBase::Environment::get().getWindowManager()->changePointer("drop_ground"); else MWBase::Environment::get().getWindowManager()->changePointer("arrow"); } else { MWBase::Environment::get().getWindowManager()->changePointer("arrow"); mWorldMouseOver = true; } } void HUD::onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new) { MWBase::Environment::get().getWindowManager()->changePointer("arrow"); mWorldMouseOver = false; } void HUD::onHMSClicked(MyGUI::Widget* _sender) { MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); } void HUD::onMapClicked(MyGUI::Widget* _sender) { MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); } void HUD::onWeaponClicked(MyGUI::Widget* _sender) { const MWWorld::Ptr& player = MWMechanics::getPlayer(); if (player.getClass().getNpcStats(player).isWerewolf()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); return; } MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); } void HUD::onMagicClicked(MyGUI::Widget* _sender) { const MWWorld::Ptr& player = MWMechanics::getPlayer(); if (player.getClass().getNpcStats(player).isWerewolf()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); return; } MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); } void HUD::setCellName(const std::string& cellName) { if (mCellName != cellName) { mCellNameTimer = 5.0f; mCellName = cellName; mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); mCellNameBox->setVisible(mMapVisible); } } void HUD::onFrame(float dt) { LocalMapBase::onFrame(dt); mCellNameTimer -= dt; mWeaponSpellTimer -= dt; if (mCellNameTimer < 0) mCellNameBox->setVisible(false); if (mWeaponSpellTimer < 0) mWeaponSpellBox->setVisible(false); mEnemyHealthTimer -= dt; if (mEnemyHealth->getVisible() && mEnemyHealthTimer < 0) { mEnemyHealth->setVisible(false); mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20)); } if (mIsDrowning) mDrowningFlashTheta += dt * osg::PI*2; } void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellId); std::string spellName = spell->mName; if (spellName != mSpellName && mSpellVisible) { mWeaponSpellTimer = 5.0f; mSpellName = spellName; mWeaponSpellBox->setCaption(mSpellName); mWeaponSpellBox->setVisible(true); } mSpellStatus->setProgressRange(100); mSpellStatus->setProgressPosition(successChancePercent); mSpellBox->setUserString("ToolTipType", "Spell"); mSpellBox->setUserString("Spell", spellId); // use the icon of the first effect const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(spell->mEffects.mList.front().mEffectID); std::string icon = effect->mIcon; int slashPos = icon.rfind('\\'); icon.insert(slashPos+1, "b_"); icon = MWBase::Environment::get().getWindowManager()->correctIconPath(icon); mSpellImage->setItem(MWWorld::Ptr()); mSpellImage->setIcon(icon); } void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) { std::string itemName = item.getClass().getName(item); if (itemName != mSpellName && mSpellVisible) { mWeaponSpellTimer = 5.0f; mSpellName = itemName; mWeaponSpellBox->setCaption(mSpellName); mWeaponSpellBox->setVisible(true); } mSpellStatus->setProgressRange(100); mSpellStatus->setProgressPosition(chargePercent); mSpellBox->setUserString("ToolTipType", "ItemPtr"); mSpellBox->setUserData(item); mSpellImage->setItem(item); } void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) { std::string itemName = item.getClass().getName(item); if (itemName != mWeaponName && mWeaponVisible) { mWeaponSpellTimer = 5.0f; mWeaponName = itemName; mWeaponSpellBox->setCaption(mWeaponName); mWeaponSpellBox->setVisible(true); } mWeapBox->clearUserStrings(); mWeapBox->setUserString("ToolTipType", "ItemPtr"); mWeapBox->setUserData(item); mWeapStatus->setProgressRange(100); mWeapStatus->setProgressPosition(durabilityPercent); mWeapImage->setItem(item); } void HUD::unsetSelectedSpell() { std::string spellName = "#{sNone}"; if (spellName != mSpellName && mSpellVisible) { mWeaponSpellTimer = 5.0f; mSpellName = spellName; mWeaponSpellBox->setCaptionWithReplacing(mSpellName); mWeaponSpellBox->setVisible(true); } mSpellStatus->setProgressRange(100); mSpellStatus->setProgressPosition(0); mSpellImage->setItem(MWWorld::Ptr()); mSpellBox->clearUserStrings(); } void HUD::unsetSelectedWeapon() { std::string itemName = "#{sSkillHandtohand}"; if (itemName != mWeaponName && mWeaponVisible) { mWeaponSpellTimer = 5.0f; mWeaponName = itemName; mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); mWeaponSpellBox->setVisible(true); } mWeapStatus->setProgressRange(100); mWeapStatus->setProgressPosition(0); MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); mWeapImage->setItem(MWWorld::Ptr()); std::string icon = (player.getClass().getNpcStats(player).isWerewolf()) ? "icons\\k\\tx_werewolf_hand.dds" : "icons\\k\\stealth_handtohand.dds"; mWeapImage->setIcon(icon); mWeapBox->clearUserStrings(); mWeapBox->setUserString("ToolTipType", "Layout"); mWeapBox->setUserString("ToolTipLayout", "HandToHandToolTip"); mWeapBox->setUserString("Caption_HandToHandText", itemName); mWeapBox->setUserString("ImageTexture_HandToHandImage", icon); } void HUD::setCrosshairVisible(bool visible) { mCrosshair->setVisible (visible); } void HUD::setCrosshairOwned(bool owned) { if(owned) { mCrosshair->changeWidgetSkin("HUD_Crosshair_Owned"); } else { mCrosshair->changeWidgetSkin("HUD_Crosshair"); } } void HUD::setHmsVisible(bool visible) { mHealth->setVisible(visible); mMagicka->setVisible(visible); mStamina->setVisible(visible); updatePositions(); } void HUD::setWeapVisible(bool visible) { mWeapBox->setVisible(visible); updatePositions(); } void HUD::setSpellVisible(bool visible) { mSpellBox->setVisible(visible); updatePositions(); } void HUD::setSneakVisible(bool visible) { mSneakBox->setVisible(visible); updatePositions(); } void HUD::setEffectVisible(bool visible) { mEffectBox->setVisible (visible); updatePositions(); } void HUD::setMinimapVisible(bool visible) { mMinimapBox->setVisible (visible); updatePositions(); } void HUD::updatePositions() { int weapDx = 0, spellDx = 0, sneakDx = 0; if (!mHealth->getVisible()) sneakDx = spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; if (!mWeapBox->getVisible()) { spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; sneakDx = spellDx; } if (!mSpellBox->getVisible()) sneakDx += mSneakBoxBaseLeft - mSpellBoxBaseLeft; mWeaponVisible = mWeapBox->getVisible(); mSpellVisible = mSpellBox->getVisible(); if (!mWeaponVisible && !mSpellVisible) mWeaponSpellBox->setVisible(false); mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); mSneakBox->setPosition(mSneakBoxBaseLeft - sneakDx, mSneakBox->getTop()); const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); // effect box can have variable width -> variable left coordinate int effectsDx = 0; if (!mMinimapBox->getVisible ()) effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); mMapVisible = mMinimapBox->getVisible (); if (!mMapVisible) mCellNameBox->setVisible(false); mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); } void HUD::updateEnemyHealthBar() { MWWorld::Ptr enemy = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mEnemyActorId); if (enemy.isEmpty()) return; MWMechanics::CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); mEnemyHealth->setProgressRange(100); // Health is usually cast to int before displaying. Actors die whenever they are < 1 health. // Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :) mEnemyHealth->setProgressPosition(static_cast(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100)); static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarFade")->getFloat(); if (fNPCHealthBarFade > 0.f) mEnemyHealth->setAlpha(std::max(0.f, std::min(1.f, mEnemyHealthTimer/fNPCHealthBarFade))); } void HUD::update() { mSpellIcons->updateWidgets(mEffectBox, true); if (mEnemyActorId != -1 && mEnemyHealth->getVisible()) { updateEnemyHealthBar(); } if (mIsDrowning) { float intensity = (cos(mDrowningFlashTheta) + 1.0f) / 2.0f; mDrowningFlash->setColour(MyGUI::Colour(intensity, 0, 0)); } } void HUD::setEnemy(const MWWorld::Ptr &enemy) { mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId(); mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarTime")->getFloat(); if (!mEnemyHealth->getVisible()) mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); mEnemyHealth->setVisible(true); updateEnemyHealthBar(); } void HUD::resetEnemy() { mEnemyActorId = -1; mEnemyHealthTimer = -1; } void HUD::customMarkerCreated(MyGUI::Widget *marker) { marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); } void HUD::doorMarkerCreated(MyGUI::Widget *marker) { marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); } } openmw-openmw-0.38.0/apps/openmw/mwgui/hud.hpp000066400000000000000000000073631264522266000213210ustar00rootroot00000000000000#ifndef OPENMW_GAME_MWGUI_HUD_H #define OPENMW_GAME_MWGUI_HUD_H #include "mapwindow.hpp" #include "../mwmechanics/stat.hpp" namespace MWWorld { class Ptr; } namespace MWGui { class DragAndDrop; class SpellIcons; class ItemWidget; class HUD : public Layout, public LocalMapBase { public: HUD(CustomMarkerCollection& customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender); virtual ~HUD(); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); /// Set time left for the player to start drowning /// @param time time left to start drowning /// @param maxTime how long we can be underwater (in total) until drowning starts void setDrowningTimeLeft(float time, float maxTime); void setDrowningBarVisible(bool visible); void setHmsVisible(bool visible); void setWeapVisible(bool visible); void setSpellVisible(bool visible); void setSneakVisible(bool visible); void setEffectVisible(bool visible); void setMinimapVisible(bool visible); void setSelectedSpell(const std::string& spellId, int successChancePercent); void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); void unsetSelectedSpell(); void unsetSelectedWeapon(); void setCrosshairVisible(bool visible); void setCrosshairOwned(bool owned); void onFrame(float dt); void setCellName(const std::string& cellName); bool getWorldMouseOver() { return mWorldMouseOver; } MyGUI::Widget* getEffectBox() { return mEffectBox; } void update(); void setEnemy(const MWWorld::Ptr& enemy); void resetEnemy(); private: MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; MyGUI::Widget* mHealthFrame; MyGUI::Widget *mWeapBox, *mSpellBox, *mSneakBox; ItemWidget *mWeapImage, *mSpellImage; MyGUI::ProgressBar *mWeapStatus, *mSpellStatus; MyGUI::Widget *mEffectBox, *mMinimapBox; MyGUI::Button* mMinimapButton; MyGUI::ScrollView* mMinimap; MyGUI::ImageBox* mCompass; MyGUI::ImageBox* mCrosshair; MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; MyGUI::Widget *mDrowningFrame, *mDrowningFlash; // bottom left elements int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft, mSneakBoxBaseLeft; // bottom right elements int mMinimapBoxBaseRight, mEffectBoxBaseRight; DragAndDrop* mDragAndDrop; std::string mCellName; float mCellNameTimer; std::string mWeaponName; std::string mSpellName; float mWeaponSpellTimer; bool mMapVisible; bool mWeaponVisible; bool mSpellVisible; bool mWorldMouseOver; SpellIcons* mSpellIcons; int mEnemyActorId; float mEnemyHealthTimer; bool mIsDrowning; float mDrowningFlashTheta; void onWorldClicked(MyGUI::Widget* _sender); void onWorldMouseOver(MyGUI::Widget* _sender, int x, int y); void onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new); void onHMSClicked(MyGUI::Widget* _sender); void onWeaponClicked(MyGUI::Widget* _sender); void onMagicClicked(MyGUI::Widget* _sender); void onMapClicked(MyGUI::Widget* _sender); // LocalMapBase virtual void customMarkerCreated(MyGUI::Widget* marker); virtual void doorMarkerCreated(MyGUI::Widget* marker); void updateEnemyHealthBar(); void updatePositions(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/inventoryitemmodel.cpp000066400000000000000000000060441264522266000244640ustar00rootroot00000000000000#include "inventoryitemmodel.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" namespace MWGui { InventoryItemModel::InventoryItemModel(const MWWorld::Ptr &actor) : mActor(actor) { } ItemStack InventoryItemModel::getItem (ModelIndex index) { if (index < 0) throw std::runtime_error("Invalid index supplied"); if (mItems.size() <= static_cast(index)) throw std::runtime_error("Item index out of range"); return mItems[index]; } size_t InventoryItemModel::getItemCount() { return mItems.size(); } ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item) { size_t i = 0; for (std::vector::iterator it = mItems.begin(); it != mItems.end(); ++it) { if (*it == item) return i; ++i; } return -1; } MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) { if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) throw std::runtime_error("Item to copy needs to be from a different container!"); return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner); } void InventoryItemModel::removeItem (const ItemStack& item, size_t count) { MWWorld::ContainerStore& store = mActor.getClass().getContainerStore(mActor); int removed = store.remove(item.mBase, count, mActor); if (removed == 0) throw std::runtime_error("Item to remove not found in container store"); else if (removed < static_cast(count)) throw std::runtime_error("Not enough items in the stack to remove"); } MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel) { // Can't move conjured items: This is a general fix that also takes care of issues with taking conjured items via the 'Take All' button. if (item.mFlags & ItemStack::Flag_Bound) return MWWorld::Ptr(); MWWorld::Ptr ret = otherModel->copyItem(item, count, false); removeItem(item, count); return ret; } void InventoryItemModel::update() { MWWorld::ContainerStore& store = mActor.getClass().getContainerStore(mActor); mItems.clear(); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { MWWorld::Ptr item = *it; // NOTE: Don't show WerewolfRobe objects in the inventory, or allow them to be taken. // Vanilla likely uses a hack like this since there's no other way to prevent it from // being shown or taken. if(item.getCellRef().getRefId() == "werewolfrobe") continue; ItemStack newItem (item, this, item.getRefData().getCount()); if (mActor.getClass().hasInventoryStore(mActor)) { MWWorld::InventoryStore& store = mActor.getClass().getInventoryStore(mActor); if (store.isEquipped(newItem.mBase)) newItem.mType = ItemStack::Type_Equipped; } mItems.push_back(newItem); } } } openmw-openmw-0.38.0/apps/openmw/mwgui/inventoryitemmodel.hpp000066400000000000000000000015441264522266000244710ustar00rootroot00000000000000#ifndef MWGUI_INVENTORY_ITEM_MODEL_H #define MWGUI_INVENTORY_ITEM_MODEL_H #include "itemmodel.hpp" namespace MWGui { class InventoryItemModel : public ItemModel { public: InventoryItemModel (const MWWorld::Ptr& actor); virtual ItemStack getItem (ModelIndex index); virtual ModelIndex getIndex (ItemStack item); virtual size_t getItemCount(); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); /// Move items from this model to \a otherModel. virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); virtual void update(); protected: MWWorld::Ptr mActor; private: std::vector mItems; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/inventorywindow.cpp000066400000000000000000000624201264522266000240140ustar00rootroot00000000000000#include "inventorywindow.hpp" #include #include #include #include #include #include #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/action.hpp" #include "../mwscript/interpretercontext.hpp" #include "../mwrender/characterpreview.hpp" #include "../mwmechanics/actorutil.hpp" #include "itemview.hpp" #include "inventoryitemmodel.hpp" #include "sortfilteritemmodel.hpp" #include "tradeitemmodel.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" #include "draganddrop.hpp" #include "widgets.hpp" namespace { bool isRightHandWeapon(const MWWorld::Ptr& item) { if (item.getClass().getTypeName() != typeid(ESM::Weapon).name()) return false; std::vector equipmentSlots = item.getClass().getEquipmentSlots(item).first; return (!equipmentSlots.empty() && equipmentSlots.front() == MWWorld::InventoryStore::Slot_CarriedRight); } } namespace MWGui { InventoryWindow::InventoryWindow(DragAndDrop* dragAndDrop, osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem) : WindowPinnableBase("openmw_inventory_window.layout") , mDragAndDrop(dragAndDrop) , mSelectedItem(-1) , mSortModel(NULL) , mTradeModel(NULL) , mGuiMode(GM_Inventory) , mLastXSize(0) , mLastYSize(0) , mPreview(new MWRender::InventoryPreview(viewer, resourceSystem, MWMechanics::getPlayer())) , mTrading(false) { mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture())); mPreview->rebuild(); mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); getWidget(mAvatar, "Avatar"); getWidget(mAvatarImage, "AvatarImage"); getWidget(mEncumbranceBar, "EncumbranceBar"); getWidget(mFilterAll, "AllButton"); getWidget(mFilterWeapon, "WeaponButton"); getWidget(mFilterApparel, "ApparelButton"); getWidget(mFilterMagic, "MagicButton"); getWidget(mFilterMisc, "MiscButton"); getWidget(mLeftPane, "LeftPane"); getWidget(mRightPane, "RightPane"); getWidget(mArmorRating, "ArmorRating"); mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); mAvatarImage->setRenderItemTexture(mPreviewTexture.get()); mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected); mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &InventoryWindow::onBackgroundSelected); mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterAll->setStateSelected(true); setGuiMode(mGuiMode); adjustPanes(); } void InventoryWindow::adjustPanes() { const float aspect = 0.5; // fixed aspect ratio for the avatar image int leftPaneWidth = static_cast((mMainWidget->getSize().height - 44 - mArmorRating->getHeight()) * aspect); mLeftPane->setSize( leftPaneWidth, mMainWidget->getSize().height-44 ); mRightPane->setCoord( mLeftPane->getPosition().left + leftPaneWidth + 4, mRightPane->getPosition().top, mMainWidget->getSize().width - 12 - leftPaneWidth - 15, mMainWidget->getSize().height-44 ); } void InventoryWindow::updatePlayer() { mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr()); if (mSortModel) // reuse existing SortModel when possible to keep previous category/filter settings mSortModel->setSourceModel(mTradeModel); else mSortModel = new SortFilterItemModel(mTradeModel); mItemView->setModel(mSortModel); mPreview->updatePtr(mPtr); mPreview->rebuild(); mPreview->update(); dirtyPreview(); updatePreviewSize(); } void InventoryWindow::setGuiMode(GuiMode mode) { std::string setting = "inventory"; mGuiMode = mode; switch(mode) { case GM_Container: setPinButtonVisible(false); setting += " container"; break; case GM_Companion: setPinButtonVisible(false); setting += " companion"; break; case GM_Barter: setPinButtonVisible(false); setting += " barter"; break; case GM_Inventory: default: setPinButtonVisible(true); break; } MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntPoint pos(static_cast(Settings::Manager::getFloat(setting + " x", "Windows") * viewSize.width), static_cast(Settings::Manager::getFloat(setting + " y", "Windows") * viewSize.height)); MyGUI::IntSize size(static_cast(Settings::Manager::getFloat(setting + " w", "Windows") * viewSize.width), static_cast(Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height)); bool needUpdate = (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight()); mMainWidget->setPosition(pos); mMainWidget->setSize(size); adjustPanes(); if (needUpdate) updatePreviewSize(); } SortFilterItemModel* InventoryWindow::getSortFilterModel() { return mSortModel; } TradeItemModel* InventoryWindow::getTradeModel() { return mTradeModel; } ItemModel* InventoryWindow::getModel() { return mTradeModel; } void InventoryWindow::onBackgroundSelected() { if (mDragAndDrop->mIsOnDragAndDrop) mDragAndDrop->drop(mTradeModel, mItemView); } void InventoryWindow::onItemSelected (int index) { onItemSelectedFromSourceModel (mSortModel->mapToSource(index)); } void InventoryWindow::onItemSelectedFromSourceModel (int index) { if (mDragAndDrop->mIsOnDragAndDrop) { mDragAndDrop->drop(mTradeModel, mItemView); return; } const ItemStack& item = mTradeModel->getItem(index); std::string sound = item.mBase.getClass().getDownSoundId(item.mBase); MWWorld::Ptr object = item.mBase; int count = item.mCount; bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); if (MyGUI::InputManager::getInstance().isControlPressed()) count = 1; if (mTrading) { // Can't give conjured items to a merchant if (item.mFlags & ItemStack::Flag_Bound) { MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog9}"); return; } // check if merchant accepts item int services = MWBase::Environment::get().getWindowManager()->getTradeWindow()->getMerchantServices(); if (!object.getClass().canSell(object, services)) { MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); MWBase::Environment::get().getWindowManager()-> messageBox("#{sBarterDialog4}"); return; } } if (count > 1 && !shift) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); std::string message = mTrading ? "#{sQuanityMenuMessage01}" : "#{sTake}"; dialog->openCountDialog(object.getClass().getName(object), message, count); dialog->eventOkClicked.clear(); if (mTrading) dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem); else dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dragItem); mSelectedItem = index; } else { mSelectedItem = index; if (mTrading) sellItem (NULL, count); else dragItem (NULL, count); } } void InventoryWindow::ensureSelectedItemUnequipped() { const ItemStack& item = mTradeModel->getItem(mSelectedItem); if (item.mType == ItemStack::Type_Equipped) { MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr); MWWorld::Ptr newStack = *invStore.unequipItem(item.mBase, mPtr); // The unequipped item was re-stacked. We have to update the index // since the item pointed does not exist anymore. if (item.mBase != newStack) { // newIndex will store the index of the ItemStack the item was stacked on int newIndex = -1; for (size_t i=0; i < mTradeModel->getItemCount(); ++i) { if (mTradeModel->getItem(i).mBase == newStack) { newIndex = i; break; } } if (newIndex == -1) throw std::runtime_error("Can't find restacked item"); mSelectedItem = newIndex; } } } void InventoryWindow::dragItem(MyGUI::Widget* sender, int count) { ensureSelectedItemUnequipped(); mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count); notifyContentChanged(); } void InventoryWindow::sellItem(MyGUI::Widget* sender, int count) { ensureSelectedItemUnequipped(); const ItemStack& item = mTradeModel->getItem(mSelectedItem); std::string sound = item.mBase.getClass().getDownSoundId(item.mBase); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); if (item.mType == ItemStack::Type_Barter) { // this was an item borrowed to us by the merchant mTradeModel->returnItemBorrowedToUs(mSelectedItem, count); MWBase::Environment::get().getWindowManager()->getTradeWindow()->returnItem(mSelectedItem, count); } else { // borrow item to the merchant mTradeModel->borrowItemFromUs(mSelectedItem, count); MWBase::Environment::get().getWindowManager()->getTradeWindow()->borrowItem(mSelectedItem, count); } mItemView->update(); notifyContentChanged(); } void InventoryWindow::updateItemView() { MWBase::Environment::get().getWindowManager()->updateSpellWindow(); mItemView->update(); dirtyPreview(); } void InventoryWindow::open() { mPtr = MWMechanics::getPlayer(); updateEncumbranceBar(); mItemView->update(); notifyContentChanged(); adjustPanes(); } void InventoryWindow::onWindowResize(MyGUI::Window* _sender) { adjustPanes(); std::string setting = "inventory"; switch(mGuiMode) { case GM_Container: setting += " container"; break; case GM_Companion: setting += " companion"; break; case GM_Barter: setting += " barter"; break; default: break; } MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); float x = _sender->getPosition().left / float(viewSize.width); float y = _sender->getPosition().top / float(viewSize.height); float w = _sender->getSize().width / float(viewSize.width); float h = _sender->getSize().height / float(viewSize.height); Settings::Manager::setFloat(setting + " x", "Windows", x); Settings::Manager::setFloat(setting + " y", "Windows", y); Settings::Manager::setFloat(setting + " w", "Windows", w); Settings::Manager::setFloat(setting + " h", "Windows", h); if (mMainWidget->getSize().width != mLastXSize || mMainWidget->getSize().height != mLastYSize) { mLastXSize = mMainWidget->getSize().width; mLastYSize = mMainWidget->getSize().height; updatePreviewSize(); updateArmorRating(); } } void InventoryWindow::updateArmorRating() { mArmorRating->setCaptionWithReplacing ("#{sArmor}: " + MyGUI::utility::toString(static_cast(mPtr.getClass().getArmorRating(mPtr)))); if (mArmorRating->getTextSize().width > mArmorRating->getSize().width) mArmorRating->setCaptionWithReplacing (MyGUI::utility::toString(static_cast(mPtr.getClass().getArmorRating(mPtr)))); } void InventoryWindow::updatePreviewSize() { MyGUI::IntSize size = mAvatarImage->getSize(); int width = std::min(mPreview->getTextureWidth(), size.width); int height = std::min(mPreview->getTextureHeight(), size.height); mPreview->setViewport(width, height); mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, height/float(mPreview->getTextureHeight()), width/float(mPreview->getTextureWidth()), 0.f)); } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) { if (_sender == mFilterAll) mSortModel->setCategory(SortFilterItemModel::Category_All); else if (_sender == mFilterWeapon) mSortModel->setCategory(SortFilterItemModel::Category_Weapon); else if (_sender == mFilterApparel) mSortModel->setCategory(SortFilterItemModel::Category_Apparel); else if (_sender == mFilterMagic) mSortModel->setCategory(SortFilterItemModel::Category_Magic); else if (_sender == mFilterMisc) mSortModel->setCategory(SortFilterItemModel::Category_Misc); mFilterAll->setStateSelected(false); mFilterWeapon->setStateSelected(false); mFilterApparel->setStateSelected(false); mFilterMagic->setStateSelected(false); mFilterMisc->setStateSelected(false); mItemView->update(); _sender->castType()->setStateSelected(true); } void InventoryWindow::onPinToggled() { MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned); } void InventoryWindow::onTitleDoubleClicked() { if (!mPinned) MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); } void InventoryWindow::useItem(const MWWorld::Ptr &ptr) { const std::string& script = ptr.getClass().getScript(ptr); MWWorld::Ptr player = MWMechanics::getPlayer(); // early-out for items that need to be equipped, but can't be equipped: we don't want to set OnPcEquip in that case if (!ptr.getClass().getEquipmentSlots(ptr).first.empty()) { std::pair canEquip = ptr.getClass().canBeEquipped(ptr, player); if (canEquip.first == 0) { MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second); updateItemView(); return; } } // If the item has a script, set its OnPcEquip to 1 if (!script.empty() // Another morrowind oddity: when an item has skipped equipping and pcskipequip is reset to 0 afterwards, // the next time it is equipped will work normally, but will not set onpcequip && (ptr != mSkippedToEquip || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1)) ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); // Give the script a chance to run once before we do anything else // this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this) if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled()) { MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); } mSkippedToEquip = MWWorld::Ptr(); if (ptr.getRefData().getCount()) // make sure the item is still there, the script might have removed it { if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0) { boost::shared_ptr action = ptr.getClass().use(ptr); action->execute (player); } else mSkippedToEquip = ptr; } if (isVisible()) { mItemView->update(); notifyContentChanged(); } // else: will be updated in open() } void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender) { if (mDragAndDrop->mIsOnDragAndDrop) { MWWorld::Ptr ptr = mDragAndDrop->mItem.mBase; mDragAndDrop->finish(); if (mDragAndDrop->mSourceModel != mTradeModel) { // Move item to the player's inventory ptr = mDragAndDrop->mSourceModel->moveItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount, mTradeModel); } useItem(ptr); } else { MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance ().getLastPressedPosition (MyGUI::MouseButton::Left); MyGUI::IntPoint relPos = mousePos - mAvatarImage->getAbsolutePosition (); MWWorld::Ptr itemSelected = getAvatarSelectedItem (relPos.left, relPos.top); if (itemSelected.isEmpty ()) return; for (size_t i=0; i < mTradeModel->getItemCount (); ++i) { if (mTradeModel->getItem(i).mBase == itemSelected) { onItemSelectedFromSourceModel(i); return; } } throw std::runtime_error("Can't find clicked item"); } } MWWorld::Ptr InventoryWindow::getAvatarSelectedItem(int x, int y) { // convert to OpenGL lower-left origin y = (mAvatarImage->getHeight()-1) - y; int slot = mPreview->getSlotSelected (x, y); if (slot == -1) return MWWorld::Ptr(); MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr); if(invStore.getSlot(slot) != invStore.end()) { MWWorld::Ptr item = *invStore.getSlot(slot); // NOTE: Don't allow users to select WerewolfRobe objects in the inventory. Vanilla // likely uses a hack like this since there's no other way to prevent it from being // taken. if(item.getCellRef().getRefId() == "werewolfrobe") return MWWorld::Ptr(); return item; } return MWWorld::Ptr(); } void InventoryWindow::updateEncumbranceBar() { MWWorld::Ptr player = MWMechanics::getPlayer(); float capacity = player.getClass().getCapacity(player); float encumbrance = player.getClass().getEncumbrance(player); mTradeModel->adjustEncumbrance(encumbrance); mEncumbranceBar->setValue(static_cast(encumbrance), static_cast(capacity)); } void InventoryWindow::onFrame() { if (!mMainWidget->getVisible()) return; updateEncumbranceBar(); } void InventoryWindow::setTrading(bool trading) { mTrading = trading; } void InventoryWindow::dirtyPreview() { mPreview->update(); updateArmorRating(); } void InventoryWindow::notifyContentChanged() { // update the spell window just in case new enchanted items were added to inventory MWBase::Environment::get().getWindowManager()->updateSpellWindow(); MWBase::Environment::get().getMechanicsManager()->updateMagicEffects( MWMechanics::getPlayer()); dirtyPreview(); } void InventoryWindow::pickUpObject (MWWorld::Ptr object) { // If the inventory is not yet enabled, don't pick anything up if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory)) return; // make sure the object is of a type that can be picked up std::string type = object.getTypeName(); if ( (type != typeid(ESM::Apparatus).name()) && (type != typeid(ESM::Armor).name()) && (type != typeid(ESM::Book).name()) && (type != typeid(ESM::Clothing).name()) && (type != typeid(ESM::Ingredient).name()) && (type != typeid(ESM::Light).name()) && (type != typeid(ESM::Miscellaneous).name()) && (type != typeid(ESM::Lockpick).name()) && (type != typeid(ESM::Probe).name()) && (type != typeid(ESM::Repair).name()) && (type != typeid(ESM::Weapon).name()) && (type != typeid(ESM::Potion).name())) return; if (object.getClass().getName(object) == "") // objects without name presented to user can never be picked up return; int count = object.getRefData().getCount(); MWWorld::Ptr player = MWMechanics::getPlayer(); MWBase::Environment::get().getWorld()->breakInvisibility(player); MWBase::Environment::get().getMechanicsManager()->itemTaken(player, object, MWWorld::Ptr(), count); // add to player inventory // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player); // remove from world MWBase::Environment::get().getWorld()->deleteObject (object); // get ModelIndex to the item mTradeModel->update(); size_t i=0; for (; igetItemCount(); ++i) { if (mTradeModel->getItem(i).mBase == newObject) break; } if (i == mTradeModel->getItemCount()) throw std::runtime_error("Added item not found"); mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count); MWBase::Environment::get().getWindowManager()->updateSpellWindow(); } void InventoryWindow::cycle(bool next) { ItemModel::ModelIndex selected = -1; // not using mSortFilterModel as we only need sorting, not filtering SortFilterItemModel model(new InventoryItemModel(MWMechanics::getPlayer())); model.setSortByType(false); model.update(); if (model.getItemCount() == 0) return; for (ItemModel::ModelIndex i=0; irebuild(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/inventorywindow.hpp000066400000000000000000000063631264522266000240250ustar00rootroot00000000000000#ifndef MGUI_Inventory_H #define MGUI_Inventory_H #include "windowpinnablebase.hpp" #include "mode.hpp" #include "../mwworld/ptr.hpp" namespace osgViewer { class Viewer; } namespace Resource { class ResourceSystem; } namespace MWRender { class InventoryPreview; } namespace MWGui { namespace Widgets { class MWDynamicStat; } class ItemView; class SortFilterItemModel; class TradeItemModel; class DragAndDrop; class ItemModel; class InventoryWindow : public WindowPinnableBase { public: InventoryWindow(DragAndDrop* dragAndDrop, osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem); virtual void open(); /// start trading, disables item drag&drop void setTrading(bool trading); void onFrame(); void pickUpObject (MWWorld::Ptr object); MWWorld::Ptr getAvatarSelectedItem(int x, int y); void rebuildAvatar(); SortFilterItemModel* getSortFilterModel(); TradeItemModel* getTradeModel(); ItemModel* getModel(); void updateItemView(); void updatePlayer(); void useItem(const MWWorld::Ptr& ptr); void setGuiMode(GuiMode mode); /// Cycle to previous/next weapon void cycle(bool next); private: DragAndDrop* mDragAndDrop; int mSelectedItem; MWWorld::Ptr mPtr; MWGui::ItemView* mItemView; SortFilterItemModel* mSortModel; TradeItemModel* mTradeModel; MyGUI::Widget* mAvatar; MyGUI::ImageBox* mAvatarImage; MyGUI::TextBox* mArmorRating; Widgets::MWDynamicStat* mEncumbranceBar; MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; MyGUI::Button* mFilterAll; MyGUI::Button* mFilterWeapon; MyGUI::Button* mFilterApparel; MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; MWWorld::Ptr mSkippedToEquip; GuiMode mGuiMode; int mLastXSize; int mLastYSize; std::auto_ptr mPreviewTexture; std::auto_ptr mPreview; bool mTrading; void onItemSelected(int index); void onItemSelectedFromSourceModel(int index); void onBackgroundSelected(); void sellItem(MyGUI::Widget* sender, int count); void dragItem(MyGUI::Widget* sender, int count); void onWindowResize(MyGUI::Window* _sender); void onFilterChanged(MyGUI::Widget* _sender); void onAvatarClicked(MyGUI::Widget* _sender); void onPinToggled(); void onTitleDoubleClicked(); void updateEncumbranceBar(); void notifyContentChanged(); void dirtyPreview(); void updatePreviewSize(); void updateArmorRating(); void adjustPanes(); /// Unequips mSelectedItem, if it is equipped, and then updates mSelectedItem in case it was re-stacked void ensureSelectedItemUnequipped(); }; } #endif // Inventory_H openmw-openmw-0.38.0/apps/openmw/mwgui/itemmodel.cpp000066400000000000000000000136021264522266000225040ustar00rootroot00000000000000#include "itemmodel.hpp" #include #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/store.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" namespace MWGui { ItemStack::ItemStack(const MWWorld::Ptr &base, ItemModel *creator, size_t count) : mType(Type_Normal) , mFlags(0) , mCreator(creator) , mCount(count) , mBase(base) { if (base.getClass().getEnchantment(base) != "") mFlags |= Flag_Enchanted; static std::set boundItemIDCache; // If this is empty then we haven't executed the GMST cache logic yet; or there isn't any sMagicBound* GMST's for some reason if (boundItemIDCache.empty()) { // Build a list of known bound item ID's const MWWorld::Store &gameSettings = MWBase::Environment::get().getWorld()->getStore().get(); for (MWWorld::Store::iterator currentIteration = gameSettings.begin(); currentIteration != gameSettings.end(); ++currentIteration) { const ESM::GameSetting ¤tSetting = *currentIteration; std::string currentGMSTID = currentSetting.mId; Misc::StringUtils::lowerCaseInPlace(currentGMSTID); // Don't bother checking this GMST if it's not a sMagicBound* one. const std::string& toFind = "smagicbound"; if (currentGMSTID.compare(0, toFind.length(), toFind) != 0) continue; // All sMagicBound* GMST's should be of type string std::string currentGMSTValue = currentSetting.getString(); Misc::StringUtils::lowerCaseInPlace(currentGMSTValue); boundItemIDCache.insert(currentGMSTValue); } } // Perform bound item check and assign the Flag_Bound bit if it passes std::string tempItemID = base.getCellRef().getRefId(); Misc::StringUtils::lowerCaseInPlace(tempItemID); if (boundItemIDCache.count(tempItemID) != 0) mFlags |= Flag_Bound; } ItemStack::ItemStack() : mType(Type_Normal) , mFlags(0) , mCreator(NULL) , mCount(0) { } bool ItemStack::stacks(const ItemStack &other) { if(mBase == other.mBase) return true; // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure if (mBase.getContainerStore() && other.mBase.getContainerStore()) return mBase.getContainerStore()->stacks(mBase, other.mBase) && other.mBase.getContainerStore()->stacks(mBase, other.mBase); if (mBase.getContainerStore()) return mBase.getContainerStore()->stacks(mBase, other.mBase); if (other.mBase.getContainerStore()) return other.mBase.getContainerStore()->stacks(mBase, other.mBase); MWWorld::ContainerStore store; return store.stacks(mBase, other.mBase); } bool operator == (const ItemStack& left, const ItemStack& right) { if (left.mType != right.mType) return false; if(left.mBase == right.mBase) return true; // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure if (left.mBase.getContainerStore() && right.mBase.getContainerStore()) return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase) && right.mBase.getContainerStore()->stacks(left.mBase, right.mBase); if (left.mBase.getContainerStore()) return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase); if (right.mBase.getContainerStore()) return right.mBase.getContainerStore()->stacks(left.mBase, right.mBase); MWWorld::ContainerStore store; return store.stacks(left.mBase, right.mBase); } ItemModel::ItemModel() { } MWWorld::Ptr ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel) { MWWorld::Ptr ret = otherModel->copyItem(item, count); removeItem(item, count); return ret; } ProxyItemModel::ProxyItemModel() : mSourceModel(NULL) { } ProxyItemModel::~ProxyItemModel() { delete mSourceModel; } MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) { return mSourceModel->copyItem (item, count, setNewOwner); } void ProxyItemModel::removeItem (const ItemStack& item, size_t count) { mSourceModel->removeItem (item, count); } ItemModel::ModelIndex ProxyItemModel::mapToSource (ModelIndex index) { const ItemStack& itemToSearch = getItem(index); for (size_t i=0; igetItemCount(); ++i) { const ItemStack& item = mSourceModel->getItem(i); if (item.mBase == itemToSearch.mBase) return i; } return -1; } ItemModel::ModelIndex ProxyItemModel::mapFromSource (ModelIndex index) { const ItemStack& itemToSearch = mSourceModel->getItem(index); for (size_t i=0; igetIndex(item); } void ProxyItemModel::setSourceModel(ItemModel *sourceModel) { if (mSourceModel == sourceModel) return; if (mSourceModel) { delete mSourceModel; mSourceModel = NULL; } mSourceModel = sourceModel; } } openmw-openmw-0.38.0/apps/openmw/mwgui/itemmodel.hpp000066400000000000000000000061751264522266000225200ustar00rootroot00000000000000#ifndef MWGUI_ITEM_MODEL_H #define MWGUI_ITEM_MODEL_H #include "../mwworld/ptr.hpp" namespace MWGui { class ItemModel; /// @brief A single item stack managed by an item model struct ItemStack { ItemStack (const MWWorld::Ptr& base, ItemModel* creator, size_t count); ItemStack(); bool stacks (const ItemStack& other); ///< like operator==, only without checking mType enum Type { Type_Barter, Type_Equipped, Type_Normal }; Type mType; enum Flags { Flag_Enchanted = (1<<0), Flag_Bound = (1<<1) }; int mFlags; ItemModel* mCreator; size_t mCount; MWWorld::Ptr mBase; }; bool operator == (const ItemStack& left, const ItemStack& right); /// @brief The base class that all item models should derive from. class ItemModel { public: ItemModel(); virtual ~ItemModel() {} typedef int ModelIndex; // -1 means invalid index /// Throws for invalid index or out of range index virtual ItemStack getItem (ModelIndex index) = 0; /// The number of items in the model, this specifies the range of indices you can pass to /// the getItem function (but this range is only valid until the next call to update()) virtual size_t getItemCount() = 0; /// Returns an invalid index if the item was not found virtual ModelIndex getIndex (ItemStack item) = 0; /// Rebuild the item model, this will invalidate existing model indices virtual void update() = 0; /// Move items from this model to \a otherModel. /// @note Derived implementations may return an empty Ptr if the move was unsuccessful. virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); /// @param setNewOwner If true, set the copied item's owner to the actor we are copying to, /// otherwise reset owner to "" virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0; private: ItemModel(const ItemModel&); ItemModel& operator=(const ItemModel&); }; /// @brief A proxy item model can be used to filter or rearrange items from a source model (or even add new items to it). /// The neat thing is that this does not actually alter the source model. class ProxyItemModel : public ItemModel { public: ProxyItemModel(); virtual ~ProxyItemModel(); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); virtual ModelIndex getIndex (ItemStack item); /// @note Takes ownership of the passed pointer. void setSourceModel(ItemModel* sourceModel); ModelIndex mapToSource (ModelIndex index); ModelIndex mapFromSource (ModelIndex index); protected: ItemModel* mSourceModel; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/itemselection.cpp000066400000000000000000000033361264522266000233740ustar00rootroot00000000000000#include "itemselection.hpp" #include #include #include "itemview.hpp" #include "inventoryitemmodel.hpp" #include "sortfilteritemmodel.hpp" namespace MWGui { ItemSelectionDialog::ItemSelectionDialog(const std::string &label) : WindowModal("openmw_itemselection_dialog.layout") , mSortModel(NULL) , mModel(NULL) { getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &ItemSelectionDialog::onSelectedItem); MyGUI::TextBox* l; getWidget(l, "Label"); l->setCaptionWithReplacing (label); MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemSelectionDialog::onCancelButtonClicked); center(); } void ItemSelectionDialog::exit() { eventDialogCanceled(); } void ItemSelectionDialog::openContainer(const MWWorld::Ptr& container) { mModel = new InventoryItemModel(container); mSortModel = new SortFilterItemModel(mModel); mItemView->setModel(mSortModel); mItemView->resetScrollBars(); } void ItemSelectionDialog::setCategory(int category) { mSortModel->setCategory(category); mItemView->update(); } void ItemSelectionDialog::setFilter(int filter) { mSortModel->setFilter(filter); mItemView->update(); } void ItemSelectionDialog::onSelectedItem(int index) { ItemStack item = mSortModel->getItem(index); eventItemSelected(item.mBase); } void ItemSelectionDialog::onCancelButtonClicked(MyGUI::Widget* sender) { exit(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/itemselection.hpp000066400000000000000000000020331264522266000233720ustar00rootroot00000000000000#ifndef OPENMW_GAME_MWGUI_ITEMSELECTION_H #define OPENMW_GAME_MWGUI_ITEMSELECTION_H #include #include "windowbase.hpp" namespace MWWorld { class Ptr; } namespace MWGui { class ItemView; class SortFilterItemModel; class InventoryItemModel; class ItemSelectionDialog : public WindowModal { public: ItemSelectionDialog(const std::string& label); virtual void exit(); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Item; EventHandle_Item eventItemSelected; EventHandle_Void eventDialogCanceled; void openContainer (const MWWorld::Ptr& container); void setCategory(int category); void setFilter(int filter); private: ItemView* mItemView; SortFilterItemModel* mSortModel; InventoryItemModel* mModel; void onSelectedItem(int index); void onCancelButtonClicked(MyGUI::Widget* sender); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/itemview.cpp000066400000000000000000000115761264522266000223660ustar00rootroot00000000000000#include "itemview.hpp" #include #include #include #include #include #include #include #include "../mwworld/class.hpp" #include "itemmodel.hpp" #include "itemwidget.hpp" namespace MWGui { ItemView::ItemView() : mModel(NULL) , mScrollView(NULL) { } ItemView::~ItemView() { delete mModel; } void ItemView::setModel(ItemModel *model) { if (mModel == model) return; delete mModel; mModel = model; update(); } void ItemView::initialiseOverride() { Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); if (mScrollView == NULL) throw std::runtime_error("Item view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); } void ItemView::layoutWidgets() { if (!mScrollView->getChildCount()) return; int x = 0; int y = 0; MyGUI::Widget* dragArea = mScrollView->getChildAt(0); int maxHeight = mScrollView->getHeight(); int rows = maxHeight/42; rows = std::max(rows, 1); bool showScrollbar = int(std::ceil(dragArea->getChildCount()/float(rows))) > mScrollView->getWidth()/42; if (showScrollbar) maxHeight -= 18; for (unsigned int i=0; igetChildCount(); ++i) { MyGUI::Widget* w = dragArea->getChildAt(i); w->setPosition(x, y); y += 42; if (y > maxHeight-42 && i < dragArea->getChildCount()-1) { x += 42; y = 0; } } x += 42; MyGUI::IntSize size = MyGUI::IntSize(std::max(mScrollView->getSize().width, x), mScrollView->getSize().height); // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mScrollView->setVisibleVScroll(false); mScrollView->setVisibleHScroll(false); mScrollView->setCanvasSize(size); mScrollView->setVisibleVScroll(true); mScrollView->setVisibleHScroll(true); dragArea->setSize(size); } void ItemView::update() { while (mScrollView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); if (!mModel) return; mModel->update(); MyGUI::Widget* dragArea = mScrollView->createWidget("",0,0,mScrollView->getWidth(),mScrollView->getHeight(), MyGUI::Align::Stretch); dragArea->setNeedMouseFocus(true); dragArea->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedBackground); dragArea->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheelMoved); for (ItemModel::ModelIndex i=0; i(mModel->getItemCount()); ++i) { const ItemStack& item = mModel->getItem(i); ItemWidget* itemWidget = dragArea->createWidget("MW_ItemIcon", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default); itemWidget->setUserString("ToolTipType", "ItemModelIndex"); itemWidget->setUserData(std::make_pair(i, mModel)); ItemWidget::ItemState state = ItemWidget::None; if (item.mType == ItemStack::Type_Barter) state = ItemWidget::Barter; if (item.mType == ItemStack::Type_Equipped) state = ItemWidget::Equip; itemWidget->setItem(item.mBase, state); itemWidget->setCount(item.mCount); itemWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedItem); itemWidget->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheelMoved); } layoutWidgets(); } void ItemView::resetScrollBars() { mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); } void ItemView::onSelectedItem(MyGUI::Widget *sender) { ItemModel::ModelIndex index = (*sender->getUserData >()).first; eventItemClicked(index); } void ItemView::onSelectedBackground(MyGUI::Widget *sender) { eventBackgroundClicked(); } void ItemView::onMouseWheelMoved(MyGUI::Widget *_sender, int _rel) { if (mScrollView->getViewOffset().left + _rel*0.3f > 0) mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); else mScrollView->setViewOffset(MyGUI::IntPoint(static_cast(mScrollView->getViewOffset().left + _rel*0.3f), 0)); } void ItemView::setSize(const MyGUI::IntSize &_value) { bool changed = (_value.width != getWidth() || _value.height != getHeight()); Base::setSize(_value); if (changed) layoutWidgets(); } void ItemView::setCoord(const MyGUI::IntCoord &_value) { bool changed = (_value.width != getWidth() || _value.height != getHeight()); Base::setCoord(_value); if (changed) layoutWidgets(); } void ItemView::registerComponents() { MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } } openmw-openmw-0.38.0/apps/openmw/mwgui/itemview.hpp000066400000000000000000000025421264522266000223640ustar00rootroot00000000000000#ifndef MWGUI_ITEMVIEW_H #define MWGUI_ITEMVIEW_H #include #include "itemmodel.hpp" namespace MWGui { class ItemView : public MyGUI::Widget { MYGUI_RTTI_DERIVED(ItemView) public: ItemView(); virtual ~ItemView(); /// Register needed components with MyGUI's factory manager static void registerComponents (); /// Takes ownership of \a model void setModel (ItemModel* model); typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ModelIndex; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /// Fired when an item was clicked EventHandle_ModelIndex eventItemClicked; /// Fired when the background was clicked (useful for drag and drop) EventHandle_Void eventBackgroundClicked; void update(); void resetScrollBars(); private: virtual void initialiseOverride(); void layoutWidgets(); virtual void setSize(const MyGUI::IntSize& _value); virtual void setCoord(const MyGUI::IntCoord& _value); void onSelectedItem (MyGUI::Widget* sender); void onSelectedBackground (MyGUI::Widget* sender); void onMouseWheelMoved(MyGUI::Widget* _sender, int _rel); ItemModel* mModel; MyGUI::ScrollView* mScrollView; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/itemwidget.cpp000066400000000000000000000057721264522266000227000ustar00rootroot00000000000000#include "itemwidget.hpp" #include #include #include // correctIconPath #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" namespace { std::string getCountString(int count) { if (count == 1) return ""; if (count > 9999) return MyGUI::utility::toString(int(count/1000.f)) + "k"; else return MyGUI::utility::toString(count); } } namespace MWGui { ItemWidget::ItemWidget() : mItem(NULL) , mFrame(NULL) , mText(NULL) { } void ItemWidget::registerComponents() { MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } void ItemWidget::initialiseOverride() { assignWidget(mItem, "Item"); if (mItem) mItem->setNeedMouseFocus(false); assignWidget(mFrame, "Frame"); if (mFrame) mFrame->setNeedMouseFocus(false); assignWidget(mText, "Text"); if (mText) mText->setNeedMouseFocus(false); Base::initialiseOverride(); } void ItemWidget::setCount(int count) { if (!mText) return; mText->setCaption(getCountString(count)); } void ItemWidget::setIcon(const std::string &icon) { if (mItem) mItem->setImageTexture(icon); } void ItemWidget::setFrame(const std::string &frame, const MyGUI::IntCoord &coord) { if (mFrame) { mFrame->setImageTexture(frame); mFrame->setImageTile(MyGUI::IntSize(coord.width, coord.height)); // Why is this needed? MyGUI bug? mFrame->setImageCoord(coord); } } void ItemWidget::setIcon(const MWWorld::Ptr &ptr) { setIcon(MWBase::Environment::get().getWindowManager()->correctIconPath(ptr.getClass().getInventoryIcon(ptr))); } void ItemWidget::setItem(const MWWorld::Ptr &ptr, ItemState state) { if (!mItem) return; if (ptr.isEmpty()) { if (mFrame) mFrame->setImageTexture(""); mItem->setImageTexture(""); mText->setCaption(""); return; } bool isMagic = !ptr.getClass().getEnchantment(ptr).empty(); std::string backgroundTex = "textures\\menu_icon"; if (isMagic) backgroundTex += "_magic"; if (state == None) { if (!isMagic) backgroundTex = ""; } else if (state == Equip) { backgroundTex += "_equip"; } else if (state == Barter) backgroundTex += "_barter"; if (backgroundTex != "") backgroundTex += ".dds"; if (state == Barter && !isMagic) setFrame(backgroundTex, MyGUI::IntCoord(2,2,42,42)); else setFrame(backgroundTex, MyGUI::IntCoord(0,0,42,42)); setIcon(ptr); } } openmw-openmw-0.38.0/apps/openmw/mwgui/itemwidget.hpp000066400000000000000000000022101264522266000226650ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_ITEMWIDGET_H #define OPENMW_MWGUI_ITEMWIDGET_H #include namespace MWWorld { class Ptr; } namespace MWGui { /// @brief A widget that shows an icon for an MWWorld::Ptr class ItemWidget : public MyGUI::Widget { MYGUI_RTTI_DERIVED(ItemWidget) public: ItemWidget(); /// Register needed components with MyGUI's factory manager static void registerComponents (); enum ItemState { None, Equip, Barter, Magic }; /// Set count to be displayed in a textbox over the item void setCount(int count); /// \a ptr may be empty void setItem (const MWWorld::Ptr& ptr, ItemState state = None); // Set icon and frame manually void setIcon (const std::string& icon); void setIcon (const MWWorld::Ptr& ptr); void setFrame (const std::string& frame, const MyGUI::IntCoord& coord); private: virtual void initialiseOverride(); MyGUI::ImageBox* mItem; MyGUI::ImageBox* mFrame; MyGUI::TextBox* mText; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/jailscreen.cpp000066400000000000000000000105701264522266000226450ustar00rootroot00000000000000#include #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/store.hpp" #include "../mwworld/class.hpp" #include "jailscreen.hpp" namespace MWGui { JailScreen::JailScreen() : WindowBase("openmw_jail_screen.layout"), mDays(1), mFadeTimeRemaining(0), mTimeAdvancer(0.01f) { getWidget(mProgressBar, "ProgressBar"); setVisible(false); mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &JailScreen::onJailProgressChanged); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &JailScreen::onJailFinished); center(); } void JailScreen::goToJail(int days) { mDays = days; MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); mFadeTimeRemaining = 0.5; setVisible(false); mProgressBar->setScrollRange(100+1); mProgressBar->setScrollPosition(0); mProgressBar->setTrackSize(0); } void JailScreen::onFrame(float dt) { mTimeAdvancer.onFrame(dt); if (mFadeTimeRemaining <= 0) return; mFadeTimeRemaining -= dt; if (mFadeTimeRemaining <= 0) { MWWorld::Ptr player = MWMechanics::getPlayer(); MWBase::Environment::get().getWorld()->teleportToClosestMarker(player, "prisonmarker"); setVisible(true); mTimeAdvancer.run(100); } } void JailScreen::onJailProgressChanged(int cur, int /*total*/) { mProgressBar->setScrollPosition(0); mProgressBar->setTrackSize(static_cast(cur / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize())); } void JailScreen::onJailFinished() { MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Jail); MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); MWWorld::Ptr player = MWMechanics::getPlayer(); MWBase::Environment::get().getWorld()->advanceTime(mDays * 24); for (int i=0; irest(true); std::set skills; for (int day=0; day& gmst = MWBase::Environment::get().getWorld()->getStore().get(); std::string message; if (mDays == 1) message = gmst.find("sNotifyMessage42")->getString(); else message = gmst.find("sNotifyMessage43")->getString(); std::stringstream dayStr; dayStr << mDays; if (message.find("%d") != std::string::npos) message.replace(message.find("%d"), 2, dayStr.str()); for (std::set::iterator it = skills.begin(); it != skills.end(); ++it) { std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->getString(); std::stringstream skillValue; skillValue << player.getClass().getNpcStats(player).getSkill(*it).getBase(); std::string skillMsg = gmst.find("sNotifyMessage44")->getString(); if (*it == ESM::Skill::Sneak || *it == ESM::Skill::Security) skillMsg = gmst.find("sNotifyMessage39")->getString(); if (skillMsg.find("%s") != std::string::npos) skillMsg.replace(skillMsg.find("%s"), 2, skillName); if (skillMsg.find("%d") != std::string::npos) skillMsg.replace(skillMsg.find("%d"), 2, skillValue.str()); message += "\n" + skillMsg; } std::vector buttons; buttons.push_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons); } } openmw-openmw-0.38.0/apps/openmw/mwgui/jailscreen.hpp000066400000000000000000000010721264522266000226470ustar00rootroot00000000000000#ifndef MWGUI_JAILSCREEN_H #define MWGUI_JAILSCREEN_H #include "windowbase.hpp" #include "timeadvancer.hpp" namespace MWGui { class JailScreen : public WindowBase { public: JailScreen(); void goToJail(int days); void onFrame(float dt); private: int mDays; float mFadeTimeRemaining; MyGUI::ScrollBar* mProgressBar; void onJailProgressChanged(int cur, int total); void onJailFinished(); TimeAdvancer mTimeAdvancer; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/journalbooks.cpp000066400000000000000000000175551264522266000232500ustar00rootroot00000000000000#include "journalbooks.hpp" #include namespace { MyGUI::Colour getTextColour (const std::string& type) { return MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=" + type + "}")); } struct AddContent { MWGui::BookTypesetter::Ptr mTypesetter; MWGui::BookTypesetter::Style* mBodyStyle; AddContent (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) : mTypesetter (typesetter), mBodyStyle (body_style) { } }; struct AddSpan : AddContent { AddSpan (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) : AddContent (typesetter, body_style) { } void operator () (intptr_t topicId, size_t begin, size_t end) { MWGui::BookTypesetter::Style* style = mBodyStyle; static const MyGUI::Colour linkHot (getTextColour("journal_link_over")); static const MyGUI::Colour linkNormal (getTextColour("journal_link")); static const MyGUI::Colour linkActive (getTextColour("journal_link_pressed")); if (topicId) style = mTypesetter->createHotStyle (mBodyStyle, linkNormal, linkHot, linkActive, topicId); mTypesetter->write (style, begin, end); } }; struct AddEntry { MWGui::BookTypesetter::Ptr mTypesetter; MWGui::BookTypesetter::Style* mBodyStyle; AddEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) : mTypesetter (typesetter), mBodyStyle (body_style) { } void operator () (MWGui::JournalViewModel::Entry const & entry) { mTypesetter->addContent (entry.body ()); entry.visitSpans (AddSpan (mTypesetter, mBodyStyle)); } }; struct AddJournalEntry : AddEntry { bool mAddHeader; MWGui::BookTypesetter::Style* mHeaderStyle; AddJournalEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style, MWGui::BookTypesetter::Style* header_style, bool add_header) : AddEntry (typesetter, body_style), mAddHeader (add_header), mHeaderStyle (header_style) { } void operator () (MWGui::JournalViewModel::JournalEntry const & entry) { if (mAddHeader) { mTypesetter->write (mHeaderStyle, entry.timestamp ()); mTypesetter->lineBreak (); } AddEntry::operator () (entry); mTypesetter->sectionBreak (10); } }; struct AddTopicEntry : AddEntry { intptr_t mContentId; MWGui::BookTypesetter::Style* mHeaderStyle; AddTopicEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style, MWGui::BookTypesetter::Style* header_style, intptr_t contentId) : AddEntry (typesetter, body_style), mContentId (contentId), mHeaderStyle (header_style) { } void operator () (MWGui::JournalViewModel::TopicEntry const & entry) { mTypesetter->write (mBodyStyle, entry.source ()); mTypesetter->write (mBodyStyle, 0, 3);// begin AddEntry::operator() (entry); mTypesetter->selectContent (mContentId); mTypesetter->write (mBodyStyle, 2, 3);// end quote mTypesetter->sectionBreak (10); } }; struct AddTopicName : AddContent { AddTopicName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : AddContent (typesetter, style) { } void operator () (MWGui::JournalViewModel::Utf8Span topicName) { mTypesetter->write (mBodyStyle, topicName); mTypesetter->sectionBreak (10); } }; struct AddQuestName : AddContent { AddQuestName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : AddContent (typesetter, style) { } void operator () (MWGui::JournalViewModel::Utf8Span topicName) { mTypesetter->write (mBodyStyle, topicName); mTypesetter->sectionBreak (10); } }; } namespace MWGui { MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) { typedef MWGui::BookTypesetter::Utf8Point point; point begin = reinterpret_cast (text); return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); } typedef TypesetBook::Ptr book; JournalBooks::JournalBooks (JournalViewModel::Ptr model) : mModel (model) { } book JournalBooks::createEmptyJournalBook () { BookTypesetter::Ptr typesetter = createTypesetter (); BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); typesetter->write (header, to_utf8_span ("You have no journal entries!")); typesetter->lineBreak (); typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest.")); return typesetter->complete (); } book JournalBooks::createJournalBook () { BookTypesetter::Ptr typesetter = createTypesetter (); BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); mModel->visitJournalEntries ("", AddJournalEntry (typesetter, body, header, true)); return typesetter->complete (); } book JournalBooks::createTopicBook (uintptr_t topicId) { BookTypesetter::Ptr typesetter = createTypesetter (); BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); mModel->visitTopicName (topicId, AddTopicName (typesetter, header)); intptr_t contentId = typesetter->addContent (to_utf8_span (", \"")); mModel->visitTopicEntries (topicId, AddTopicEntry (typesetter, body, header, contentId)); return typesetter->complete (); } book JournalBooks::createQuestBook (const std::string& questName) { BookTypesetter::Ptr typesetter = createTypesetter (); BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); AddQuestName addName (typesetter, header); addName(to_utf8_span(questName.c_str())); mModel->visitJournalEntries (questName, AddJournalEntry (typesetter, body, header, false)); return typesetter->complete (); } book JournalBooks::createTopicIndexBook () { BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); for (int i = 0; i < 26; ++i) { char ch = 'A' + i; char buffer [32]; sprintf (buffer, "( %c )", ch); MyGUI::Colour linkHot (getTextColour("journal_topic_over")); MyGUI::Colour linkActive (getTextColour("journal_topic_pressed")); MyGUI::Colour linkNormal (getTextColour("journal_topic")); BookTypesetter::Style* style = typesetter->createHotStyle (body, linkNormal, linkHot, linkActive, ch); if (i == 13) typesetter->sectionBreak (); typesetter->write (style, to_utf8_span (buffer)); typesetter->lineBreak (); } return typesetter->complete (); } BookTypesetter::Ptr JournalBooks::createTypesetter () { //TODO: determine page size from layout... return BookTypesetter::create (240, 300); } } openmw-openmw-0.38.0/apps/openmw/mwgui/journalbooks.hpp000066400000000000000000000013671264522266000232470ustar00rootroot00000000000000#ifndef MWGUI_JOURNALBOOKS_HPP #define MWGUI_JOURNALBOOKS_HPP #include "bookpage.hpp" #include "journalviewmodel.hpp" namespace MWGui { MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text); struct JournalBooks { typedef TypesetBook::Ptr Book; JournalViewModel::Ptr mModel; JournalBooks (JournalViewModel::Ptr model); Book createEmptyJournalBook (); Book createJournalBook (); Book createTopicBook (uintptr_t topicId); Book createTopicBook (const std::string& topicId); Book createQuestBook (const std::string& questName); Book createTopicIndexBook (); private: BookTypesetter::Ptr createTypesetter (); }; } #endif // MWGUI_JOURNALBOOKS_HPP openmw-openmw-0.38.0/apps/openmw/mwgui/journalviewmodel.cpp000066400000000000000000000277151264522266000241250ustar00rootroot00000000000000#include "journalviewmodel.hpp" #include #include #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwdialogue/journalentry.hpp" #include "../mwdialogue/keywordsearch.hpp" namespace MWGui { struct JournalViewModelImpl; struct JournalViewModelImpl : JournalViewModel { typedef MWDialogue::KeywordSearch KeywordSearchT; mutable bool mKeywordSearchLoaded; mutable KeywordSearchT mKeywordSearch; JournalViewModelImpl () { mKeywordSearchLoaded = false; } virtual ~JournalViewModelImpl () { } /// \todo replace this nasty BS static Utf8Span toUtf8Span (std::string const & str) { if (str.size () == 0) return Utf8Span (Utf8Point (NULL), Utf8Point (NULL)); Utf8Point point = reinterpret_cast (str.c_str ()); return Utf8Span (point, point + str.size ()); } void load () { } void unload () { mKeywordSearch.clear (); mKeywordSearchLoaded = false; } void ensureKeyWordSearchLoaded () const { if (!mKeywordSearchLoaded) { MWBase::Journal * journal = MWBase::Environment::get().getJournal(); for(MWBase::Journal::TTopicIter i = journal->topicBegin(); i != journal->topicEnd (); ++i) mKeywordSearch.seed (i->first, intptr_t (&i->second)); mKeywordSearchLoaded = true; } } bool isEmpty () const { MWBase::Journal * journal = MWBase::Environment::get().getJournal(); return journal->begin () == journal->end (); } template struct BaseEntry : Interface { typedef t_iterator iterator_t; iterator_t itr; JournalViewModelImpl const * mModel; BaseEntry (JournalViewModelImpl const * model, iterator_t itr) : itr (itr), mModel (model), loaded (false) {} virtual ~BaseEntry () {} mutable bool loaded; mutable std::string utf8text; typedef std::pair Range; // hyperlinks in @link# notation mutable std::map mHyperLinks; virtual std::string getText () const = 0; void ensureLoaded () const { if (!loaded) { mModel->ensureKeyWordSearchLoaded (); utf8text = getText (); size_t pos_end = 0; for(;;) { size_t pos_begin = utf8text.find('@'); if (pos_begin != std::string::npos) pos_end = utf8text.find('#', pos_begin); if (pos_begin != std::string::npos && pos_end != std::string::npos) { std::string link = utf8text.substr(pos_begin + 1, pos_end - pos_begin - 1); const char specialPseudoAsteriskCharacter = 127; std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*'); std::string topicName = MWBase::Environment::get().getWindowManager()-> getTranslationDataStorage().topicStandardForm(link); std::string displayName = link; while (displayName[displayName.size()-1] == '*') displayName.erase(displayName.size()-1, 1); utf8text.replace(pos_begin, pos_end+1-pos_begin, displayName); intptr_t value; if (mModel->mKeywordSearch.containsKeyword(topicName, value)) mHyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = value; } else break; } loaded = true; } } Utf8Span body () const { ensureLoaded (); return toUtf8Span (utf8text); } void visitSpans (boost::function < void (TopicId, size_t, size_t)> visitor) const { ensureLoaded (); mModel->ensureKeyWordSearchLoaded (); if (mHyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) { size_t formatted = 0; // points to the first character that is not laid out yet for (std::map::const_iterator it = mHyperLinks.begin(); it != mHyperLinks.end(); ++it) { intptr_t topicId = it->second; if (formatted < it->first.first) visitor (0, formatted, it->first.first); visitor (topicId, it->first.first, it->first.second); formatted = it->first.second; } if (formatted < utf8text.size()) visitor (0, formatted, utf8text.size()); } else { std::vector matches; mModel->mKeywordSearch.highlightKeywords(utf8text.begin(), utf8text.end(), matches); std::string::const_iterator i = utf8text.begin (); for (std::vector::const_iterator it = matches.begin(); it != matches.end(); ++it) { const KeywordSearchT::Match& match = *it; if (i != match.mBeg) visitor (0, i - utf8text.begin (), match.mBeg - utf8text.begin ()); visitor (match.mValue, match.mBeg - utf8text.begin (), match.mEnd - utf8text.begin ()); i = match.mEnd; } if (i != utf8text.end ()) visitor (0, i - utf8text.begin (), utf8text.size ()); } } }; void visitQuestNames (bool active_only, boost::function visitor) const { MWBase::Journal * journal = MWBase::Environment::get ().getJournal (); std::set visitedQuests; // Note that for purposes of the journal GUI, quests are identified by the name, not the ID, so several // different quest IDs can end up in the same quest log. A quest log should be considered finished // when any quest ID in that log is finished. for (MWBase::Journal::TQuestIter i = journal->questBegin (); i != journal->questEnd (); ++i) { const MWDialogue::Quest& quest = i->second; bool isFinished = false; for (MWBase::Journal::TQuestIter j = journal->questBegin (); j != journal->questEnd (); ++j) { if (quest.getName() == j->second.getName() && j->second.isFinished()) isFinished = true; } if (active_only && isFinished) continue; // Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal. // Note that even with Tribunal, some quests still don't have quest names. I'm assuming those are not supposed // to appear in the quest book. if (!quest.getName().empty()) { // Don't list the same quest name twice if (visitedQuests.find(quest.getName()) != visitedQuests.end()) continue; visitor (quest.getName(), isFinished); visitedQuests.insert(quest.getName()); } } } template struct JournalEntryImpl : BaseEntry { using BaseEntry ::itr; mutable std::string timestamp_buffer; JournalEntryImpl (JournalViewModelImpl const * model, iterator_t itr) : BaseEntry (model, itr) {} std::string getText () const { return itr->getText(); } Utf8Span timestamp () const { if (timestamp_buffer.empty ()) { std::string dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}"); std::ostringstream os; os << itr->mDayOfMonth << ' ' << MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth) << " (" << dayStr << " " << (itr->mDay) << ')'; timestamp_buffer = os.str (); } return toUtf8Span (timestamp_buffer); } }; void visitJournalEntries (const std::string& questName, boost::function visitor) const { MWBase::Journal * journal = MWBase::Environment::get().getJournal(); if (!questName.empty()) { std::vector quests; for (MWBase::Journal::TQuestIter questIt = journal->questBegin(); questIt != journal->questEnd(); ++questIt) { if (Misc::StringUtils::ciEqual(questIt->second.getName(), questName)) quests.push_back(&questIt->second); } for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i) { for (std::vector::iterator questIt = quests.begin(); questIt != quests.end(); ++questIt) { MWDialogue::Quest const* quest = *questIt; for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j) { if (i->mInfoId == j->mInfoId) visitor (JournalEntryImpl (this, i)); } } } } else { for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i) visitor (JournalEntryImpl (this, i)); } } void visitTopicName (TopicId topicId, boost::function visitor) const { MWDialogue::Topic const & topic = * reinterpret_cast (topicId); visitor (toUtf8Span (topic.getName())); } void visitTopicNamesStartingWith (char character, boost::function < void (const std::string&) > visitor) const { MWBase::Journal * journal = MWBase::Environment::get().getJournal(); for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i) { if (i->first [0] != Misc::StringUtils::toLower(character)) continue; visitor (i->second.getName()); } } struct TopicEntryImpl : BaseEntry { MWDialogue::Topic const & mTopic; TopicEntryImpl (JournalViewModelImpl const * model, MWDialogue::Topic const & topic, iterator_t itr) : BaseEntry (model, itr), mTopic (topic) {} std::string getText () const { return itr->getText(); } Utf8Span source () const { return toUtf8Span (itr->mActorName); } }; void visitTopicEntries (TopicId topicId, boost::function visitor) const { typedef MWDialogue::Topic::TEntryIter iterator_t; MWDialogue::Topic const & topic = * reinterpret_cast (topicId); for (iterator_t i = topic.begin (); i != topic.end (); ++i) visitor (TopicEntryImpl (this, topic, i)); } }; JournalViewModel::Ptr JournalViewModel::create () { return boost::make_shared (); } } openmw-openmw-0.38.0/apps/openmw/mwgui/journalviewmodel.hpp000066400000000000000000000071531264522266000241240ustar00rootroot00000000000000#ifndef MWGUI_JOURNALVIEWMODEL_HPP #define MWGUI_JOURNALVIEWMODEL_HPP #include #include #include #include #include #include namespace MWGui { /// View-Model for the journal GUI /// /// This interface defines an abstract data model suited /// specifically to the needs of the journal GUI. It isolates /// the journal GUI from the implementation details of the /// game data store. struct JournalViewModel { typedef boost::shared_ptr Ptr; typedef intptr_t QuestId; typedef intptr_t TopicId; typedef uint8_t const * Utf8Point; typedef std::pair Utf8Span; /// The base interface for both journal entries and topics. struct Entry { /// returns the body text for the journal entry /// /// This function returns a borrowed reference to the body of the /// journal entry. The returned reference becomes invalid when the /// entry is destroyed. virtual Utf8Span body () const = 0; /// Visits each subset of text in the body, delivering the beginning /// and end of the span relative to the body, and a valid topic ID if /// the span represents a keyword, or zero if not. virtual void visitSpans (boost::function visitor) const = 0; }; /// An interface to topic data. struct TopicEntry : Entry { /// Returns a pre-formatted span of UTF8 encoded text representing /// the name of the NPC this portion of dialog was heard from. virtual Utf8Span source () const = 0; }; /// An interface to journal data. struct JournalEntry : Entry { /// Returns a pre-formatted span of UTF8 encoded text representing /// the in-game date this entry was added to the journal. virtual Utf8Span timestamp () const = 0; }; /// called prior to journal opening virtual void load () = 0; /// called prior to journal closing virtual void unload () = 0; /// returns true if their are no journal entries to display virtual bool isEmpty () const = 0; /// walks the active and optionally completed, quests providing the name and completed status virtual void visitQuestNames (bool active_only, boost::function visitor) const = 0; /// walks over the journal entries related to all quests with the given name /// If \a questName is empty, simply visits all journal entries virtual void visitJournalEntries (const std::string& questName, boost::function visitor) const = 0; /// provides the name of the topic specified by its id virtual void visitTopicName (TopicId topicId, boost::function visitor) const = 0; /// walks over the topics whose names start with the specified character providing the topics name virtual void visitTopicNamesStartingWith (char character, boost::function < void (const std::string&) > visitor) const = 0; /// walks over the topic entries for the topic specified by its identifier virtual void visitTopicEntries (TopicId topicId, boost::function visitor) const = 0; // create an instance of the default journal view model implementation static Ptr create (); }; } #endif // MWGUI_JOURNALVIEWMODEL_HPP openmw-openmw-0.38.0/apps/openmw/mwgui/journalwindow.cpp000066400000000000000000000456751264522266000234460ustar00rootroot00000000000000#include "journalwindow.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/journal.hpp" #include "bookpage.hpp" #include "windowbase.hpp" #include "journalviewmodel.hpp" #include "journalbooks.hpp" namespace { static char const OptionsOverlay [] = "OptionsOverlay"; static char const OptionsBTN [] = "OptionsBTN"; static char const PrevPageBTN [] = "PrevPageBTN"; static char const NextPageBTN [] = "NextPageBTN"; static char const CloseBTN [] = "CloseBTN"; static char const JournalBTN [] = "JournalBTN"; static char const TopicsBTN [] = "TopicsBTN"; static char const QuestsBTN [] = "QuestsBTN"; static char const CancelBTN [] = "CancelBTN"; static char const ShowAllBTN [] = "ShowAllBTN"; static char const ShowActiveBTN [] = "ShowActiveBTN"; static char const PageOneNum [] = "PageOneNum"; static char const PageTwoNum [] = "PageTwoNum"; static char const TopicsList [] = "TopicsList"; static char const QuestsList [] = "QuestsList"; static char const LeftBookPage [] = "LeftBookPage"; static char const RightBookPage [] = "RightBookPage"; static char const LeftTopicIndex [] = "LeftTopicIndex"; static char const RightTopicIndex [] = "RightTopicIndex"; struct JournalWindowImpl : MWGui::WindowBase, MWGui::JournalBooks, MWGui::JournalWindow { struct DisplayState { unsigned int mPage; Book mBook; }; typedef std::stack DisplayStateStack; DisplayStateStack mStates; Book mTopicIndexBook; bool mQuestMode; bool mOptionsMode; bool mAllQuests; template T * getWidget (char const * name) { T * widget; WindowBase::getWidget (widget, name); return widget; } template void setText (char const * name, value_type const & value) { getWidget (name) -> setCaption (MyGUI::utility::toString (value)); } void setVisible (char const * name, bool visible) { getWidget (name) -> setVisible (visible); } void adviseButtonClick (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender)) { getWidget (name) -> eventMouseButtonClick += newDelegate(this, Handler); } MWGui::BookPage* getPage (char const * name) { return getWidget (name); } JournalWindowImpl (MWGui::JournalViewModel::Ptr Model, bool questList) : WindowBase("openmw_journal.layout"), JournalBooks (Model) { mMainWidget->setVisible(false); center(); adviseButtonClick (OptionsBTN, &JournalWindowImpl::notifyOptions ); adviseButtonClick (PrevPageBTN, &JournalWindowImpl::notifyPrevPage ); adviseButtonClick (NextPageBTN, &JournalWindowImpl::notifyNextPage ); adviseButtonClick (CloseBTN, &JournalWindowImpl::notifyClose ); adviseButtonClick (JournalBTN, &JournalWindowImpl::notifyJournal ); adviseButtonClick (TopicsBTN, &JournalWindowImpl::notifyTopics ); adviseButtonClick (QuestsBTN, &JournalWindowImpl::notifyQuests ); adviseButtonClick (CancelBTN, &JournalWindowImpl::notifyCancel ); adviseButtonClick (ShowAllBTN, &JournalWindowImpl::notifyShowAll ); adviseButtonClick (ShowActiveBTN, &JournalWindowImpl::notifyShowActive); Gui::MWList* list = getWidget(QuestsList); list->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyQuestClicked); Gui::MWList* topicsList = getWidget(TopicsList); topicsList->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyTopicSelected); { MWGui::BookPage::ClickCallback callback; callback = boost::bind (&JournalWindowImpl::notifyTopicClicked, this, _1); getPage (LeftBookPage)->adviseLinkClicked (callback); getPage (RightBookPage)->adviseLinkClicked (callback); getPage (LeftBookPage)->eventMouseWheel += MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel); getPage (RightBookPage)->eventMouseWheel += MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel); } { MWGui::BookPage::ClickCallback callback; callback = boost::bind (&JournalWindowImpl::notifyIndexLinkClicked, this, _1); getPage (LeftTopicIndex)->adviseLinkClicked (callback); getPage (RightTopicIndex)->adviseLinkClicked (callback); } adjustButton(PrevPageBTN); adjustButton(NextPageBTN); adjustButton(CloseBTN); adjustButton(CancelBTN); adjustButton(JournalBTN); Gui::ImageButton* optionsButton = getWidget(OptionsBTN); Gui::ImageButton* showActiveButton = getWidget(ShowActiveBTN); Gui::ImageButton* showAllButton = getWidget(ShowAllBTN); Gui::ImageButton* questsButton = getWidget(QuestsBTN); if (!questList) { // If tribunal is not installed (-> no options button), we still want the Topics button available, // so place it where the options button would have been Gui::ImageButton* topicsButton = getWidget(TopicsBTN); topicsButton->detachFromWidget(); topicsButton->attachToWidget(optionsButton->getParent()); topicsButton->setPosition(optionsButton->getPosition()); topicsButton->eventMouseButtonClick.clear(); topicsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &JournalWindowImpl::notifyOptions); optionsButton->setVisible(false); showActiveButton->setVisible(false); showAllButton->setVisible(false); questsButton->setVisible(false); } else { optionsButton->setImage("textures/tx_menubook_options.dds"); showActiveButton->setImage("textures/tx_menubook_quests_active.dds"); showAllButton->setImage("textures/tx_menubook_quests_all.dds"); questsButton->setImage("textures/tx_menubook_quests.dds"); adjustButton(ShowAllBTN); adjustButton(ShowActiveBTN); adjustButton(OptionsBTN); adjustButton(QuestsBTN); } Gui::ImageButton* nextButton = getWidget(NextPageBTN); if (nextButton->getSize().width == 64) { // english button has a 7 pixel wide strip of garbage on its right edge nextButton->setSize(64-7, nextButton->getSize().height); nextButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,nextButton->getSize().height)); } adjustButton(TopicsBTN); int width = getWidget(TopicsBTN)->getSize().width + getWidget(QuestsBTN)->getSize().width; int topicsWidth = getWidget(TopicsBTN)->getSize().width; int pageWidth = getWidget(RightBookPage)->getSize().width; getWidget(TopicsBTN)->setPosition((pageWidth - width)/2, getWidget(TopicsBTN)->getPosition().top); getWidget(QuestsBTN)->setPosition((pageWidth - width)/2 + topicsWidth, getWidget(QuestsBTN)->getPosition().top); mQuestMode = false; mAllQuests = false; mOptionsMode = false; } void adjustButton (char const * name) { Gui::ImageButton* button = getWidget(name); MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); button->setSize(button->getRequestedSize()); if (button->getAlign().isRight()) button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); } void open() { if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) { MWBase::Environment::get().getWindowManager()->popGuiMode (); } mModel->load (); setBookMode (); Book journalBook; if (mModel->isEmpty ()) journalBook = createEmptyJournalBook (); else journalBook = createJournalBook (); pushBook (journalBook, 0); // fast forward to the last page if (!mStates.empty ()) { unsigned int & page = mStates.top ().mPage; page = mStates.top().mBook->pageCount()-1; if (page%2) --page; } updateShowingPages(); } void close() { mModel->unload (); getPage (LeftBookPage)->showPage (Book (), 0); getPage (RightBookPage)->showPage (Book (), 0); while (!mStates.empty ()) mStates.pop (); mTopicIndexBook.reset (); } void setVisible (bool newValue) { WindowBase::setVisible (newValue); } void setBookMode () { mOptionsMode = false; setVisible (OptionsBTN, true); setVisible (OptionsOverlay, false); updateShowingPages (); updateCloseJournalButton (); } void setOptionsMode () { mOptionsMode = true; setVisible (OptionsBTN, false); setVisible (OptionsOverlay, true); setVisible (PrevPageBTN, false); setVisible (NextPageBTN, false); setVisible (CloseBTN, false); setVisible (JournalBTN, false); setVisible (TopicsList, false); setVisible (QuestsList, mQuestMode); setVisible (LeftTopicIndex, !mQuestMode); setVisible (RightTopicIndex, !mQuestMode); setVisible (ShowAllBTN, mQuestMode && !mAllQuests); setVisible (ShowActiveBTN, mQuestMode && mAllQuests); //TODO: figure out how to make "options" page overlay book page // correctly, so that text may show underneath getPage (RightBookPage)->showPage (Book (), 0); // If in quest mode, ensure the quest list is updated if (mQuestMode) notifyQuests(getWidget(QuestsList)); } void pushBook (Book book, unsigned int page) { DisplayState bs; bs.mPage = page; bs.mBook = book; mStates.push (bs); updateShowingPages (); updateCloseJournalButton (); } void replaceBook (Book book, unsigned int page) { assert (!mStates.empty ()); mStates.top ().mBook = book; mStates.top ().mPage = page; updateShowingPages (); } void popBook () { mStates.pop (); updateShowingPages (); updateCloseJournalButton (); } void updateCloseJournalButton () { setVisible (CloseBTN, mStates.size () < 2); setVisible (JournalBTN, mStates.size () >= 2); } void updateShowingPages () { Book book; unsigned int page; unsigned int relPages; if (!mStates.empty ()) { book = mStates.top ().mBook; page = mStates.top ().mPage; relPages = book->pageCount () - page; } else { page = 0; relPages = 0; } setVisible (PrevPageBTN, page > 0); setVisible (NextPageBTN, relPages > 2); setVisible (PageOneNum, relPages > 0); setVisible (PageTwoNum, relPages > 1); getPage (LeftBookPage)->showPage ((relPages > 0) ? book : Book (), page+0); getPage (RightBookPage)->showPage ((relPages > 0) ? book : Book (), page+1); setText (PageOneNum, page + 1); setText (PageTwoNum, page + 2); } void notifyTopicClicked (intptr_t linkId) { Book topicBook = createTopicBook (linkId); if (mStates.size () > 1) replaceBook (topicBook, 0); else pushBook (topicBook, 0); setVisible (OptionsOverlay, false); setVisible (OptionsBTN, true); setVisible (JournalBTN, true); mOptionsMode = false; } void notifyTopicSelected (const std::string& topic, int id) { const MWBase::Journal* journal = MWBase::Environment::get().getJournal(); intptr_t topicId = 0; /// \todo get rid of intptr ids for(MWBase::Journal::TTopicIter i = journal->topicBegin(); i != journal->topicEnd (); ++i) { if (Misc::StringUtils::ciEqual(i->first, topic)) topicId = intptr_t (&i->second); } notifyTopicClicked(topicId); } void notifyQuestClicked (const std::string& name, int id) { Book book = createQuestBook (name); if (mStates.size () > 1) replaceBook (book, 0); else pushBook (book, 0); setVisible (OptionsOverlay, false); setVisible (OptionsBTN, true); setVisible (JournalBTN, true); mOptionsMode = false; } void notifyOptions(MyGUI::Widget* _sender) { setOptionsMode (); if (!mTopicIndexBook) mTopicIndexBook = createTopicIndexBook (); getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1); } void notifyJournal(MyGUI::Widget* _sender) { assert (mStates.size () > 1); popBook (); } void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId character) { setVisible (LeftTopicIndex, false); setVisible (RightTopicIndex, false); setVisible (TopicsList, true); Gui::MWList* list = getWidget(TopicsList); list->clear(); AddNamesToList add(list); mModel->visitTopicNamesStartingWith((char) character, add); list->adjustSize(); } void notifyTopics(MyGUI::Widget* _sender) { mQuestMode = false; setVisible (LeftTopicIndex, true); setVisible (RightTopicIndex, true); setVisible (TopicsList, false); setVisible (QuestsList, false); setVisible (ShowAllBTN, false); setVisible (ShowActiveBTN, false); } struct AddNamesToList { AddNamesToList(Gui::MWList* list) : mList(list) {} Gui::MWList* mList; void operator () (const std::string& name, bool finished=false) { mList->addItem(name); } }; struct SetNamesInactive { SetNamesInactive(Gui::MWList* list) : mList(list) {} Gui::MWList* mList; void operator () (const std::string& name, bool finished) { if (finished) { mList->getItemWidget(name)->setStateSelected(true); } } }; void notifyQuests(MyGUI::Widget* _sender) { mQuestMode = true; setVisible (LeftTopicIndex, false); setVisible (RightTopicIndex, false); setVisible (TopicsList, false); setVisible (QuestsList, true); setVisible (ShowAllBTN, !mAllQuests); setVisible (ShowActiveBTN, mAllQuests); Gui::MWList* list = getWidget(QuestsList); list->clear(); AddNamesToList add(list); mModel->visitQuestNames(!mAllQuests, add); list->adjustSize(); if (mAllQuests) { SetNamesInactive setInactive(list); mModel->visitQuestNames(!mAllQuests, setInactive); } } void notifyShowAll(MyGUI::Widget* _sender) { mAllQuests = true; notifyQuests(_sender); } void notifyShowActive(MyGUI::Widget* _sender) { mAllQuests = false; notifyQuests(_sender); } void notifyCancel(MyGUI::Widget* _sender) { setBookMode (); } void notifyClose(MyGUI::Widget* _sender) { MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); MWBase::Environment::get().getWindowManager ()->popGuiMode (); } void notifyMouseWheel(MyGUI::Widget* sender, int rel) { if (rel < 0) notifyNextPage(sender); else notifyPrevPage(sender); } void notifyNextPage(MyGUI::Widget* _sender) { if (mOptionsMode) return; if (!mStates.empty ()) { unsigned int & page = mStates.top ().mPage; Book book = mStates.top ().mBook; if (page+2 < book->pageCount()) { page += 2; updateShowingPages (); } } } void notifyPrevPage(MyGUI::Widget* _sender) { if (mOptionsMode) return; if (!mStates.empty ()) { unsigned int & page = mStates.top ().mPage; if(page >= 2) { page -= 2; updateShowingPages (); } } } }; } // glue the implementation to the interface MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model, bool questList) { return new JournalWindowImpl (Model, questList); } openmw-openmw-0.38.0/apps/openmw/mwgui/journalwindow.hpp000066400000000000000000000011321264522266000234270ustar00rootroot00000000000000#ifndef MWGUI_JOURNAL_H #define MWGUI_JOURNAL_H #include namespace MWBase { class WindowManager; } namespace MWGui { struct JournalViewModel; struct JournalWindow { /// construct a new instance of the one JournalWindow implementation static JournalWindow * create (boost::shared_ptr Model, bool questList); /// destroy this instance of the JournalWindow implementation virtual ~JournalWindow () {}; /// show/hide the journal window virtual void setVisible (bool newValue) = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/layout.cpp000066400000000000000000000043541264522266000220460ustar00rootroot00000000000000#include "layout.hpp" #include #include #include #include #include namespace MWGui { void Layout::initialise(const std::string& _layout, MyGUI::Widget* _parent) { const std::string MAIN_WINDOW = "_Main"; mLayoutName = _layout; if (mLayoutName.empty()) mMainWidget = _parent; else { mPrefix = MyGUI::utility::toString(this, "_"); mListWindowRoot = MyGUI::LayoutManager::getInstance().loadLayout(mLayoutName, mPrefix, _parent); const std::string main_name = mPrefix + MAIN_WINDOW; for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); iter!=mListWindowRoot.end(); ++iter) { if ((*iter)->getName() == main_name) { mMainWidget = (*iter); break; } } MYGUI_ASSERT(mMainWidget, "root widget name '" << MAIN_WINDOW << "' in layout '" << mLayoutName << "' not found."); } } void Layout::shutdown() { MyGUI::Gui::getInstance().destroyWidget(mMainWidget); mListWindowRoot.clear(); } void Layout::setCoord(int x, int y, int w, int h) { mMainWidget->setCoord(x,y,w,h); } void Layout::setVisible(bool b) { mMainWidget->setVisible(b); } void Layout::setText(const std::string &name, const std::string &caption) { MyGUI::Widget* pt; getWidget(pt, name); static_cast(pt)->setCaption(caption); } void Layout::setTitle(const std::string& title) { static_cast(mMainWidget)->setCaptionWithReplacing(title); } MyGUI::Widget* Layout::getWidget(const std::string &_name) { for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); iter!=mListWindowRoot.end(); ++iter) { MyGUI::Widget* find = (*iter)->findWidget(mPrefix + _name); if (nullptr != find) { return find; } } MYGUI_EXCEPT("widget name '" << _name << "' in layout '" << mLayoutName << "' not found."); } } openmw-openmw-0.38.0/apps/openmw/mwgui/layout.hpp000066400000000000000000000031601264522266000220450ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_LAYOUT_H #define OPENMW_MWGUI_LAYOUT_H #include #include #include namespace MWGui { /** The Layout class is an utility class used to load MyGUI layouts from xml files, and to manipulate member widgets. */ class Layout { public: Layout(const std::string & _layout, MyGUI::Widget* _parent = nullptr) : mMainWidget(nullptr) { initialise(_layout, _parent); } virtual ~Layout() { shutdown(); } MyGUI::Widget* getWidget(const std::string& _name); template void getWidget(T * & _widget, const std::string & _name) { MyGUI::Widget* w = getWidget(_name); T* cast = w->castType(false); if (!cast) { MYGUI_EXCEPT("Error cast : dest type = '" << T::getClassTypeName() << "' source name = '" << w->getName() << "' source type = '" << w->getTypeName() << "' in layout '" << mLayoutName << "'"); } else _widget = cast; } private: void initialise(const std::string & _layout, MyGUI::Widget* _parent = nullptr); void shutdown(); public: void setCoord(int x, int y, int w, int h); virtual void setVisible(bool b); void setText(const std::string& name, const std::string& caption); // NOTE: this assume that mMainWidget is of type Window. void setTitle(const std::string& title); MyGUI::Widget* mMainWidget; protected: std::string mPrefix; std::string mLayoutName; MyGUI::VectorWidgetPtr mListWindowRoot; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/levelupdialog.cpp000066400000000000000000000212321264522266000233570ustar00rootroot00000000000000#include "levelupdialog.hpp" #include #include #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWGui { const unsigned int LevelupDialog::sMaxCoins = 3; LevelupDialog::LevelupDialog() : WindowBase("openmw_levelup_dialog.layout"), mCoinCount(sMaxCoins) { getWidget(mOkButton, "OkButton"); getWidget(mClassImage, "ClassImage"); getWidget(mLevelText, "LevelText"); getWidget(mLevelDescription, "LevelDescription"); getWidget(mCoinBox, "Coins"); getWidget(mAssignWidget, "AssignWidget"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onOkButtonClicked); for (int i=1; i<9; ++i) { MyGUI::TextBox* t; getWidget(t, "AttribVal" + MyGUI::utility::toString(i)); MyGUI::Button* b; getWidget(b, "Attrib" + MyGUI::utility::toString(i)); b->setUserData (i-1); b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked); mAttributes.push_back(b); mAttributeValues.push_back(t); getWidget(t, "AttribMultiplier" + MyGUI::utility::toString(i)); mAttributeMultipliers.push_back(t); } for (unsigned int i = 0; i < mCoinCount; ++i) { MyGUI::ImageBox* image = mCoinBox->createWidget("ImageBox", MyGUI::IntCoord(0,0,16,16), MyGUI::Align::Default); image->setImageTexture ("icons\\tx_goldicon.dds"); mCoins.push_back(image); } center(); } void LevelupDialog::setAttributeValues() { MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); for (int i = 0; i < 8; ++i) { int val = creatureStats.getAttribute(i).getBase(); if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), i) != mSpentAttributes.end()) { val += pcStats.getLevelupAttributeMultiplier(i); } if (val >= 100) val = 100; mAttributeValues[i]->setCaption(MyGUI::utility::toString(val)); } } void LevelupDialog::resetCoins() { const int coinSpacing = 10; int curX = mCoinBox->getWidth()/2 - (coinSpacing*(mCoinCount - 1) + 16*mCoinCount)/2; for (unsigned int i=0; idetachFromWidget(); image->attachToWidget(mCoinBox); if (i < mCoinCount) { mCoins[i]->setVisible(true); image->setCoord(MyGUI::IntCoord(curX,0,16,16)); curX += 16+coinSpacing; } else mCoins[i]->setVisible(false); } } void LevelupDialog::assignCoins() { resetCoins(); for (unsigned int i=0; idetachFromWidget(); image->attachToWidget(mAssignWidget); int attribute = mSpentAttributes[i]; int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 20; MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mAssignWidget->getAbsolutePosition() - MyGUI::IntPoint(22+xdiff,0); pos.top += (mAttributes[attribute]->getHeight() - image->getHeight())/2; image->setPosition(pos); } setAttributeValues(); } void LevelupDialog::open() { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player); const ESM::NPC *playerData = player.get()->mBase; // set class image const ESM::Class *cls = world->getStore().get().find(playerData->mClass); if(world->getStore().get().isDynamic(cls->mId)) { // Choosing Stealth specialization and Speed/Agility as attributes, if possible. Otherwise fall back to first class found. MWWorld::SharedIterator it = world->getStore().get().begin(); for(; it != world->getStore().get().end(); ++it) { if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3) break; } if (it == world->getStore().get().end()) it = world->getStore().get().begin(); if (it != world->getStore().get().end()) cls = &*it; } mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); int level = creatureStats.getLevel ()+1; mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + MyGUI::utility::toString(level)); std::string levelupdescription; if(level > 20) levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default"); else levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+MyGUI::utility::toString(level)); mLevelDescription->setCaption (levelupdescription); unsigned int availableAttributes = 0; for (int i = 0; i < 8; ++i) { MyGUI::TextBox* text = mAttributeMultipliers[i]; if (pcStats.getAttribute(i).getBase() < 100) { mAttributes[i]->setEnabled(true); mAttributeValues[i]->setEnabled(true); availableAttributes++; int mult = pcStats.getLevelupAttributeMultiplier (i); mult = std::min(mult, 100-pcStats.getAttribute(i).getBase()); text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult)); } else { mAttributes[i]->setEnabled(false); mAttributeValues[i]->setEnabled(false); text->setCaption(""); } } mCoinCount = std::min(sMaxCoins, availableAttributes); mSpentAttributes.clear(); resetCoins(); setAttributeValues(); center(); // Play LevelUp Music MWBase::Environment::get().getSoundManager()->streamMusic("Special/MW_Triumph.mp3"); } void LevelupDialog::onOkButtonClicked(MyGUI::Widget* sender) { MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); if (mSpentAttributes.size() < mCoinCount) MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage36}"); else { // increase attributes for (unsigned int i = 0; i < mCoinCount; ++i) { MWMechanics::AttributeValue attribute = pcStats.getAttribute(mSpentAttributes[i]); attribute.setBase(attribute.getBase() + pcStats.getLevelupAttributeMultiplier(mSpentAttributes[i])); if (attribute.getBase() >= 100) attribute.setBase(100); pcStats.setAttribute(mSpentAttributes[i], attribute); } pcStats.levelUp(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Levelup); } } void LevelupDialog::onAttributeClicked(MyGUI::Widget *sender) { int attribute = *sender->getUserData(); std::vector::iterator found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute); if (found != mSpentAttributes.end()) mSpentAttributes.erase(found); else { if (mSpentAttributes.size() == mCoinCount) mSpentAttributes[mCoinCount - 1] = attribute; else mSpentAttributes.push_back(attribute); } assignCoins(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/levelupdialog.hpp000066400000000000000000000017711264522266000233720ustar00rootroot00000000000000#ifndef MWGUI_LEVELUPDIALOG_H #define MWGUI_LEVELUPDIALOG_H #include "windowbase.hpp" namespace MWGui { class LevelupDialog : public WindowBase { public: LevelupDialog(); virtual void open(); private: MyGUI::Button* mOkButton; MyGUI::ImageBox* mClassImage; MyGUI::TextBox* mLevelText; MyGUI::EditBox* mLevelDescription; MyGUI::Widget* mCoinBox; MyGUI::Widget* mAssignWidget; std::vector mAttributes; std::vector mAttributeValues; std::vector mAttributeMultipliers; std::vector mCoins; std::vector mSpentAttributes; unsigned int mCoinCount; static const unsigned int sMaxCoins; void onOkButtonClicked(MyGUI::Widget* sender); void onAttributeClicked(MyGUI::Widget* sender); void assignCoins(); void resetCoins(); void setAttributeValues(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/loadingscreen.cpp000066400000000000000000000256371264522266000233550ustar00rootroot00000000000000#include "loadingscreen.hpp" #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwrender/vismask.hpp" #include "backgroundimage.hpp" namespace MWGui { LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer) : WindowBase("openmw_loading_screen.layout") , mVFS(vfs) , mViewer(viewer) , mTargetFrameRate(120.0) , mLastWallpaperChangeTime(0.0) , mLastRenderTime(0.0) , mLoadingOnTime(0.0) , mImportantLabel(false) , mProgress(0) { mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); getWidget(mLoadingBox, "LoadingBox"); mProgressBar->setScrollViewPage(1); mBackgroundImage = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Stretch, "Menu"); setVisible(false); findSplashScreens(); } LoadingScreen::~LoadingScreen() { } void LoadingScreen::findSplashScreens() { const std::map& index = mVFS->getIndex(); std::string pattern = "Splash/"; mVFS->normalizeFilename(pattern); std::map::const_iterator found = index.lower_bound(pattern); while (found != index.end()) { const std::string& name = found->first; if (name.size() >= pattern.size() && name.substr(0, pattern.size()) == pattern) { size_t pos = name.find_last_of('.'); if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".tga") == 0) mSplashScreens.push_back(found->first); } else break; ++found; } if (mSplashScreens.empty()) std::cerr << "No splash screens found!" << std::endl; } void LoadingScreen::setLabel(const std::string &label, bool important) { mImportantLabel = important; mLoadingText->setCaptionWithReplacing(label); int padding = mLoadingBox->getWidth() - mLoadingText->getWidth(); MyGUI::IntSize size(mLoadingText->getTextSize().width+padding, mLoadingBox->getHeight()); size.width = std::max(300, size.width); mLoadingBox->setSize(size); mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mLoadingBox->getTop()); } void LoadingScreen::setVisible(bool visible) { WindowBase::setVisible(visible); mBackgroundImage->setVisible(visible); } class CopyFramebufferToTextureCallback : public osg::Camera::DrawCallback { public: CopyFramebufferToTextureCallback(osg::Texture2D* texture, int w, int h) : mTexture(texture), mWidth(w), mHeight(h) { } virtual void operator () (osg::RenderInfo& renderInfo) const { mTexture->copyTexImage2D(*renderInfo.getState(), 0, 0, mWidth, mHeight); // Callback removes itself when done if (renderInfo.getCurrentCamera()) renderInfo.getCurrentCamera()->setInitialDrawCallback(NULL); } private: osg::ref_ptr mTexture; int mWidth, mHeight; }; void LoadingScreen::loadingOn() { mLoadingOnTime = mTimer.time_m(); // Early-out if already on if (mMainWidget->getVisible()) return; if (mViewer->getIncrementalCompileOperation()) { mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(mTargetFrameRate); } bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); if (!showWallpaper) { // Copy the current framebuffer onto a texture and display that texture as the background image // Note, we could also set the camera to disable clearing and have the background image transparent, // but then we get shaking effects on buffer swaps. if (!mTexture) { mTexture = new osg::Texture2D; mTexture->setInternalFormat(GL_RGB); mTexture->setResizeNonPowerOfTwoHint(false); } int width = mViewer->getCamera()->getViewport()->width(); int height = mViewer->getCamera()->getViewport()->height(); mViewer->getCamera()->setInitialDrawCallback(new CopyFramebufferToTextureCallback(mTexture, width, height)); if (!mGuiTexture.get()) { mGuiTexture.reset(new osgMyGUI::OSGTexture(mTexture)); } mBackgroundImage->setBackgroundImage(""); mBackgroundImage->setRenderItemTexture(mGuiTexture.get()); mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); } setVisible(true); if (showWallpaper) { changeWallpaper(); } MWBase::Environment::get().getWindowManager()->pushGuiMode(showWallpaper ? GM_LoadingWallpaper : GM_Loading); } void LoadingScreen::loadingOff() { if (mLastRenderTime < mLoadingOnTime) { // the loading was so fast that we didn't show loading screen at all // we may still want to show the label if the caller requested it if (mImportantLabel) { MWBase::Environment::get().getWindowManager()->messageBox(mLoadingText->getCaption()); mImportantLabel = false; } } else mImportantLabel = false; // label was already shown on loading screen //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; setVisible(false); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); } void LoadingScreen::changeWallpaper () { if (!mSplashScreens.empty()) { std::string const & randomSplash = mSplashScreens.at(Misc::Rng::rollDice(mSplashScreens.size())); // TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3 // we can't do this by default, because the Morrowind splash screens are 1024x1024, but should be displayed as 4:3 bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); mBackgroundImage->setBackgroundImage(randomSplash, true, stretch); } } void LoadingScreen::setProgressRange (size_t range) { mProgressBar->setScrollRange(range+1); mProgressBar->setScrollPosition(0); mProgressBar->setTrackSize(0); mProgress = 0; } void LoadingScreen::setProgress (size_t value) { // skip expensive update if there isn't enough visible progress if (value - mProgress < mProgressBar->getScrollRange()/200.f) return; value = std::min(value, mProgressBar->getScrollRange()-1); mProgress = value; mProgressBar->setScrollPosition(0); mProgressBar->setTrackSize(static_cast(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize())); draw(); } void LoadingScreen::increaseProgress (size_t increase) { mProgressBar->setScrollPosition(0); size_t value = mProgress + increase; value = std::min(value, mProgressBar->getScrollRange()-1); mProgress = value; mProgressBar->setTrackSize(static_cast(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize())); draw(); } bool LoadingScreen::needToDrawLoadingScreen() { if ( mTimer.time_m() <= mLastRenderTime + (1.0/mTargetFrameRate) * 1000.0) return false; // the minimal delay before a loading screen shows const float initialDelay = 0.05; bool alreadyShown = (mLastRenderTime > mLoadingOnTime); float diff = (mTimer.time_m() - mLoadingOnTime); if (!alreadyShown) { // bump the delay by the current progress - i.e. if during the initial delay the loading // has almost finished, no point showing the loading screen now diff -= mProgress / static_cast(mProgressBar->getScrollRange()) * 100.f; } bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); if (!showWallpaper && diff < initialDelay*1000) return false; return true; } void LoadingScreen::draw() { if (!needToDrawLoadingScreen()) return; bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); if (showWallpaper && mTimer.time_m() > mLastWallpaperChangeTime + 5000*1) { mLastWallpaperChangeTime = mTimer.time_m(); changeWallpaper(); } // Turn off rendering except the GUI int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); int oldCullMask = mViewer->getCamera()->getCullMask(); mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI); mViewer->getCamera()->setCullMask(MWRender::Mask_GUI); MWBase::Environment::get().getInputManager()->update(0, true, true); //osg::Timer timer; // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); //std::cout << "frame took " << timer.time_m() << std::endl; //if (mViewer->getIncrementalCompileOperation()) //std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl; // resume 3d rendering mViewer->getUpdateVisitor()->setTraversalMask(oldUpdateMask); mViewer->getCamera()->setCullMask(oldCullMask); mLastRenderTime = mTimer.time_m(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/loadingscreen.hpp000066400000000000000000000034701264522266000233510ustar00rootroot00000000000000#ifndef MWGUI_LOADINGSCREEN_H #define MWGUI_LOADINGSCREEN_H #include #include #include "windowbase.hpp" #include namespace osgViewer { class Viewer; } namespace osg { class Texture2D; } namespace VFS { class Manager; } namespace MWGui { class BackgroundImage; class LoadingScreen : public WindowBase, public Loading::Listener { public: LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer); virtual ~LoadingScreen(); /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details virtual void setLabel (const std::string& label, bool important); virtual void loadingOn(); virtual void loadingOff(); virtual void setProgressRange (size_t range); virtual void setProgress (size_t value); virtual void increaseProgress (size_t increase=1); virtual void setVisible(bool visible); private: void findSplashScreens(); bool needToDrawLoadingScreen(); const VFS::Manager* mVFS; osg::ref_ptr mViewer; double mTargetFrameRate; double mLastWallpaperChangeTime; double mLastRenderTime; osg::Timer mTimer; double mLoadingOnTime; bool mImportantLabel; size_t mProgress; MyGUI::Widget* mLoadingBox; MyGUI::TextBox* mLoadingText; MyGUI::ScrollBar* mProgressBar; BackgroundImage* mBackgroundImage; std::vector mSplashScreens; // TODO: add releaseGLObjects() for mTexture osg::ref_ptr mTexture; std::auto_ptr mGuiTexture; void changeWallpaper(); void draw(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/mainmenu.cpp000066400000000000000000000235571264522266000223500ustar00rootroot00000000000000#include "mainmenu.hpp" #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/statemanager.hpp" #include "savegamedialog.hpp" #include "confirmationdialog.hpp" #include "backgroundimage.hpp" #include "videowidget.hpp" namespace MWGui { MainMenu::MainMenu(int w, int h, const VFS::Manager* vfs, const std::string& versionDescription) : Layout("openmw_mainmenu.layout") , mWidth (w), mHeight (h) , mVFS(vfs), mButtonBox(0) , mBackground(NULL) , mVideoBackground(NULL) , mVideo(NULL) , mSaveGameDialog(NULL) { getWidget(mVersionText, "VersionText"); mVersionText->setCaption(versionDescription); mHasAnimatedMenu = mVFS->exists("video/menu_background.bik"); updateMenu(); } MainMenu::~MainMenu() { delete mSaveGameDialog; } void MainMenu::onResChange(int w, int h) { mWidth = w; mHeight = h; updateMenu(); } void MainMenu::setVisible (bool visible) { if (visible) updateMenu(); showBackground( MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); Layout::setVisible (visible); } void MainMenu::onNewGameConfirmed() { MWBase::Environment::get().getStateManager()->newGame(); } void MainMenu::onExitConfirmed() { MWBase::Environment::get().getStateManager()->requestQuit(); } void MainMenu::onButtonClicked(MyGUI::Widget *sender) { std::string name = *sender->getUserData(); MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); if (name == "return") { MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); } else if (name == "options") MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); else if (name == "credits") MWBase::Environment::get().getWindowManager()->playVideo("mw_credits.bik", true); else if (name == "exitgame") { if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame) onExitConfirmed(); else { ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->askForConfirmation("#{sMessage2}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &MainMenu::onExitConfirmed); dialog->eventCancelClicked.clear(); } } else if (name == "newgame") { if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame) onNewGameConfirmed(); else { ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->askForConfirmation("#{sNotifyMessage54}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &MainMenu::onNewGameConfirmed); dialog->eventCancelClicked.clear(); } } else { if (!mSaveGameDialog) mSaveGameDialog = new SaveGameDialog(); if (name == "loadgame") mSaveGameDialog->setLoadOrSave(true); else if (name == "savegame") mSaveGameDialog->setLoadOrSave(false); mSaveGameDialog->setVisible(true); } } void MainMenu::showBackground(bool show) { if (mVideo && !show) { MyGUI::Gui::getInstance().destroyWidget(mVideoBackground); mVideoBackground = NULL; mVideo = NULL; } if (mBackground && !show) { MyGUI::Gui::getInstance().destroyWidget(mBackground); mBackground = NULL; } if (!show) return; bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); if (mHasAnimatedMenu) { if (!mVideo) { // Use black background to correct aspect ratio mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Default, "Menu"); mVideoBackground->setImageTexture("black"); mVideo = mVideoBackground->createWidget("ImageBox", 0,0,1,1, MyGUI::Align::Stretch, "Menu"); mVideo->setVFS(mVFS); mVideo->playVideo("video\\menu_background.bik"); } MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); int screenWidth = viewSize.width; int screenHeight = viewSize.height; mVideoBackground->setSize(screenWidth, screenHeight); mVideo->autoResize(stretch); mVideo->setVisible(true); } else { if (!mBackground) { mBackground = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Stretch, "Menu"); mBackground->setBackgroundImage("textures\\menu_morrowind.dds", true, stretch); } mBackground->setVisible(true); } } void MainMenu::update(float dt) { if (mVideo) { if (!mVideo->update()) { // If finished playing, start again mVideo->playVideo("video\\menu_background.bik"); } } } void MainMenu::updateMenu() { setCoord(0,0, mWidth, mHeight); if (!mButtonBox) mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); int curH = 0; MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState(); mVersionText->setVisible(state == MWBase::StateManager::State_NoGame); std::vector buttons; if (state==MWBase::StateManager::State_Running) buttons.push_back("return"); buttons.push_back("newgame"); if (state==MWBase::StateManager::State_Running && MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1 && MWBase::Environment::get().getWindowManager()->isSavingAllowed()) buttons.push_back("savegame"); if (MWBase::Environment::get().getStateManager()->characterBegin()!= MWBase::Environment::get().getStateManager()->characterEnd()) buttons.push_back("loadgame"); buttons.push_back("options"); if (state==MWBase::StateManager::State_NoGame) buttons.push_back("credits"); buttons.push_back("exitgame"); // Create new buttons if needed for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) { if (mButtons.find(*it) == mButtons.end()) { Gui::ImageButton* button = mButtonBox->createWidget ("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default); button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds"); button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds"); button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds"); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked); button->setUserData(std::string(*it)); mButtons[*it] = button; } } // Start by hiding all buttons int maxwidth = 0; for (std::map::iterator it = mButtons.begin(); it != mButtons.end(); ++it) { it->second->setVisible(false); MyGUI::IntSize requested = it->second->getRequestedSize(); if (requested.width > maxwidth) maxwidth = requested.width; } // Now show and position the ones we want for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) { assert(mButtons.find(*it) != mButtons.end()); Gui::ImageButton* button = mButtons[*it]; button->setVisible(true); MyGUI::IntSize requested = button->getRequestedSize(); // Trim off some of the excessive padding // TODO: perhaps do this within ImageButton? int trim = 8; button->setImageCoord(MyGUI::IntCoord(0, trim, requested.width, requested.height-trim)); int height = requested.height-trim*2; button->setImageTile(MyGUI::IntSize(requested.width, height)); button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, height); curH += height; } if (state == MWBase::StateManager::State_NoGame) { // Align with the background image int bottomPadding=24; mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight - curH - bottomPadding, maxwidth, curH); } else mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH); } } openmw-openmw-0.38.0/apps/openmw/mwgui/mainmenu.hpp000066400000000000000000000024311264522266000223410ustar00rootroot00000000000000#ifndef OPENMW_GAME_MWGUI_MAINMENU_H #define OPENMW_GAME_MWGUI_MAINMENU_H #include "layout.hpp" namespace Gui { class ImageButton; } namespace VFS { class Manager; } namespace MWGui { class BackgroundImage; class SaveGameDialog; class VideoWidget; class MainMenu : public Layout { int mWidth; int mHeight; bool mHasAnimatedMenu; public: MainMenu(int w, int h, const VFS::Manager* vfs, const std::string& versionDescription); ~MainMenu(); void onResChange(int w, int h); virtual void setVisible (bool visible); void update(float dt); private: const VFS::Manager* mVFS; MyGUI::Widget* mButtonBox; MyGUI::TextBox* mVersionText; BackgroundImage* mBackground; MyGUI::ImageBox* mVideoBackground; VideoWidget* mVideo; // For animated main menus std::map mButtons; void onButtonClicked (MyGUI::Widget* sender); void onNewGameConfirmed(); void onExitConfirmed(); void showBackground(bool show); void updateMenu(); SaveGameDialog* mSaveGameDialog; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/mapwindow.cpp000066400000000000000000001150521264522266000225340ustar00rootroot00000000000000#include "mapwindow.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/player.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwrender/globalmap.hpp" #include "../mwrender/localmap.hpp" #include "confirmationdialog.hpp" #include "tooltips.hpp" namespace { const int cellSize = 8192; enum LocalMapWidgetDepth { Local_MarkerAboveFogLayer = 0, Local_CompassLayer = 1, Local_FogLayer = 2, Local_MarkerLayer = 3, Local_MapLayer = 4 }; enum GlobalMapWidgetDepth { Global_CompassLayer = 0, Global_MarkerLayer = 1, Global_ExploreOverlayLayer = 2, Global_MapLayer = 3 }; /// @brief A widget that changes its color when hovered. class MarkerWidget: public MyGUI::Widget { MYGUI_RTTI_DERIVED(MarkerWidget) public: void setNormalColour(const MyGUI::Colour& colour) { mNormalColour = colour; setColour(colour); } void setHoverColour(const MyGUI::Colour& colour) { mHoverColour = colour; } private: MyGUI::Colour mNormalColour; MyGUI::Colour mHoverColour; void onMouseLostFocus(MyGUI::Widget* _new) { setColour(mNormalColour); } void onMouseSetFocus(MyGUI::Widget* _old) { setColour(mHoverColour); } }; } namespace MWGui { void CustomMarkerCollection::addMarker(const ESM::CustomMarker &marker, bool triggerEvent) { mMarkers.insert(std::make_pair(marker.mCell, marker)); if (triggerEvent) eventMarkersChanged(); } void CustomMarkerCollection::deleteMarker(const ESM::CustomMarker &marker) { std::pair range = mMarkers.equal_range(marker.mCell); for (ContainerType::iterator it = range.first; it != range.second; ++it) { if (it->second == marker) { mMarkers.erase(it); eventMarkersChanged(); return; } } throw std::runtime_error("can't find marker to delete"); } void CustomMarkerCollection::updateMarker(const ESM::CustomMarker &marker, const std::string &newNote) { std::pair range = mMarkers.equal_range(marker.mCell); for (ContainerType::iterator it = range.first; it != range.second; ++it) { if (it->second == marker) { it->second.mNote = newNote; eventMarkersChanged(); return; } } throw std::runtime_error("can't find marker to update"); } void CustomMarkerCollection::clear() { mMarkers.clear(); eventMarkersChanged(); } CustomMarkerCollection::ContainerType::const_iterator CustomMarkerCollection::begin() const { return mMarkers.begin(); } CustomMarkerCollection::ContainerType::const_iterator CustomMarkerCollection::end() const { return mMarkers.end(); } CustomMarkerCollection::RangeType CustomMarkerCollection::getMarkers(const ESM::CellId &cellId) const { return mMarkers.equal_range(cellId); } size_t CustomMarkerCollection::size() const { return mMarkers.size(); } // ------------------------------------------------------ LocalMapBase::LocalMapBase(CustomMarkerCollection &markers, MWRender::LocalMap* localMapRender) : mLocalMapRender(localMapRender) , mCurX(0) , mCurY(0) , mInterior(false) , mLocalMap(NULL) , mCompass(NULL) , mPrefix() , mChanged(true) , mFogOfWar(true) , mMapWidgetSize(0) , mCustomMarkers(markers) , mMarkerUpdateTimer(0.0f) , mLastDirectionX(0.0f) , mLastDirectionY(0.0f) { mCustomMarkers.eventMarkersChanged += MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); } LocalMapBase::~LocalMapBase() { mCustomMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers); } void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize) { mLocalMap = widget; mCompass = compass; mMapWidgetSize = mapWidgetSize; mLocalMap->setCanvasSize(mMapWidgetSize*3, mMapWidgetSize*3); mCompass->setDepth(Local_CompassLayer); mCompass->setNeedMouseFocus(false); for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) { MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left); map->setDepth(Local_MapLayer); MyGUI::ImageBox* fog = mLocalMap->createWidget("ImageBox", MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize), MyGUI::Align::Top | MyGUI::Align::Left); fog->setDepth(Local_FogLayer); map->setNeedMouseFocus(false); fog->setNeedMouseFocus(false); mMapWidgets.push_back(map); mFogWidgets.push_back(fog); } } } void LocalMapBase::setCellPrefix(const std::string& prefix) { mPrefix = prefix; mChanged = true; } bool LocalMapBase::toggleFogOfWar() { mFogOfWar = !mFogOfWar; applyFogOfWar(); return mFogOfWar; } void LocalMapBase::applyFogOfWar() { TextureVector fogTextures; for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) { int x = mCurX + (mx-1); int y = mCurY + (-1*(my-1)); MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx]; if (!mFogOfWar) { fog->setImageTexture(""); continue; } osg::ref_ptr tex = mLocalMapRender->getFogOfWarTexture(x, y); if (tex) { boost::shared_ptr myguitex (new osgMyGUI::OSGTexture(tex)); fog->setRenderItemTexture(myguitex.get()); fog->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); fogTextures.push_back(myguitex); } else fog->setImageTexture("black"); } } // Move the textures we just set into mFogTextures, and move the previous textures into fogTextures, for deletion when this function ends. // Note, above we need to ensure that all widgets are getting a new texture set, lest we delete textures that are still in use. mFogTextures.swap(fogTextures); redraw(); } MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerUserData& markerPos) { MyGUI::IntPoint widgetPos; // normalized cell coordinates float nX,nY; markerPos.interior = mInterior; if (!mInterior) { int cellX, cellY; MWBase::Environment::get().getWorld()->positionToIndex(worldX, worldY, cellX, cellY); nX = (worldX - cellSize * cellX) / cellSize; // Image space is -Y up, cells are Y up nY = 1 - (worldY - cellSize * cellY) / cellSize; float cellDx = static_cast(cellX - mCurX); float cellDy = static_cast(cellY - mCurY); markerPos.cellX = cellX; markerPos.cellY = cellY; widgetPos = MyGUI::IntPoint(static_cast(nX * mMapWidgetSize + (1 + cellDx) * mMapWidgetSize), static_cast(nY * mMapWidgetSize - (cellDy-1) * mMapWidgetSize)); } else { int cellX, cellY; osg::Vec2f worldPos (worldX, worldY); mLocalMapRender->worldToInteriorMapPosition(worldPos, nX, nY, cellX, cellY); markerPos.cellX = cellX; markerPos.cellY = cellY; // Image space is -Y up, cells are Y up widgetPos = MyGUI::IntPoint(static_cast(nX * mMapWidgetSize + (1 + (cellX - mCurX)) * mMapWidgetSize), static_cast(nY * mMapWidgetSize + (1-(cellY-mCurY)) * mMapWidgetSize)); } markerPos.nX = nX; markerPos.nY = nY; return widgetPos; } void LocalMapBase::updateCustomMarkers() { for (std::vector::iterator it = mCustomMarkerWidgets.begin(); it != mCustomMarkerWidgets.end(); ++it) MyGUI::Gui::getInstance().destroyWidget(*it); mCustomMarkerWidgets.clear(); for (int dX = -1; dX <= 1; ++dX) { for (int dY =-1; dY <= 1; ++dY) { ESM::CellId cellId; cellId.mPaged = !mInterior; cellId.mWorldspace = (mInterior ? mPrefix : "sys::default"); cellId.mIndex.mX = mCurX+dX; cellId.mIndex.mY = mCurY+dY; CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellId); for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second; ++it) { const ESM::CustomMarker& marker = it->second; MarkerUserData markerPos (mLocalMapRender); MyGUI::IntPoint widgetPos = getMarkerPosition(marker.mWorldX, marker.mWorldY, markerPos); MyGUI::IntCoord widgetCoord(widgetPos.left - 8, widgetPos.top - 8, 16, 16); MarkerWidget* markerWidget = mLocalMap->createWidget("CustomMarkerButton", widgetCoord, MyGUI::Align::Default); markerWidget->setDepth(Local_MarkerAboveFogLayer); markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); markerWidget->setUserString("Caption_TextOneLine", MyGUI::TextIterator::toTagsString(marker.mNote)); markerWidget->setNormalColour(MyGUI::Colour(0.6f, 0.6f, 0.6f)); markerWidget->setHoverColour(MyGUI::Colour(1.0f, 1.0f, 1.0f)); markerWidget->setUserData(marker); markerWidget->setNeedMouseFocus(true); customMarkerCreated(markerWidget); mCustomMarkerWidgets.push_back(markerWidget); } } } redraw(); } void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell mCurX = x; mCurY = y; mInterior = interior; mChanged = false; applyFogOfWar(); // clear all previous door markers for (std::vector::iterator it = mDoorMarkerWidgets.begin(); it != mDoorMarkerWidgets.end(); ++it) MyGUI::Gui::getInstance().destroyWidget(*it); mDoorMarkerWidgets.clear(); // Update the map textures TextureVector textures; for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) { int mapX = x + (mx-1); int mapY = y + (-1*(my-1)); MyGUI::ImageBox* box = mMapWidgets[my + 3*mx]; osg::ref_ptr texture = mLocalMapRender->getMapTexture(mapX, mapY); if (texture) { boost::shared_ptr guiTex (new osgMyGUI::OSGTexture(texture)); textures.push_back(guiTex); box->setRenderItemTexture(guiTex.get()); box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); } else box->setRenderItemTexture(NULL); } } mMapTextures.swap(textures); MWBase::World* world = MWBase::Environment::get().getWorld(); // Retrieve the door markers we want to show std::vector doors; if (interior) { MWWorld::CellStore* cell = world->getInterior (mPrefix); world->getDoorMarkers(cell, doors); } else { for (int dX=-1; dX<2; ++dX) { for (int dY=-1; dY<2; ++dY) { MWWorld::CellStore* cell = world->getExterior (mCurX+dX, mCurY+dY); world->getDoorMarkers(cell, doors); } } } // Create a widget for each marker int counter = 0; for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) { MWBase::World::DoorMarker marker = *it; std::vector destNotes; CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(marker.dest); for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second; ++it) destNotes.push_back(it->second.mNote); MarkerUserData data (mLocalMapRender); data.notes = destNotes; data.caption = marker.name; MyGUI::IntPoint widgetPos = getMarkerPosition(marker.x, marker.y, data); MyGUI::IntCoord widgetCoord(widgetPos.left - 4, widgetPos.top - 4, 8, 8); ++counter; MarkerWidget* markerWidget = mLocalMap->createWidget("MarkerButton", widgetCoord, MyGUI::Align::Default); markerWidget->setNormalColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=normal}"))); markerWidget->setHoverColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=normal_over}"))); markerWidget->setDepth(Local_MarkerLayer); markerWidget->setNeedMouseFocus(true); // Used by tooltips to not show the tooltip if marker is hidden by fog of war markerWidget->setUserString("ToolTipType", "MapMarker"); markerWidget->setUserData(data); doorMarkerCreated(markerWidget); mDoorMarkerWidgets.push_back(markerWidget); } updateMagicMarkers(); updateCustomMarkers(); } void LocalMapBase::redraw() { // Redraw children in proper order mLocalMap->getParent()->_updateChilds(); } void LocalMapBase::setPlayerPos(int cellX, int cellY, const float nx, const float ny) { MyGUI::IntPoint pos(static_cast(mMapWidgetSize + nx*mMapWidgetSize - 16), static_cast(mMapWidgetSize + ny*mMapWidgetSize - 16)); pos.left += (cellX - mCurX) * mMapWidgetSize; pos.top -= (cellY - mCurY) * mMapWidgetSize; if (pos != mCompass->getPosition()) { notifyPlayerUpdate (); mCompass->setPosition(pos); MyGUI::IntPoint middle (pos.left+16, pos.top+16); MyGUI::IntCoord viewsize = mLocalMap->getCoord(); MyGUI::IntPoint viewOffset((viewsize.width / 2) - middle.left, (viewsize.height / 2) - middle.top); mLocalMap->setViewOffset(viewOffset); } } void LocalMapBase::setPlayerDir(const float x, const float y) { if (x == mLastDirectionX && y == mLastDirectionY) return; notifyPlayerUpdate (); MyGUI::ISubWidget* main = mCompass->getSubWidgetMain(); MyGUI::RotatingSkin* rotatingSubskin = main->castType(); rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); float angle = std::atan2(x,y); rotatingSubskin->setAngle(angle); mLastDirectionX = x; mLastDirectionY = y; } void LocalMapBase::addDetectionMarkers(int type) { std::vector markers; MWBase::World* world = MWBase::Environment::get().getWorld(); world->listDetectedReferences( world->getPlayerPtr(), markers, MWBase::World::DetectionType(type)); if (markers.empty()) return; std::string markerTexture; if (type == MWBase::World::Detect_Creature) { markerTexture = "textures\\detect_animal_icon.dds"; } if (type == MWBase::World::Detect_Key) { markerTexture = "textures\\detect_key_icon.dds"; } if (type == MWBase::World::Detect_Enchantment) { markerTexture = "textures\\detect_enchantment_icon.dds"; } int counter = 0; for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) { const ESM::Position& worldPos = it->getRefData().getPosition(); MarkerUserData markerPos (mLocalMapRender); MyGUI::IntPoint widgetPos = getMarkerPosition(worldPos.pos[0], worldPos.pos[1], markerPos); MyGUI::IntCoord widgetCoord(widgetPos.left - 4, widgetPos.top - 4, 8, 8); ++counter; MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", widgetCoord, MyGUI::Align::Default); markerWidget->setDepth(Local_MarkerAboveFogLayer); markerWidget->setImageTexture(markerTexture); markerWidget->setImageCoord(MyGUI::IntCoord(0,0,8,8)); markerWidget->setNeedMouseFocus(false); mMagicMarkerWidgets.push_back(markerWidget); } } void LocalMapBase::onFrame(float dt) { mMarkerUpdateTimer += dt; if (mMarkerUpdateTimer >= 0.25) { mMarkerUpdateTimer = 0; updateMagicMarkers(); } } void LocalMapBase::updateMagicMarkers() { // clear all previous markers for (std::vector::iterator it = mMagicMarkerWidgets.begin(); it != mMagicMarkerWidgets.end(); ++it) MyGUI::Gui::getInstance().destroyWidget(*it); mMagicMarkerWidgets.clear(); addDetectionMarkers(MWBase::World::Detect_Creature); addDetectionMarkers(MWBase::World::Detect_Key); addDetectionMarkers(MWBase::World::Detect_Enchantment); // Add marker for the spot marked with Mark magic effect MWWorld::CellStore* markedCell = NULL; ESM::Position markedPosition; MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); if (markedCell && markedCell->isExterior() == !mInterior && (!mInterior || Misc::StringUtils::ciEqual(markedCell->getCell()->mName, mPrefix))) { MarkerUserData markerPos (mLocalMapRender); MyGUI::IntPoint widgetPos = getMarkerPosition(markedPosition.pos[0], markedPosition.pos[1], markerPos); MyGUI::IntCoord widgetCoord(widgetPos.left - 4, widgetPos.top - 4, 8, 8); MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", widgetCoord, MyGUI::Align::Default); markerWidget->setDepth(Local_MarkerAboveFogLayer); markerWidget->setImageTexture("textures\\menu_map_smark.dds"); markerWidget->setNeedMouseFocus(false); mMagicMarkerWidgets.push_back(markerWidget); } redraw(); } // ------------------------------------------------------------------------------------------ MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender) : WindowPinnableBase("openmw_map_window.layout") , LocalMapBase(customMarkers, localMapRender) , NoDrop(drag, mMainWidget) , mGlobalMap(0) , mGlobalMapImage(NULL) , mGlobalMapOverlay(NULL) , mGlobal(false) , mEventBoxGlobal(NULL) , mEventBoxLocal(NULL) , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot())) , mEditNoteDialog() { static bool registered = false; if (!registered) { MyGUI::FactoryManager::getInstance().registerFactory("Widget"); registered = true; } mEditNoteDialog.setVisible(false); mEditNoteDialog.eventOkClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditOk); mEditNoteDialog.eventDeleteClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditDelete); setCoord(500,0,320,300); getWidget(mLocalMap, "LocalMap"); getWidget(mGlobalMap, "GlobalMap"); getWidget(mGlobalMapImage, "GlobalMapImage"); getWidget(mGlobalMapOverlay, "GlobalMapOverlay"); getWidget(mPlayerArrowLocal, "CompassLocal"); getWidget(mPlayerArrowGlobal, "CompassGlobal"); mPlayerArrowGlobal->setDepth(Global_CompassLayer); mPlayerArrowGlobal->setNeedMouseFocus(false); mGlobalMapImage->setDepth(Global_MapLayer); mGlobalMapOverlay->setDepth(Global_ExploreOverlayLayer); mLastScrollWindowCoordinates = mLocalMap->getCoord(); mLocalMap->eventChangeCoord += MyGUI::newDelegate(this, &MapWindow::onChangeScrollWindowCoord); mGlobalMap->setVisible (false); getWidget(mButton, "WorldButton"); mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); mButton->setCaptionWithReplacing("#{sWorld}"); getWidget(mEventBoxGlobal, "EventBoxGlobal"); mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); mEventBoxGlobal->setDepth(Global_ExploreOverlayLayer); getWidget(mEventBoxLocal, "EventBoxLocal"); mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked); LocalMapBase::init(mLocalMap, mPlayerArrowLocal, Settings::Manager::getInt("local map widget size", "Map")); } void MapWindow::onNoteEditOk() { if (mEditNoteDialog.getDeleteButtonShown()) mCustomMarkers.updateMarker(mEditingMarker, mEditNoteDialog.getText()); else { mEditingMarker.mNote = mEditNoteDialog.getText(); mCustomMarkers.addMarker(mEditingMarker); } mEditNoteDialog.setVisible(false); } void MapWindow::onNoteEditDelete() { ConfirmationDialog* confirmation = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); confirmation->askForConfirmation("#{sDeleteNote}", "#{sYes}", "#{sNo}"); confirmation->eventCancelClicked.clear(); confirmation->eventOkClicked.clear(); confirmation->eventOkClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditDeleteConfirm); } void MapWindow::onNoteEditDeleteConfirm() { mCustomMarkers.deleteMarker(mEditingMarker); mEditNoteDialog.setVisible(false); } void MapWindow::onCustomMarkerDoubleClicked(MyGUI::Widget *sender) { mEditingMarker = *sender->getUserData(); mEditNoteDialog.setText(mEditingMarker.mNote); mEditNoteDialog.showDeleteButton(true); mEditNoteDialog.setVisible(true); } void MapWindow::onMapDoubleClicked(MyGUI::Widget *sender) { MyGUI::IntPoint clickedPos = MyGUI::InputManager::getInstance().getMousePosition(); MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition(); int x = int(widgetPos.left/float(mMapWidgetSize))-1; int y = (int(widgetPos.top/float(mMapWidgetSize))-1)*-1; float nX = widgetPos.left/float(mMapWidgetSize) - int(widgetPos.left/float(mMapWidgetSize)); float nY = widgetPos.top/float(mMapWidgetSize) - int(widgetPos.top/float(mMapWidgetSize)); x += mCurX; y += mCurY; osg::Vec2f worldPos; if (mInterior) { worldPos = mLocalMapRender->interiorMapToWorldPosition(nX, nY, x, y); } else { worldPos.x() = (x + nX) * cellSize; worldPos.y() = (y + (1.0f-nY)) * cellSize; } mEditingMarker.mWorldX = worldPos.x(); mEditingMarker.mWorldY = worldPos.y(); mEditingMarker.mCell.mPaged = !mInterior; if (mInterior) mEditingMarker.mCell.mWorldspace = LocalMapBase::mPrefix; else { mEditingMarker.mCell.mWorldspace = "sys::default"; mEditingMarker.mCell.mIndex.mX = x; mEditingMarker.mCell.mIndex.mY = y; } mEditNoteDialog.setVisible(true); mEditNoteDialog.showDeleteButton(false); mEditNoteDialog.setText(""); } void MapWindow::onChangeScrollWindowCoord(MyGUI::Widget* sender) { MyGUI::IntCoord currentCoordinates = sender->getCoord(); MyGUI::IntPoint currentViewPortCenter = MyGUI::IntPoint(currentCoordinates.width / 2, currentCoordinates.height / 2); MyGUI::IntPoint lastViewPortCenter = MyGUI::IntPoint(mLastScrollWindowCoordinates.width / 2, mLastScrollWindowCoordinates.height / 2); MyGUI::IntPoint viewPortCenterDiff = currentViewPortCenter - lastViewPortCenter; mLocalMap->setViewOffset(mLocalMap->getViewOffset() + viewPortCenterDiff); mGlobalMap->setViewOffset(mGlobalMap->getViewOffset() + viewPortCenterDiff); mLastScrollWindowCoordinates = currentCoordinates; } void MapWindow::renderGlobalMap(Loading::Listener* loadingListener) { mGlobalMapRender->render(loadingListener); mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); mGlobalMapTexture.reset(new osgMyGUI::OSGTexture(mGlobalMapRender->getBaseTexture())); mGlobalMapImage->setRenderItemTexture(mGlobalMapTexture.get()); mGlobalMapImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); mGlobalMapOverlayTexture.reset(new osgMyGUI::OSGTexture(mGlobalMapRender->getOverlayTexture())); mGlobalMapOverlay->setRenderItemTexture(mGlobalMapOverlayTexture.get()); mGlobalMapOverlay->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); } MapWindow::~MapWindow() { delete mGlobalMapRender; } void MapWindow::setCellName(const std::string& cellName) { setTitle("#{sCell=" + cellName + "}"); } void MapWindow::addVisitedLocation(const std::string& name, int x, int y) { CellId cell; cell.first = x; cell.second = y; if (mMarkers.insert(cell).second) { float worldX, worldY; mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY); int markerSize = 12; int offset = mGlobalMapRender->getCellSize()/2 - markerSize/2; MyGUI::IntCoord widgetCoord( static_cast(worldX * mGlobalMapRender->getWidth()+offset), static_cast(worldY * mGlobalMapRender->getHeight() + offset), markerSize, markerSize); MyGUI::Widget* markerWidget = mGlobalMap->createWidget("MarkerButton", widgetCoord, MyGUI::Align::Default); markerWidget->setUserString("Caption_TextOneLine", "#{sCell=" + name + "}"); setGlobalMapMarkerTooltip(markerWidget, x, y); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); markerWidget->setNeedMouseFocus(true); markerWidget->setColour(MyGUI::Colour::parse(MyGUI::LanguageManager::getInstance().replaceTags("#{fontcolour=normal}"))); markerWidget->setDepth(Global_MarkerLayer); markerWidget->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); markerWidget->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); mGlobalMapMarkers[std::make_pair(x,y)] = markerWidget; } } void MapWindow::cellExplored(int x, int y) { mQueuedToExplore.push_back(std::make_pair(x,y)); } void MapWindow::onFrame(float dt) { LocalMapBase::onFrame(dt); mGlobalMapRender->cleanupCameras(); for (std::vector::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it) { mGlobalMapRender->exploreCell(it->first, it->second, mLocalMapRender->getMapTexture(it->first, it->second)); } mQueuedToExplore.clear(); NoDrop::onFrame(dt); } void MapWindow::setGlobalMapMarkerTooltip(MyGUI::Widget* markerWidget, int x, int y) { ESM::CellId cellId; cellId.mIndex.mX = x; cellId.mIndex.mY = y; cellId.mWorldspace = "sys::default"; cellId.mPaged = true; CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellId); std::vector destNotes; for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second; ++it) destNotes.push_back(it->second.mNote); if (!destNotes.empty()) { MarkerUserData data (NULL); data.notes = destNotes; data.caption = markerWidget->getUserString("Caption_TextOneLine"); markerWidget->setUserData(data); markerWidget->setUserString("ToolTipType", "MapMarker"); } else { markerWidget->setUserString("ToolTipType", "Layout"); } } void MapWindow::updateCustomMarkers() { LocalMapBase::updateCustomMarkers(); for (std::map, MyGUI::Widget*>::iterator widgetIt = mGlobalMapMarkers.begin(); widgetIt != mGlobalMapMarkers.end(); ++widgetIt) { int x = widgetIt->first.first; int y = widgetIt->first.second; MyGUI::Widget* markerWidget = widgetIt->second; setGlobalMapMarkerTooltip(markerWidget, x, y); } } void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { if (_id!=MyGUI::MouseButton::Left) return; mLastDragPos = MyGUI::IntPoint(_left, _top); } void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { if (_id!=MyGUI::MouseButton::Left) return; MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; if (!mGlobal) mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); else mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); mLastDragPos = MyGUI::IntPoint(_left, _top); } void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) { mGlobal = !mGlobal; mGlobalMap->setVisible(mGlobal); mLocalMap->setVisible(!mGlobal); mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : "#{sWorld}"); if (mGlobal) globalMapUpdatePlayer (); } void MapWindow::onPinToggled() { MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned); } void MapWindow::onTitleDoubleClicked() { if (!mPinned) MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); } void MapWindow::open() { globalMapUpdatePlayer(); } void MapWindow::globalMapUpdatePlayer () { // For interiors, position is set by WindowManager via setGlobalMapPlayerPosition if (MWBase::Environment::get().getWorld ()->isCellExterior ()) { osg::Vec3f pos = MWBase::Environment::get().getWorld ()->getPlayerPtr().getRefData().getPosition().asVec3(); setGlobalMapPlayerPosition(pos.x(), pos.y()); } } void MapWindow::notifyPlayerUpdate () { globalMapUpdatePlayer (); setGlobalMapPlayerDir(mLastDirectionX, mLastDirectionY); } void MapWindow::setGlobalMapPlayerPosition(float worldX, float worldY) { float x, y; mGlobalMapRender->worldPosToImageSpace (worldX, worldY, x, y); x *= mGlobalMapRender->getWidth(); y *= mGlobalMapRender->getHeight(); mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(static_cast(x - 16), static_cast(y - 16))); // set the view offset so that player is in the center MyGUI::IntSize viewsize = mGlobalMap->getSize(); MyGUI::IntPoint viewoffs(static_cast(viewsize.width * 0.5f - x), static_cast(viewsize.height *0.5 - y)); mGlobalMap->setViewOffset(viewoffs); } void MapWindow::setGlobalMapPlayerDir(const float x, const float y) { MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); MyGUI::RotatingSkin* rotatingSubskin = main->castType(); rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); float angle = std::atan2(x,y); rotatingSubskin->setAngle(angle); } void MapWindow::clear() { mMarkers.clear(); mGlobalMapRender->clear(); mChanged = true; for (std::map, MyGUI::Widget*>::iterator it = mGlobalMapMarkers.begin(); it != mGlobalMapMarkers.end(); ++it) MyGUI::Gui::getInstance().destroyWidget(it->second); mGlobalMapMarkers.clear(); } void MapWindow::write(ESM::ESMWriter &writer, Loading::Listener& progress) { ESM::GlobalMap map; mGlobalMapRender->write(map); map.mMarkers = mMarkers; writer.startRecord(ESM::REC_GMAP); map.save(writer); writer.endRecord(ESM::REC_GMAP); } void MapWindow::readRecord(ESM::ESMReader &reader, uint32_t type) { if (type == ESM::REC_GMAP) { ESM::GlobalMap map; map.load(reader); mGlobalMapRender->read(map); for (std::set::iterator it = map.mMarkers.begin(); it != map.mMarkers.end(); ++it) { const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first, it->second); if (cell && !cell->mName.empty()) addVisitedLocation(cell->mName, it->first, it->second); } } } void MapWindow::setAlpha(float alpha) { NoDrop::setAlpha(alpha); // can't allow showing map with partial transparency, as the fog of war will also go transparent // and reveal parts of the map you shouldn't be able to see for (std::vector::iterator it = mMapWidgets.begin(); it != mMapWidgets.end(); ++it) (*it)->setVisible(alpha == 1); } void MapWindow::customMarkerCreated(MyGUI::Widget *marker) { marker->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); marker->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); marker->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onCustomMarkerDoubleClicked); } void MapWindow::doorMarkerCreated(MyGUI::Widget *marker) { marker->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); marker->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); } // ------------------------------------------------------------------- EditNoteDialog::EditNoteDialog() : WindowModal("openmw_edit_note.layout") { getWidget(mOkButton, "OkButton"); getWidget(mCancelButton, "CancelButton"); getWidget(mDeleteButton, "DeleteButton"); getWidget(mTextEdit, "TextEdit"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onCancelButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onOkButtonClicked); mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditNoteDialog::onDeleteButtonClicked); } void EditNoteDialog::showDeleteButton(bool show) { mDeleteButton->setVisible(show); } bool EditNoteDialog::getDeleteButtonShown() { return mDeleteButton->getVisible(); } void EditNoteDialog::setText(const std::string &text) { mTextEdit->setCaption(MyGUI::TextIterator::toTagsString(text)); } std::string EditNoteDialog::getText() { return MyGUI::TextIterator::getOnlyText(mTextEdit->getCaption()); } void EditNoteDialog::open() { WindowModal::open(); center(); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } void EditNoteDialog::exit() { setVisible(false); } void EditNoteDialog::onCancelButtonClicked(MyGUI::Widget *sender) { setVisible(false); } void EditNoteDialog::onOkButtonClicked(MyGUI::Widget *sender) { eventOkClicked(); } void EditNoteDialog::onDeleteButtonClicked(MyGUI::Widget *sender) { eventDeleteClicked(); } bool LocalMapBase::MarkerUserData::isPositionExplored() const { if (!mLocalMapRender) return true; return mLocalMapRender->isPositionExplored(nX, nY, cellX, cellY, interior); } } openmw-openmw-0.38.0/apps/openmw/mwgui/mapwindow.hpp000066400000000000000000000176521264522266000225500ustar00rootroot00000000000000#ifndef MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H #include #include #include "windowpinnablebase.hpp" #include #include namespace MWRender { class GlobalMap; class LocalMap; } namespace ESM { class ESMReader; class ESMWriter; } namespace Loading { class Listener; } namespace MWGui { class CustomMarkerCollection { public: void addMarker(const ESM::CustomMarker& marker, bool triggerEvent=true); void deleteMarker (const ESM::CustomMarker& marker); void updateMarker(const ESM::CustomMarker& marker, const std::string& newNote); void clear(); size_t size() const; typedef std::multimap ContainerType; typedef std::pair RangeType; ContainerType::const_iterator begin() const; ContainerType::const_iterator end() const; RangeType getMarkers(const ESM::CellId& cellId) const; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; EventHandle_Void eventMarkersChanged; private: ContainerType mMarkers; }; class LocalMapBase { public: LocalMapBase(CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender); virtual ~LocalMapBase(); void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize); void setCellPrefix(const std::string& prefix); void setActiveCell(const int x, const int y, bool interior=false); void setPlayerDir(const float x, const float y); void setPlayerPos(int cellX, int cellY, const float nx, const float ny); void onFrame(float dt); bool toggleFogOfWar(); struct MarkerUserData { MarkerUserData(MWRender::LocalMap* map) : mLocalMapRender(map) , interior(false) , cellX(0) , cellY(0) , nX(0.f) , nY(0.f) { } bool isPositionExplored() const; MWRender::LocalMap* mLocalMapRender; bool interior; int cellX; int cellY; float nX; float nY; std::vector notes; std::string caption; }; protected: MWRender::LocalMap* mLocalMapRender; int mCurX, mCurY; bool mInterior; MyGUI::ScrollView* mLocalMap; MyGUI::ImageBox* mCompass; std::string mPrefix; bool mChanged; bool mFogOfWar; int mMapWidgetSize; // Stores markers that were placed by a player. May be shared between multiple map views. CustomMarkerCollection& mCustomMarkers; std::vector mMapWidgets; std::vector mFogWidgets; typedef std::vector > TextureVector; TextureVector mMapTextures; TextureVector mFogTextures; // Keep track of created marker widgets, just to easily remove them later. std::vector mDoorMarkerWidgets; std::vector mMagicMarkerWidgets; std::vector mCustomMarkerWidgets; virtual void updateCustomMarkers(); void applyFogOfWar(); MyGUI::IntPoint getMarkerPosition (float worldX, float worldY, MarkerUserData& markerPos); virtual void notifyPlayerUpdate() {} virtual void notifyMapChanged() {} virtual void customMarkerCreated(MyGUI::Widget* marker) {} virtual void doorMarkerCreated(MyGUI::Widget* marker) {} void updateMagicMarkers(); void addDetectionMarkers(int type); void redraw(); float mMarkerUpdateTimer; float mLastDirectionX; float mLastDirectionY; }; class EditNoteDialog : public MWGui::WindowModal { public: EditNoteDialog(); virtual void open(); virtual void exit(); void showDeleteButton(bool show); bool getDeleteButtonShown(); void setText(const std::string& text); std::string getText(); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; EventHandle_Void eventDeleteClicked; EventHandle_Void eventOkClicked; private: void onCancelButtonClicked(MyGUI::Widget* sender); void onOkButtonClicked(MyGUI::Widget* sender); void onDeleteButtonClicked(MyGUI::Widget* sender); MyGUI::TextBox* mTextEdit; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; MyGUI::Button* mDeleteButton; }; class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop { public: MapWindow(CustomMarkerCollection& customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender); virtual ~MapWindow(); void setCellName(const std::string& cellName); virtual void setAlpha(float alpha); void renderGlobalMap(Loading::Listener* loadingListener); /// adds the marker to the global map /// @param name The ESM::Cell::mName void addVisitedLocation(const std::string& name, int x, int y); // reveals this cell's map on the global map void cellExplored(int x, int y); void setGlobalMapPlayerPosition (float worldX, float worldY); void setGlobalMapPlayerDir(const float x, const float y); virtual void open(); void onFrame(float dt); virtual void updateCustomMarkers(); /// Clear all savegame-specific data void clear(); void write (ESM::ESMWriter& writer, Loading::Listener& progress); void readRecord (ESM::ESMReader& reader, uint32_t type); private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onWorldButtonClicked(MyGUI::Widget* _sender); void onMapDoubleClicked(MyGUI::Widget* sender); void onCustomMarkerDoubleClicked(MyGUI::Widget* sender); void onNoteEditOk(); void onNoteEditDelete(); void onNoteEditDeleteConfirm(); void onNoteDoubleClicked(MyGUI::Widget* sender); void onChangeScrollWindowCoord(MyGUI::Widget* sender); void globalMapUpdatePlayer(); void setGlobalMapMarkerTooltip(MyGUI::Widget* widget, int x, int y); MyGUI::ScrollView* mGlobalMap; std::auto_ptr mGlobalMapTexture; std::auto_ptr mGlobalMapOverlayTexture; MyGUI::ImageBox* mGlobalMapImage; MyGUI::ImageBox* mGlobalMapOverlay; MyGUI::ImageBox* mPlayerArrowLocal; MyGUI::ImageBox* mPlayerArrowGlobal; MyGUI::Button* mButton; MyGUI::IntPoint mLastDragPos; bool mGlobal; MyGUI::IntCoord mLastScrollWindowCoordinates; // Markers on global map typedef std::pair CellId; std::set mMarkers; // Cells that should be explored in the next frame (i.e. their map revealed on the global map) // We can't do this immediately, because the map update is not immediate either (see mNeedMapUpdate in scene.cpp) std::vector mQueuedToExplore; MyGUI::Button* mEventBoxGlobal; MyGUI::Button* mEventBoxLocal; MWRender::GlobalMap* mGlobalMapRender; std::map, MyGUI::Widget*> mGlobalMapMarkers; EditNoteDialog mEditNoteDialog; ESM::CustomMarker mEditingMarker; virtual void onPinToggled(); virtual void onTitleDoubleClicked(); virtual void doorMarkerCreated(MyGUI::Widget* marker); virtual void customMarkerCreated(MyGUI::Widget *marker); virtual void notifyPlayerUpdate(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/merchantrepair.cpp000066400000000000000000000126501264522266000235330ustar00rootroot00000000000000#include "merchantrepair.hpp" #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" namespace MWGui { MerchantRepair::MerchantRepair() : WindowBase("openmw_merchantrepair.layout") { getWidget(mList, "RepairView"); getWidget(mOkButton, "OkButton"); getWidget(mGoldLabel, "PlayerGold"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onOkButtonClick); } void MerchantRepair::startRepair(const MWWorld::Ptr &actor) { mActor = actor; while (mList->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mList->getChildAt(0)); int currentY = 0; MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor; for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); iter!=store.end(); ++iter) { if (iter->getClass().hasItemHealth(*iter)) { int maxDurability = iter->getClass().getItemMaxHealth(*iter); int durability = iter->getClass().getItemHealth(*iter); if (maxDurability == durability) continue; int basePrice = iter->getClass().getValue(*iter); float fRepairMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fRepairMult")->getFloat(); float p = static_cast(std::max(1, basePrice)); float r = static_cast(std::max(1, static_cast(maxDurability / p))); int x = static_cast((maxDurability - durability) / r); x = static_cast(fRepairMult * x); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mActor, x, true); std::string name = iter->getClass().getName(*iter) + " - " + MyGUI::utility::toString(price) + MWBase::Environment::get().getWorld()->getStore().get() .find("sgp")->getString(); MyGUI::Button* button = mList->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip 0, currentY, 0, 18, MyGUI::Align::Default ); currentY += 18; button->setUserString("Price", MyGUI::utility::toString(price)); button->setUserData(*iter); button->setCaptionWithReplacing(name); button->setSize(button->getTextSize().width,18); button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel); button->setUserString("ToolTipType", "ItemPtr"); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick); } } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mList->setVisibleVScroll(false); mList->setCanvasSize (MyGUI::IntSize(mList->getWidth(), std::max(mList->getHeight(), currentY))); mList->setVisibleVScroll(true); mGoldLabel->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); } void MerchantRepair::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (mList->getViewOffset().top + _rel*0.3f > 0) mList->setViewOffset(MyGUI::IntPoint(0, 0)); else mList->setViewOffset(MyGUI::IntPoint(0, static_cast(mList->getViewOffset().top + _rel*0.3f))); } void MerchantRepair::open() { center(); // Reset scrollbars mList->setViewOffset(MyGUI::IntPoint(0, 0)); } void MerchantRepair::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_MerchantRepair); } void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) { MWWorld::Ptr player = MWMechanics::getPlayer(); int price = MyGUI::utility::parseInt(sender->getUserString("Price")); if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) return; // repair MWWorld::Ptr item = *sender->getUserData(); item.getCellRef().setCharge(item.getClass().getItemMaxHealth(item)); player.getClass().getContainerStore(player).restack(item); MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); // add gold to NPC trading gold pool MWMechanics::CreatureStats& actorStats = mActor.getClass().getCreatureStats(mActor); actorStats.setGoldPool(actorStats.getGoldPool() + price); startRepair(mActor); } void MerchantRepair::onOkButtonClick(MyGUI::Widget *sender) { exit(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/merchantrepair.hpp000066400000000000000000000012021264522266000235270ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_MERCHANTREPAIR_H #define OPENMW_MWGUI_MERCHANTREPAIR_H #include "windowbase.hpp" #include "../mwworld/ptr.hpp" namespace MWGui { class MerchantRepair : public WindowBase { public: MerchantRepair(); virtual void open(); virtual void exit(); void startRepair(const MWWorld::Ptr& actor); private: MyGUI::ScrollView* mList; MyGUI::Button* mOkButton; MyGUI::TextBox* mGoldLabel; MWWorld::Ptr mActor; protected: void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onRepairButtonClick(MyGUI::Widget* sender); void onOkButtonClick(MyGUI::Widget* sender); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/messagebox.cpp000066400000000000000000000324011264522266000226600ustar00rootroot00000000000000#include "messagebox.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" #undef MessageBox namespace MWGui { MessageBoxManager::MessageBoxManager (float timePerChar) { mInterMessageBoxe = NULL; mStaticMessageBox = NULL; mLastButtonPressed = -1; mMessageBoxSpeed = timePerChar; } MessageBoxManager::~MessageBoxManager () { std::vector::iterator it(mMessageBoxes.begin()); for (; it != mMessageBoxes.end(); ++it) { delete *it; } } void MessageBoxManager::clear() { delete mInterMessageBoxe; mInterMessageBoxe = NULL; std::vector::iterator it(mMessageBoxes.begin()); for (; it != mMessageBoxes.end(); ++it) { if (*it == mStaticMessageBox) mStaticMessageBox = NULL; delete *it; } mMessageBoxes.clear(); mLastButtonPressed = -1; } void MessageBoxManager::onFrame (float frameDuration) { std::vector::iterator it; for(it = mMessageBoxes.begin(); it != mMessageBoxes.end();) { (*it)->mCurrentTime += frameDuration; if((*it)->mCurrentTime >= (*it)->mMaxTime && *it != mStaticMessageBox) { delete *it; it = mMessageBoxes.erase(it); } else ++it; } float height = 0; it = mMessageBoxes.begin(); while(it != mMessageBoxes.end()) { (*it)->update(static_cast(height)); height += (*it)->getHeight(); ++it; } if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { mLastButtonPressed = mInterMessageBoxe->readPressedButton(); delete mInterMessageBoxe; mInterMessageBoxe = NULL; MWBase::Environment::get().getInputManager()->changeInputMode( MWBase::Environment::get().getWindowManager()->isGuiMode()); } } void MessageBoxManager::createMessageBox (const std::string& message, bool stat) { MessageBox *box = new MessageBox(*this, message); box->mCurrentTime = 0; std::string realMessage = MyGUI::LanguageManager::getInstance().replaceTags(message); box->mMaxTime = realMessage.length()*mMessageBoxSpeed; if(stat) mStaticMessageBox = box; mMessageBoxes.push_back(box); if(mMessageBoxes.size() > 3) { delete *mMessageBoxes.begin(); mMessageBoxes.erase(mMessageBoxes.begin()); } int height = 0; for(std::vector::iterator it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) { (*it)->update(height); height += (*it)->getHeight(); } } void MessageBoxManager::removeStaticMessageBox () { removeMessageBox(mStaticMessageBox); mStaticMessageBox = NULL; } bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) { if (mInterMessageBoxe != NULL) { std::cerr << "Warning: replacing an interactive message box that was not answered yet" << std::endl; delete mInterMessageBoxe; mInterMessageBoxe = NULL; } mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); mLastButtonPressed = -1; return true; } bool MessageBoxManager::isInteractiveMessageBox () { return mInterMessageBoxe != NULL; } bool MessageBoxManager::removeMessageBox (MessageBox *msgbox) { std::vector::iterator it; for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) { if((*it) == msgbox) { delete (*it); mMessageBoxes.erase(it); return true; } } return false; } int MessageBoxManager::readPressedButton (bool reset) { int pressed = mLastButtonPressed; if (reset) mLastButtonPressed = -1; return pressed; } MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) : Layout("openmw_messagebox.layout") , mCurrentTime(0) , mMaxTime(0) , mMessageBoxManager(parMessageBoxManager) , mMessage(message) { // defines mBottomPadding = 48; mNextBoxPadding = 4; getWidget(mMessageWidget, "message"); mMessageWidget->setCaptionWithReplacing(mMessage); } void MessageBox::update (int height) { MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntPoint pos; pos.left = (gameWindowSize.width - mMainWidget->getWidth())/2; pos.top = (gameWindowSize.height - mMainWidget->getHeight() - height - mBottomPadding); mMainWidget->setPosition(pos); } int MessageBox::getHeight () { return mMainWidget->getHeight()+mNextBoxPadding; } InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) : WindowModal("openmw_interactive_messagebox.layout") , mMessageBoxManager(parMessageBoxManager) , mButtonPressed(-1) { WindowModal::open(); int textPadding = 10; // padding between text-widget and main-widget int textButtonPadding = 10; // padding between the text-widget und the button-widget int buttonLeftPadding = 10; // padding between the buttons if horizontal int buttonTopPadding = 10; // ^-- if vertical int buttonPadding = 5; // padding between button label and button itself int buttonMainPadding = 10; // padding between buttons and bottom of the main widget mMarkedToDelete = false; getWidget(mMessageWidget, "message"); getWidget(mButtonsWidget, "buttons"); mMessageWidget->setSize(400, mMessageWidget->getHeight()); mMessageWidget->setCaptionWithReplacing(message); MyGUI::IntSize textSize = mMessageWidget->getTextSize(); MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); int biggestButtonWidth = 0; int buttonsWidth = 0; int buttonsHeight = 0; int buttonHeight = 0; MyGUI::IntCoord dummyCoord(0, 0, 0, 0); std::vector::const_iterator it; for(it = buttons.begin(); it != buttons.end(); ++it) { MyGUI::Button* button = mButtonsWidget->createWidget( MyGUI::WidgetStyle::Child, std::string("MW_Button"), dummyCoord, MyGUI::Align::Default); button->setCaptionWithReplacing(*it); button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); mButtons.push_back(button); if (buttonsWidth != 0) buttonsWidth += buttonLeftPadding; int buttonWidth = button->getTextSize().width + 2*buttonPadding; buttonsWidth += buttonWidth; buttonHeight = button->getTextSize().height + 2*buttonPadding; if (buttonsHeight != 0) buttonsHeight += buttonTopPadding; buttonsHeight += buttonHeight; if(buttonWidth > biggestButtonWidth) { biggestButtonWidth = buttonWidth; } } MyGUI::IntSize mainWidgetSize; if(buttonsWidth < textSize.width) { // on one line mainWidgetSize.width = textSize.width + 3*textPadding; mainWidgetSize.height = textPadding + textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; MyGUI::IntSize realSize = mainWidgetSize + // To account for borders (mMainWidget->getSize() - mMainWidget->getClientWidget()->getSize()); MyGUI::IntPoint absPos; absPos.left = (gameWindowSize.width - realSize.width)/2; absPos.top = (gameWindowSize.height - realSize.height)/2; mMainWidget->setPosition(absPos); mMainWidget->setSize(realSize); MyGUI::IntCoord messageWidgetCoord; messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; messageWidgetCoord.top = textPadding; mMessageWidget->setCoord(messageWidgetCoord); mMessageWidget->setSize(textSize); MyGUI::IntCoord buttonCord; MyGUI::IntSize buttonSize(0, buttonHeight); int left = (mainWidgetSize.width - buttonsWidth)/2; std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { buttonCord.left = left; buttonCord.top = messageWidgetCoord.top + textSize.height + textButtonPadding; buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding; buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding; (*button)->setCoord(buttonCord); (*button)->setSize(buttonSize); left += buttonSize.width + buttonLeftPadding; } } else { // among each other if(biggestButtonWidth > textSize.width) { mainWidgetSize.width = biggestButtonWidth + buttonTopPadding*2; } else { mainWidgetSize.width = textSize.width + 3*textPadding; } MyGUI::IntCoord buttonCord; MyGUI::IntSize buttonSize(0, buttonHeight); int top = textPadding + textSize.height + textButtonPadding; std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { buttonSize.width = (*button)->getTextSize().width + buttonPadding*2; buttonSize.height = (*button)->getTextSize().height + buttonPadding*2; buttonCord.top = top; buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2; (*button)->setCoord(buttonCord); (*button)->setSize(buttonSize); top += buttonSize.height + buttonTopPadding; } mainWidgetSize.height = textPadding + textSize.height + textButtonPadding + buttonsHeight + buttonMainPadding; mMainWidget->setSize(mainWidgetSize + // To account for borders (mMainWidget->getSize() - mMainWidget->getClientWidget()->getSize())); MyGUI::IntPoint absPos; absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2; absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2; mMainWidget->setPosition(absPos); MyGUI::IntCoord messageWidgetCoord; messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; messageWidgetCoord.top = textPadding; messageWidgetCoord.width = textSize.width; messageWidgetCoord.height = textSize.height; mMessageWidget->setCoord(messageWidgetCoord); } // Set key focus to "Ok" button std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok)) { MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button); (*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed); break; } } } void InteractiveMessageBox::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) { if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter || _key == MyGUI::KeyCode::Space) buttonActivated(_sender); } void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) { buttonActivated (pressed); } void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed) { mMarkedToDelete = true; int index = 0; std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { if(*button == pressed) { mButtonPressed = index; mMessageBoxManager.onButtonPressed(mButtonPressed); return; } index++; } } int InteractiveMessageBox::readPressedButton () { return mButtonPressed; } } openmw-openmw-0.38.0/apps/openmw/mwgui/messagebox.hpp000066400000000000000000000055351264522266000226750ustar00rootroot00000000000000#ifndef MWGUI_MESSAGE_BOX_H #define MWGUI_MESSAGE_BOX_H #include "windowbase.hpp" #undef MessageBox namespace MyGUI { class Widget; class Button; class EditBox; } namespace MWGui { class InteractiveMessageBox; class MessageBoxManager; class MessageBox; class MessageBoxManager { public: MessageBoxManager (float timePerChar); ~MessageBoxManager (); void onFrame (float frameDuration); void createMessageBox (const std::string& message, bool stat = false); void removeStaticMessageBox (); bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); bool isInteractiveMessageBox (); /// Remove all message boxes void clear(); bool removeMessageBox (MessageBox *msgbox); /// @param reset Reset the pressed button to -1 after reading it. int readPressedButton (bool reset=true); typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; // Note: this delegate unassigns itself after it was fired, i.e. works once. EventHandle_Int eventButtonPressed; void onButtonPressed(int button) { eventButtonPressed(button); eventButtonPressed.clear(); } private: std::vector mMessageBoxes; InteractiveMessageBox* mInterMessageBoxe; MessageBox* mStaticMessageBox; float mMessageBoxSpeed; int mLastButtonPressed; }; class MessageBox : public Layout { public: MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message); void setMessage (const std::string& message); int getHeight (); void update (int height); float mCurrentTime; float mMaxTime; protected: MessageBoxManager& mMessageBoxManager; const std::string& mMessage; MyGUI::EditBox* mMessageWidget; int mBottomPadding; int mNextBoxPadding; }; class InteractiveMessageBox : public WindowModal { public: InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); bool mMarkedToDelete; private: void buttonActivated (MyGUI::Widget* _widget); void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char); MessageBoxManager& mMessageBoxManager; MyGUI::EditBox* mMessageWidget; MyGUI::Widget* mButtonsWidget; std::vector mButtons; int mButtonPressed; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/mode.hpp000066400000000000000000000024171264522266000214600ustar00rootroot00000000000000#ifndef MWGUI_MODE_H #define MWGUI_MODE_H namespace MWGui { enum GuiMode { GM_None, GM_Settings, // Settings window GM_Inventory, // Inventory mode GM_Container, GM_Companion, GM_MainMenu, // Main menu mode GM_Console, // Console mode GM_Journal, // Journal mode GM_Scroll, // Read scroll GM_Book, // Read book GM_Alchemy, // Make potions GM_Repair, GM_Dialogue, // NPC interaction GM_Barter, GM_Rest, GM_RestBed, GM_SpellBuying, GM_Travel, GM_SpellCreation, GM_Enchanting, GM_Recharge, GM_Training, GM_MerchantRepair, GM_Levelup, // Startup character creation dialogs GM_Name, GM_Race, GM_Birth, GM_Class, GM_ClassGenerate, GM_ClassPick, GM_ClassCreate, GM_Review, GM_Loading, GM_LoadingWallpaper, GM_Jail, GM_QuickKeysMenu }; // Windows shown in inventory mode enum GuiWindow { GW_None = 0, GW_Map = 0x01, GW_Inventory = 0x02, GW_Magic = 0x04, GW_Stats = 0x08, GW_ALL = 0xFF }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/pickpocketitemmodel.cpp000066400000000000000000000036441264522266000245660ustar00rootroot00000000000000#include "pickpocketitemmodel.hpp" #include #include #include "../mwworld/class.hpp" namespace MWGui { PickpocketItemModel::PickpocketItemModel(const MWWorld::Ptr& thief, ItemModel *sourceModel, bool hideItems) { mSourceModel = sourceModel; int chance = thief.getClass().getSkill(thief, ESM::Skill::Sneak); mSourceModel->update(); // build list of items that player is unable to find when attempts to pickpocket. if (hideItems) { for (size_t i = 0; igetItemCount(); ++i) { if (Misc::Rng::roll0to99() > chance) mHiddenItems.push_back(mSourceModel->getItem(i)); } } } ItemStack PickpocketItemModel::getItem (ModelIndex index) { if (index < 0) throw std::runtime_error("Invalid index supplied"); if (mItems.size() <= static_cast(index)) throw std::runtime_error("Item index out of range"); return mItems[index]; } size_t PickpocketItemModel::getItemCount() { return mItems.size(); } void PickpocketItemModel::update() { mSourceModel->update(); mItems.clear(); for (size_t i = 0; igetItemCount(); ++i) { const ItemStack& item = mSourceModel->getItem(i); // Bound items may not be stolen if (item.mFlags & ItemStack::Flag_Bound) continue; if (std::find(mHiddenItems.begin(), mHiddenItems.end(), item) == mHiddenItems.end() && item.mType != ItemStack::Type_Equipped) mItems.push_back(item); } } void PickpocketItemModel::removeItem (const ItemStack &item, size_t count) { ProxyItemModel::removeItem(item, count); /// \todo check if player is detected } } openmw-openmw-0.38.0/apps/openmw/mwgui/pickpocketitemmodel.hpp000066400000000000000000000013421264522266000245640ustar00rootroot00000000000000#ifndef MWGUI_PICKPOCKET_ITEM_MODEL_H #define MWGUI_PICKPOCKET_ITEM_MODEL_H #include "itemmodel.hpp" namespace MWGui { /// @brief The pickpocket item model randomly hides item stacks based on a specified chance. Equipped items are always hidden. class PickpocketItemModel : public ProxyItemModel { public: PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel, bool hideItems=true); virtual ItemStack getItem (ModelIndex index); virtual size_t getItemCount(); virtual void update(); virtual void removeItem (const ItemStack& item, size_t count); private: std::vector mHiddenItems; std::vector mItems; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/quickkeysmenu.cpp000066400000000000000000000461361264522266000234320ustar00rootroot00000000000000#include "quickkeysmenu.hpp" #include #include #include #include #include #include #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "itemselection.hpp" #include "spellview.hpp" #include "itemwidget.hpp" #include "sortfilteritemmodel.hpp" namespace MWGui { QuickKeysMenu::QuickKeysMenu() : WindowBase("openmw_quickkeys_menu.layout") , mAssignDialog(0) , mItemSelectionDialog(0) , mMagicSelectionDialog(0) , mSelectedIndex(-1) { getWidget(mOkButton, "OKButton"); getWidget(mInstructionLabel, "InstructionLabel"); mMainWidget->setSize(mMainWidget->getWidth(), mMainWidget->getHeight() + (mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight())); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked); center(); for (int i = 0; i < 10; ++i) { ItemWidget* button; getWidget(button, "QuickKey" + MyGUI::utility::toString(i+1)); button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); mQuickKeyButtons.push_back(button); mAssigned.push_back(Type_Unassigned); unassign(button, i); } } void QuickKeysMenu::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_QuickKeysMenu); } void QuickKeysMenu::clear() { for (int i=0; i<10; ++i) { unassign(mQuickKeyButtons[i], i); } } QuickKeysMenu::~QuickKeysMenu() { delete mAssignDialog; delete mItemSelectionDialog; delete mMagicSelectionDialog; } void QuickKeysMenu::unassign(ItemWidget* key, int index) { key->clearUserStrings(); key->setItem(MWWorld::Ptr()); while (key->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(key->getChildAt(0)); if (index == 9) { mAssigned[index] = Type_HandToHand; MyGUI::ImageBox* image = key->createWidget("ImageBox", MyGUI::IntCoord(14, 13, 32, 32), MyGUI::Align::Default); image->setImageTexture("icons\\k\\stealth_handtohand.dds"); image->setNeedMouseFocus(false); } else { mAssigned[index] = Type_Unassigned; MyGUI::TextBox* textBox = key->createWidgetReal("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default); textBox->setTextAlign (MyGUI::Align::Center); textBox->setCaption (MyGUI::utility::toString(index+1)); textBox->setNeedMouseFocus (false); } } void QuickKeysMenu::onQuickKeyButtonClicked(MyGUI::Widget* sender) { int index = -1; for (int i = 0; i < 10; ++i) { if (sender == mQuickKeyButtons[i] || sender->getParent () == mQuickKeyButtons[i]) { index = i; break; } } assert(index != -1); mSelectedIndex = index; { // open assign dialog if (!mAssignDialog) mAssignDialog = new QuickKeysMenuAssign(this); mAssignDialog->setVisible (true); } } void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender) { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_QuickKeysMenu); } void QuickKeysMenu::onItemButtonClicked(MyGUI::Widget* sender) { if (!mItemSelectionDialog ) { mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}"); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItemCancel); } mItemSelectionDialog->setVisible(true); mItemSelectionDialog->openContainer(MWMechanics::getPlayer()); mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems); mAssignDialog->setVisible (false); } void QuickKeysMenu::onMagicButtonClicked(MyGUI::Widget* sender) { if (!mMagicSelectionDialog ) { mMagicSelectionDialog = new MagicSelectionDialog(this); } mMagicSelectionDialog->setVisible(true); mAssignDialog->setVisible (false); } void QuickKeysMenu::onUnassignButtonClicked(MyGUI::Widget* sender) { unassign(mQuickKeyButtons[mSelectedIndex], mSelectedIndex); mAssignDialog->setVisible (false); } void QuickKeysMenu::onCancelButtonClicked(MyGUI::Widget* sender) { mAssignDialog->setVisible (false); } void QuickKeysMenu::onAssignItem(MWWorld::Ptr item) { assert (mSelectedIndex >= 0); ItemWidget* button = mQuickKeyButtons[mSelectedIndex]; while (button->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0)); mAssigned[mSelectedIndex] = Type_Item; button->setItem(item, ItemWidget::Barter); button->setUserString ("ToolTipType", "ItemPtr"); button->setUserData(item); if (mItemSelectionDialog) mItemSelectionDialog->setVisible(false); } void QuickKeysMenu::onAssignItemCancel() { mItemSelectionDialog->setVisible(false); } void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) { assert (mSelectedIndex >= 0); ItemWidget* button = mQuickKeyButtons[mSelectedIndex]; while (button->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0)); mAssigned[mSelectedIndex] = Type_MagicItem; button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40)); button->setIcon(item); button->setUserString ("ToolTipType", "ItemPtr"); button->setUserData(item); if (mMagicSelectionDialog) mMagicSelectionDialog->setVisible(false); } void QuickKeysMenu::onAssignMagic (const std::string& spellId) { assert (mSelectedIndex >= 0); ItemWidget* button = mQuickKeyButtons[mSelectedIndex]; while (button->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0)); mAssigned[mSelectedIndex] = Type_Magic; button->setItem(MWWorld::Ptr()); button->setUserString ("ToolTipType", "Spell"); button->setUserString ("Spell", spellId); const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); // use the icon of the first effect const ESM::Spell* spell = esmStore.get().find(spellId); const ESM::MagicEffect* effect = esmStore.get().find(spell->mEffects.mList.front().mEffectID); std::string path = effect->mIcon; int slashPos = path.rfind('\\'); path.insert(slashPos+1, "b_"); path = MWBase::Environment::get().getWindowManager()->correctIconPath(path); button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40)); button->setIcon(path); if (mMagicSelectionDialog) mMagicSelectionDialog->setVisible(false); } void QuickKeysMenu::onAssignMagicCancel () { mMagicSelectionDialog->setVisible(false); } void QuickKeysMenu::activateQuickKey(int index) { assert (index-1 >= 0); ItemWidget* button = mQuickKeyButtons[index-1]; QuickKeyType type = mAssigned[index-1]; MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); if (type == Type_Item || type == Type_MagicItem) { MWWorld::Ptr item = *button->getUserData(); // make sure the item is available if (item.getRefData ().getCount() < 1) { // Try searching for a compatible replacement std::string id = item.getCellRef().getRefId(); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { if (Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), id)) { item = *it; button->setUserData(item); break; } } if (item.getRefData().getCount() < 1) { // No replacement was found MWBase::Environment::get().getWindowManager ()->messageBox ( "#{sQuickMenu5} " + item.getClass().getName(item)); return; } } } if (type == Type_Magic) { std::string spellId = button->getUserString("Spell"); // Make sure the player still has this spell MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); if (!spells.hasSpell(spellId)) return; store.setSelectedEnchantItem(store.end()); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell); } else if (type == Type_Item) { MWWorld::Ptr item = *button->getUserData(); MWBase::Environment::get().getWindowManager()->useItem(item); MWWorld::ContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); // change draw state only if the item is in player's right hand if (rightHand != store.end() && item == *rightHand) { MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon); } } else if (type == Type_MagicItem) { MWWorld::Ptr item = *button->getUserData(); // retrieve ContainerStoreIterator to the item MWWorld::ContainerStoreIterator it = store.begin(); for (; it != store.end(); ++it) { if (*it == item) { break; } } assert(it != store.end()); // equip, if it can be equipped if (!item.getClass().getEquipmentSlots(item).first.empty()) { MWBase::Environment::get().getWindowManager()->useItem(item); // make sure that item was successfully equipped if (!store.isEquipped(item)) return; } store.setSelectedEnchantItem(it); MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell); } else if (type == Type_HandToHand) { store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, player); MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon); } } // --------------------------------------------------------------------------------------------------------- QuickKeysMenuAssign::QuickKeysMenuAssign (QuickKeysMenu* parent) : WindowModal("openmw_quickkeys_menu_assign.layout") , mParent(parent) { getWidget(mLabel, "Label"); getWidget(mItemButton, "ItemButton"); getWidget(mMagicButton, "MagicButton"); getWidget(mUnassignButton, "UnassignButton"); getWidget(mCancelButton, "CancelButton"); mItemButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onItemButtonClicked); mMagicButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onMagicButtonClicked); mUnassignButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onUnassignButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onCancelButtonClicked); int maxWidth = mItemButton->getTextSize ().width + 24; maxWidth = std::max(maxWidth, mMagicButton->getTextSize ().width + 24); maxWidth = std::max(maxWidth, mUnassignButton->getTextSize ().width + 24); maxWidth = std::max(maxWidth, mCancelButton->getTextSize ().width + 24); mMainWidget->setSize(maxWidth + 24, mMainWidget->getHeight()); mLabel->setSize(maxWidth, mLabel->getHeight()); mItemButton->setCoord((maxWidth - mItemButton->getTextSize().width-24)/2 + 8, mItemButton->getTop(), mItemButton->getTextSize().width + 24, mItemButton->getHeight()); mMagicButton->setCoord((maxWidth - mMagicButton->getTextSize().width-24)/2 + 8, mMagicButton->getTop(), mMagicButton->getTextSize().width + 24, mMagicButton->getHeight()); mUnassignButton->setCoord((maxWidth - mUnassignButton->getTextSize().width-24)/2 + 8, mUnassignButton->getTop(), mUnassignButton->getTextSize().width + 24, mUnassignButton->getHeight()); mCancelButton->setCoord((maxWidth - mCancelButton->getTextSize().width-24)/2 + 8, mCancelButton->getTop(), mCancelButton->getTextSize().width + 24, mCancelButton->getHeight()); center(); } void QuickKeysMenuAssign::exit() { setVisible(false); } void QuickKeysMenu::write(ESM::ESMWriter &writer) { writer.startRecord(ESM::REC_KEYS); ESM::QuickKeys keys; for (int i=0; i<10; ++i) { ItemWidget* button = mQuickKeyButtons[i]; int type = mAssigned[i]; ESM::QuickKeys::QuickKey key; key.mType = type; switch (type) { case Type_Unassigned: case Type_HandToHand: break; case Type_Item: case Type_MagicItem: { MWWorld::Ptr item = *button->getUserData(); key.mId = item.getCellRef().getRefId(); break; } case Type_Magic: std::string spellId = button->getUserString("Spell"); key.mId = spellId; break; } keys.mKeys.push_back(key); } keys.save(writer); writer.endRecord(ESM::REC_KEYS); } void QuickKeysMenu::readRecord(ESM::ESMReader &reader, uint32_t type) { if (type != ESM::REC_KEYS) return; ESM::QuickKeys keys; keys.load(reader); int i=0; for (std::vector::const_iterator it = keys.mKeys.begin(); it != keys.mKeys.end(); ++it) { if (i >= 10) return; mSelectedIndex = i; int keyType = it->mType; std::string id = it->mId; ItemWidget* button = mQuickKeyButtons[i]; switch (keyType) { case Type_Magic: if (MWBase::Environment::get().getWorld()->getStore().get().search(id)) onAssignMagic(id); break; case Type_Item: case Type_MagicItem: { // Find the item by id MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); MWWorld::Ptr item; for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { if (Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), id)) { if (item.isEmpty() || // Prefer the stack with the lowest remaining uses !item.getClass().hasItemHealth(*it) || it->getClass().getItemHealth(*it) < item.getClass().getItemHealth(item)) { item = *it; } } } if (item.isEmpty()) unassign(button, i); else { if (keyType == Type_Item) onAssignItem(item); else if (keyType == Type_MagicItem) onAssignMagicItem(item); } break; } case Type_Unassigned: case Type_HandToHand: unassign(button, i); break; } ++i; } } // --------------------------------------------------------------------------------------------------------- MagicSelectionDialog::MagicSelectionDialog(QuickKeysMenu* parent) : WindowModal("openmw_magicselection_dialog.layout") , mParent(parent) { getWidget(mCancelButton, "CancelButton"); getWidget(mMagicList, "MagicList"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked); mMagicList->setShowCostColumn(false); mMagicList->setHighlightSelected(false); mMagicList->eventSpellClicked += MyGUI::newDelegate(this, &MagicSelectionDialog::onModelIndexSelected); center(); } void MagicSelectionDialog::onCancelButtonClicked (MyGUI::Widget *sender) { exit(); } void MagicSelectionDialog::exit() { mParent->onAssignMagicCancel(); } void MagicSelectionDialog::open () { WindowModal::open(); mMagicList->setModel(new SpellModel(MWMechanics::getPlayer())); mMagicList->resetScrollbars(); } void MagicSelectionDialog::onModelIndexSelected(SpellModel::ModelIndex index) { const Spell& spell = mMagicList->getModel()->getItem(index); if (spell.mType == Spell::Type_EnchantedItem) mParent->onAssignMagicItem(spell.mItem); else mParent->onAssignMagic(spell.mId); } } openmw-openmw-0.38.0/apps/openmw/mwgui/quickkeysmenu.hpp000066400000000000000000000052261264522266000234320ustar00rootroot00000000000000#ifndef MWGUI_QUICKKEYS_H #define MWGUI_QUICKKEYS_H #include "../mwworld/ptr.hpp" #include "windowbase.hpp" #include "spellmodel.hpp" namespace MWGui { class QuickKeysMenuAssign; class ItemSelectionDialog; class MagicSelectionDialog; class ItemWidget; class SpellView; class QuickKeysMenu : public WindowBase { public: QuickKeysMenu(); ~QuickKeysMenu(); virtual void exit(); void onItemButtonClicked(MyGUI::Widget* sender); void onMagicButtonClicked(MyGUI::Widget* sender); void onUnassignButtonClicked(MyGUI::Widget* sender); void onCancelButtonClicked(MyGUI::Widget* sender); void onAssignItem (MWWorld::Ptr item); void onAssignItemCancel (); void onAssignMagicItem (MWWorld::Ptr item); void onAssignMagic (const std::string& spellId); void onAssignMagicCancel (); void activateQuickKey(int index); /// @note This enum is serialized, so don't move the items around! enum QuickKeyType { Type_Item, Type_Magic, Type_MagicItem, Type_Unassigned, Type_HandToHand }; void write (ESM::ESMWriter& writer); void readRecord (ESM::ESMReader& reader, uint32_t type); void clear(); private: MyGUI::EditBox* mInstructionLabel; MyGUI::Button* mOkButton; std::vector mQuickKeyButtons; std::vector mAssigned; QuickKeysMenuAssign* mAssignDialog; ItemSelectionDialog* mItemSelectionDialog; MagicSelectionDialog* mMagicSelectionDialog; int mSelectedIndex; void onQuickKeyButtonClicked(MyGUI::Widget* sender); void onOkButtonClicked(MyGUI::Widget* sender); void unassign(ItemWidget* key, int index); }; class QuickKeysMenuAssign : public WindowModal { public: QuickKeysMenuAssign(QuickKeysMenu* parent); virtual void exit(); private: MyGUI::TextBox* mLabel; MyGUI::Button* mItemButton; MyGUI::Button* mMagicButton; MyGUI::Button* mUnassignButton; MyGUI::Button* mCancelButton; QuickKeysMenu* mParent; }; class MagicSelectionDialog : public WindowModal { public: MagicSelectionDialog(QuickKeysMenu* parent); virtual void open(); virtual void exit(); private: MyGUI::Button* mCancelButton; SpellView* mMagicList; QuickKeysMenu* mParent; void onCancelButtonClicked (MyGUI::Widget* sender); void onModelIndexSelected(SpellModel::ModelIndex index); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/race.cpp000066400000000000000000000357201264522266000214440ustar00rootroot00000000000000#include "race.hpp" #include #include #include #include #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwrender/characterpreview.hpp" #include "tooltips.hpp" namespace { int wrap(int index, int max) { if (index < 0) return max - 1; else if (index >= max) return 0; else return index; } bool sortRaces(const std::pair& left, const std::pair& right) { return left.second.compare(right.second) < 0; } } namespace MWGui { RaceDialog::RaceDialog(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem) : WindowModal("openmw_chargen_race.layout") , mViewer(viewer) , mResourceSystem(resourceSystem) , mGenderIndex(0) , mFaceIndex(0) , mHairIndex(0) , mCurrentAngle(0) , mPreviewDirty(true) { // Centre dialog center(); setText("AppearanceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu1", "Appearance")); getWidget(mPreviewImage, "PreviewImage"); getWidget(mHeadRotate, "HeadRotate"); mHeadRotate->setScrollRange(1000); mHeadRotate->setScrollPosition(500); mHeadRotate->setScrollViewPage(50); mHeadRotate->setScrollPage(50); mHeadRotate->setScrollWheelPage(50); mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); // Set up next/previous buttons MyGUI::Button *prevButton, *nextButton; setText("GenderChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu2", "Change Sex")); getWidget(prevButton, "PrevGenderButton"); getWidget(nextButton, "NextGenderButton"); prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender); setText("FaceChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu3", "Change Face")); getWidget(prevButton, "PrevFaceButton"); getWidget(nextButton, "NextFaceButton"); prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace); setText("HairChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu4", "Change Hair")); getWidget(prevButton, "PrevHairButton"); getWidget(nextButton, "NextHairButton"); prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair); setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race")); getWidget(mRaceList, "RaceList"); mRaceList->setScrollVisible(true); mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onAccept); mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); getWidget(mSkillList, "SkillList"); setText("SpellPowerT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu7", "Specials")); getWidget(mSpellPowerList, "SpellPowerList"); MyGUI::Button* backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); updateRaces(); updateSkills(); updateSpellPowers(); } void RaceDialog::setNextButtonShow(bool shown) { MyGUI::Button* okButton; getWidget(okButton, "OKButton"); if (shown) okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } void RaceDialog::open() { WindowModal::open(); updateRaces(); updateSkills(); updateSpellPowers(); mPreviewImage->setRenderItemTexture(NULL); mPreview.reset(NULL); mPreviewTexture.reset(NULL); mPreview.reset(new MWRender::RaceSelectionPreview(mViewer, mResourceSystem)); mPreview->rebuild(); mPreview->setAngle (mCurrentAngle); mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture())); mPreviewImage->setRenderItemTexture(mPreviewTexture.get()); mPreviewImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); const ESM::NPC& proto = mPreview->getPrototype(); setRaceId(proto.mRace); recountParts(); for (unsigned int i=0; igetScrollRange()/2+mHeadRotate->getScrollRange()/10; mHeadRotate->setScrollPosition(initialPos); onHeadRotate(mHeadRotate, initialPos); } void RaceDialog::setRaceId(const std::string &raceId) { mCurrentRaceId = raceId; mRaceList->setIndexSelected(MyGUI::ITEM_NONE); size_t count = mRaceList->getItemCount(); for (size_t i = 0; i < count; ++i) { if (Misc::StringUtils::ciEqual(*mRaceList->getItemDataAt(i), raceId)) { mRaceList->setIndexSelected(i); break; } } updateSkills(); updateSpellPowers(); } void RaceDialog::close() { mPreviewImage->setRenderItemTexture(NULL); mPreviewTexture.reset(NULL); mPreview.reset(NULL); } // widget controls void RaceDialog::onOkClicked(MyGUI::Widget* _sender) { if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) return; eventDone(this); } void RaceDialog::onBackClicked(MyGUI::Widget* _sender) { eventBack(); } void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position) { float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5f) * 3.14f * 2; mPreview->setAngle (angle); mCurrentAngle = angle; } void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) { mGenderIndex = wrap(mGenderIndex - 1, 2); recountParts(); updatePreview(); } void RaceDialog::onSelectNextGender(MyGUI::Widget*) { mGenderIndex = wrap(mGenderIndex + 1, 2); recountParts(); updatePreview(); } void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) { mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size()); updatePreview(); } void RaceDialog::onSelectNextFace(MyGUI::Widget*) { mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size()); updatePreview(); } void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) { mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size()); updatePreview(); } void RaceDialog::onSelectNextHair(MyGUI::Widget*) { mHairIndex = wrap(mHairIndex + 1, mAvailableHairs.size()); updatePreview(); } void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) { if (_index == MyGUI::ITEM_NONE) return; const std::string *raceId = mRaceList->getItemDataAt(_index); if (Misc::StringUtils::ciEqual(mCurrentRaceId, *raceId)) return; mCurrentRaceId = *raceId; recountParts(); updatePreview(); updateSkills(); updateSpellPowers(); } void RaceDialog::onAccept(MyGUI::ListBox *_sender, size_t _index) { onSelectRace(_sender, _index); if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) return; eventDone(this); } void RaceDialog::getBodyParts (int part, std::vector& out) { out.clear(); const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); for (MWWorld::Store::iterator it = store.begin(); it != store.end(); ++it) { const ESM::BodyPart& bodypart = *it; if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) continue; if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) continue; if (bodypart.mData.mPart != static_cast(part)) continue; if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) continue; bool firstPerson = (bodypart.mId.size() >= 3) && bodypart.mId[bodypart.mId.size()-3] == '1' && bodypart.mId[bodypart.mId.size()-2] == 's' && bodypart.mId[bodypart.mId.size()-1] == 't'; if (firstPerson) continue; if (Misc::StringUtils::ciEqual(bodypart.mRace, mCurrentRaceId)) out.push_back(bodypart.mId); } } void RaceDialog::recountParts() { getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs); getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads); mFaceIndex = 0; mHairIndex = 0; } // update widget content void RaceDialog::updatePreview() { ESM::NPC record = mPreview->getPrototype(); record.mRace = mCurrentRaceId; record.setIsMale(mGenderIndex == 0); record.mHead = mAvailableHeads[mFaceIndex]; record.mHair = mAvailableHairs[mHairIndex]; try { mPreview->setPrototype(record); } catch (std::exception& e) { std::cerr << "Error creating preview: " << e.what() << std::endl; } } void RaceDialog::updateRaces() { mRaceList->removeAllItems(); const MWWorld::Store &races = MWBase::Environment::get().getWorld()->getStore().get(); std::vector > items; // ID, name MWWorld::Store::iterator it = races.begin(); for (; it != races.end(); ++it) { bool playable = it->mData.mFlags & ESM::Race::Playable; if (!playable) // Only display playable races continue; items.push_back(std::make_pair(it->mId, it->mName)); } std::sort(items.begin(), items.end(), sortRaces); int index = 0; for (std::vector >::const_iterator it = items.begin(); it != items.end(); ++it) { mRaceList->addItem(it->second, it->first); if (Misc::StringUtils::ciEqual(it->first, mCurrentRaceId)) mRaceList->setIndexSelected(index); ++index; } } void RaceDialog::updateSkills() { for (std::vector::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } mSkillItems.clear(); if (mCurrentRaceId.empty()) return; Widgets::MWSkillPtr skillWidget; const int lineHeight = 18; MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mCurrentRaceId); int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? for (int i = 0; i < count; ++i) { int skillId = race->mData.mBonus[i].mSkill; if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes continue; skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, std::string("Skill") + MyGUI::utility::toString(i)); skillWidget->setSkillNumber(skillId); skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(static_cast(race->mData.mBonus[i].mBonus))); ToolTips::createSkillToolTip(skillWidget, skillId); mSkillItems.push_back(skillWidget); coord1.top += lineHeight; } } void RaceDialog::updateSpellPowers() { for (std::vector::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } mSpellPowerItems.clear(); if (mCurrentRaceId.empty()) return; const int lineHeight = 18; MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mCurrentRaceId); std::vector::const_iterator it = race->mPowers.mList.begin(); std::vector::const_iterator end = race->mPowers.mList.end(); for (int i = 0; it != end; ++it) { const std::string &spellpower = *it; Widgets::MWSpellPtr spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + MyGUI::utility::toString(i)); spellPowerWidget->setSpellId(spellpower); spellPowerWidget->setUserString("ToolTipType", "Spell"); spellPowerWidget->setUserString("Spell", spellpower); mSpellPowerItems.push_back(spellPowerWidget); coord.top += lineHeight; ++i; } } const ESM::NPC& RaceDialog::getResult() const { return mPreview->getPrototype(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/race.hpp000066400000000000000000000057401264522266000214500ustar00rootroot00000000000000#ifndef MWGUI_RACE_H #define MWGUI_RACE_H #include "windowbase.hpp" namespace MWGui { class WindowManager; } namespace MWRender { class RaceSelectionPreview; } namespace ESM { struct NPC; } namespace osgViewer { class Viewer; } namespace Resource { class ResourceSystem; } namespace MWGui { class RaceDialog : public WindowModal { public: RaceDialog(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem); enum Gender { GM_Male, GM_Female }; const ESM::NPC &getResult() const; const std::string &getRaceId() const { return mCurrentRaceId; } Gender getGender() const { return mGenderIndex == 0 ? GM_Male : GM_Female; } void setRaceId(const std::string &raceId); void setGender(Gender gender) { mGenderIndex = gender == GM_Male ? 0 : 1; } void setNextButtonShow(bool shown); virtual void open(); virtual void close(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n */ EventHandle_Void eventBack; /** Event : Dialog finished, OK button clicked.\n signature : void method()\n */ EventHandle_WindowBase eventDone; protected: void onHeadRotate(MyGUI::ScrollBar* _sender, size_t _position); void onSelectPreviousGender(MyGUI::Widget* _sender); void onSelectNextGender(MyGUI::Widget* _sender); void onSelectPreviousFace(MyGUI::Widget* _sender); void onSelectNextFace(MyGUI::Widget* _sender); void onSelectPreviousHair(MyGUI::Widget* _sender); void onSelectNextHair(MyGUI::Widget* _sender); void onSelectRace(MyGUI::ListBox* _sender, size_t _index); void onAccept(MyGUI::ListBox* _sender, size_t _index); void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); private: void updateRaces(); void updateSkills(); void updateSpellPowers(); void updatePreview(); void recountParts(); void getBodyParts (int part, std::vector& out); osgViewer::Viewer* mViewer; Resource::ResourceSystem* mResourceSystem; std::vector mAvailableHeads; std::vector mAvailableHairs; MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; MyGUI::ScrollBar* mHeadRotate; MyGUI::Widget* mSkillList; std::vector mSkillItems; MyGUI::Widget* mSpellPowerList; std::vector mSpellPowerItems; int mGenderIndex, mFaceIndex, mHairIndex; std::string mCurrentRaceId; float mCurrentAngle; std::auto_ptr mPreview; std::auto_ptr mPreviewTexture; bool mPreviewDirty; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/recharge.cpp000066400000000000000000000166011264522266000223070ustar00rootroot00000000000000#include "recharge.hpp" #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "widgets.hpp" #include "itemwidget.hpp" namespace MWGui { Recharge::Recharge() : WindowBase("openmw_recharge_dialog.layout") { getWidget(mBox, "Box"); getWidget(mView, "View"); getWidget(mGemBox, "GemBox"); getWidget(mGemIcon, "GemIcon"); getWidget(mChargeLabel, "ChargeLabel"); getWidget(mCancelButton, "CancelButton"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onCancel); setVisible(false); } void Recharge::open() { center(); // Reset scrollbars mView->setViewOffset(MyGUI::IntPoint(0, 0)); } void Recharge::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Recharge); } void Recharge::start (const MWWorld::Ptr &item) { mGemIcon->setItem(item); mGemIcon->setUserString("ToolTipType", "ItemPtr"); mGemIcon->setUserData(item); updateView(); } void Recharge::updateView() { MWWorld::Ptr gem = *mGemIcon->getUserData(); std::string soul = gem.getCellRef().getSoul(); const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get().find(soul); mChargeLabel->setCaptionWithReplacing("#{sCharges} " + MyGUI::utility::toString(creature->mData.mSoul)); bool toolBoxVisible = (gem.getRefData().getCount() != 0); mGemBox->setVisible(toolBoxVisible); bool toolBoxWasVisible = (mBox->getPosition().top != mGemBox->getPosition().top); if (toolBoxVisible && !toolBoxWasVisible) { // shrink mBox->setPosition(mBox->getPosition() + MyGUI::IntPoint(0, mGemBox->getSize().height)); mBox->setSize(mBox->getSize() - MyGUI::IntSize(0,mGemBox->getSize().height)); } else if (!toolBoxVisible && toolBoxWasVisible) { // expand mBox->setPosition(MyGUI::IntPoint (mBox->getPosition().left, mGemBox->getPosition().top)); mBox->setSize(mBox->getSize() + MyGUI::IntSize(0,mGemBox->getSize().height)); } while (mView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mView->getChildAt(0)); int currentY = 0; MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { std::string enchantmentName = iter->getClass().getEnchantment(*iter); if (enchantmentName.empty()) continue; const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(enchantmentName); if (iter->getCellRef().getEnchantmentCharge() >= enchantment->mData.mCharge || iter->getCellRef().getEnchantmentCharge() == -1) continue; MyGUI::TextBox* text = mView->createWidget ( "SandText", MyGUI::IntCoord(8, currentY, mView->getWidth()-8, 18), MyGUI::Align::Default); text->setCaption(iter->getClass().getName(*iter)); text->setNeedMouseFocus(false); currentY += 19; ItemWidget* icon = mView->createWidget ( "MW_ItemIconSmall", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default); icon->setItem(*iter); icon->setUserString("ToolTipType", "ItemPtr"); icon->setUserData(*iter); icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onItemClicked); icon->eventMouseWheel += MyGUI::newDelegate(this, &Recharge::onMouseWheel); Widgets::MWDynamicStatPtr chargeWidget = mView->createWidget ("MW_ChargeBar", MyGUI::IntCoord(72, currentY+2, 199, 20), MyGUI::Align::Default); chargeWidget->setValue(static_cast(iter->getCellRef().getEnchantmentCharge()), enchantment->mData.mCharge); chargeWidget->setNeedMouseFocus(false); currentY += 32 + 4; } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mView->setVisibleVScroll(false); mView->setCanvasSize (MyGUI::IntSize(mView->getWidth(), std::max(mView->getHeight(), currentY))); mView->setVisibleVScroll(true); } void Recharge::onCancel(MyGUI::Widget *sender) { exit(); } void Recharge::onItemClicked(MyGUI::Widget *sender) { MWWorld::Ptr gem = *mGemIcon->getUserData(); if (!gem.getRefData().getCount()) return; MWWorld::Ptr item = *sender->getUserData(); MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified(); if (luckTerm < 1|| luckTerm > 10) luckTerm = 1; float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); if (intelligenceTerm > 20) intelligenceTerm = 20; if (intelligenceTerm < 1) intelligenceTerm = 1; float x = (npcStats.getSkill(ESM::Skill::Enchant).getModified() + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); int roll = Misc::Rng::roll0to99(); if (roll < x) { std::string soul = gem.getCellRef().getSoul(); const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get().find(soul); float restored = creature->mData.mSoul * (roll / x); const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( item.getClass().getEnchantment(item)); item.getCellRef().setEnchantmentCharge( std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast(enchantment->mData.mCharge))); player.getClass().getContainerStore(player).restack(item); player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); } gem.getContainerStore()->remove(gem, 1, player); if (gem.getRefData().getCount() == 0) { std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage51")->getString(); message = boost::str(boost::format(message) % gem.getClass().getName(gem)); MWBase::Environment::get().getWindowManager()->messageBox(message); // special case: readd Azura's Star if (Misc::StringUtils::ciEqual(gem.get()->mBase->mId, "Misc_SoulGem_Azura")) player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player); } updateView(); } void Recharge::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (mView->getViewOffset().top + _rel*0.3f > 0) mView->setViewOffset(MyGUI::IntPoint(0, 0)); else mView->setViewOffset(MyGUI::IntPoint(0, static_cast(mView->getViewOffset().top + _rel*0.3f))); } } openmw-openmw-0.38.0/apps/openmw/mwgui/recharge.hpp000066400000000000000000000013011264522266000223030ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_RECHARGE_H #define OPENMW_MWGUI_RECHARGE_H #include "windowbase.hpp" namespace MWWorld { class Ptr; } namespace MWGui { class ItemWidget; class Recharge : public WindowBase { public: Recharge(); virtual void open(); virtual void exit(); void start (const MWWorld::Ptr& gem); protected: MyGUI::Widget* mBox; MyGUI::ScrollView* mView; MyGUI::Widget* mGemBox; ItemWidget* mGemIcon; MyGUI::TextBox* mChargeLabel; MyGUI::Button* mCancelButton; void updateView(); void onItemClicked (MyGUI::Widget* sender); void onCancel (MyGUI::Widget* sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/referenceinterface.cpp000066400000000000000000000016031264522266000243420ustar00rootroot00000000000000#include "referenceinterface.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWGui { ReferenceInterface::ReferenceInterface() : mCurrentPlayerCell(NULL) { } ReferenceInterface::~ReferenceInterface() { } void ReferenceInterface::checkReferenceAvailable() { MWWorld::CellStore* playerCell = MWMechanics::getPlayer().getCell(); // check if player has changed cell, or count of the reference has become 0 if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) || (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0)) { if (!mPtr.isEmpty()) { mPtr = MWWorld::Ptr(); onReferenceUnavailable(); } } mCurrentPlayerCell = playerCell; } } openmw-openmw-0.38.0/apps/openmw/mwgui/referenceinterface.hpp000066400000000000000000000020401264522266000243430ustar00rootroot00000000000000#ifndef MWGUI_REFERENCEINTERFACE_H #define MWGUI_REFERENCEINTERFACE_H #include "../mwworld/ptr.hpp" namespace MWGui { /// \brief this class is intended for GUI interfaces that access an MW-Reference /// for example dialogue window accesses an NPC, or Container window accesses a Container /// these classes have to be automatically closed if the reference becomes unavailable /// make sure that checkReferenceAvailable() is called every frame and that onReferenceUnavailable() has been overridden class ReferenceInterface { public: ReferenceInterface(); virtual ~ReferenceInterface(); void checkReferenceAvailable(); ///< closes the window, if the MW-reference has become unavailable virtual void resetReference() { mPtr = MWWorld::Ptr(); mCurrentPlayerCell = NULL; } protected: virtual void onReferenceUnavailable() = 0; ///< called when reference has become unavailable MWWorld::Ptr mPtr; private: MWWorld::CellStore* mCurrentPlayerCell; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/repair.cpp000066400000000000000000000125241264522266000220110ustar00rootroot00000000000000#include "repair.hpp" #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "widgets.hpp" #include "itemwidget.hpp" namespace MWGui { Repair::Repair() : WindowBase("openmw_repair.layout") { getWidget(mRepairBox, "RepairBox"); getWidget(mRepairView, "RepairView"); getWidget(mToolBox, "ToolBox"); getWidget(mToolIcon, "ToolIcon"); getWidget(mUsesLabel, "UsesLabel"); getWidget(mQualityLabel, "QualityLabel"); getWidget(mCancelButton, "CancelButton"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onCancel); } void Repair::open() { center(); // Reset scrollbars mRepairView->setViewOffset(MyGUI::IntPoint(0, 0)); } void Repair::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Repair); } void Repair::startRepairItem(const MWWorld::Ptr &item) { mRepair.setTool(item); mToolIcon->setItem(item); mToolIcon->setUserString("ToolTipType", "ItemPtr"); mToolIcon->setUserData(item); updateRepairView(); } void Repair::updateRepairView() { MWWorld::LiveCellRef *ref = mRepair.getTool().get(); int uses = mRepair.getTool().getClass().getItemHealth(mRepair.getTool()); float quality = ref->mBase->mData.mQuality; std::stringstream qualityStr; qualityStr << std::setprecision(3) << quality; mUsesLabel->setCaptionWithReplacing("#{sUses} " + MyGUI::utility::toString(uses)); mQualityLabel->setCaptionWithReplacing("#{sQuality} " + qualityStr.str()); bool toolBoxVisible = (mRepair.getTool().getRefData().getCount() != 0); mToolBox->setVisible(toolBoxVisible); bool toolBoxWasVisible = (mRepairBox->getPosition().top != mToolBox->getPosition().top); if (toolBoxVisible && !toolBoxWasVisible) { // shrink mRepairBox->setPosition(mRepairBox->getPosition() + MyGUI::IntPoint(0,mToolBox->getSize().height)); mRepairBox->setSize(mRepairBox->getSize() - MyGUI::IntSize(0,mToolBox->getSize().height)); } else if (!toolBoxVisible && toolBoxWasVisible) { // expand mRepairBox->setPosition(MyGUI::IntPoint (mRepairBox->getPosition().left, mToolBox->getPosition().top)); mRepairBox->setSize(mRepairBox->getSize() + MyGUI::IntSize(0,mToolBox->getSize().height)); } while (mRepairView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mRepairView->getChildAt(0)); int currentY = 0; MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor; for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); iter!=store.end(); ++iter) { if (iter->getClass().hasItemHealth(*iter)) { int maxDurability = iter->getClass().getItemMaxHealth(*iter); int durability = iter->getClass().getItemHealth(*iter); if (maxDurability == durability) continue; MyGUI::TextBox* text = mRepairView->createWidget ( "SandText", MyGUI::IntCoord(8, currentY, mRepairView->getWidth()-8, 18), MyGUI::Align::Default); text->setCaption(iter->getClass().getName(*iter)); text->setNeedMouseFocus(false); currentY += 19; ItemWidget* icon = mRepairView->createWidget ( "MW_ItemIconSmall", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default); icon->setItem(*iter); icon->setUserString("ToolTipType", "ItemPtr"); icon->setUserData(*iter); icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onRepairItem); icon->eventMouseWheel += MyGUI::newDelegate(this, &Repair::onMouseWheel); Widgets::MWDynamicStatPtr chargeWidget = mRepairView->createWidget ("MW_ChargeBar", MyGUI::IntCoord(72, currentY+2, 199, 20), MyGUI::Align::Default); chargeWidget->setValue(durability, maxDurability); chargeWidget->setNeedMouseFocus(false); currentY += 32 + 4; } } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mRepairView->setVisibleVScroll(false); mRepairView->setCanvasSize (MyGUI::IntSize(mRepairView->getWidth(), std::max(mRepairView->getHeight(), currentY))); mRepairView->setVisibleVScroll(true); } void Repair::onCancel(MyGUI::Widget *sender) { exit(); } void Repair::onRepairItem(MyGUI::Widget *sender) { if (!mRepair.getTool().getRefData().getCount()) return; mRepair.repair(*sender->getUserData()); updateRepairView(); } void Repair::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (mRepairView->getViewOffset().top + _rel*0.3f > 0) mRepairView->setViewOffset(MyGUI::IntPoint(0, 0)); else mRepairView->setViewOffset(MyGUI::IntPoint(0, static_cast(mRepairView->getViewOffset().top + _rel*0.3f))); } } openmw-openmw-0.38.0/apps/openmw/mwgui/repair.hpp000066400000000000000000000014321264522266000220120ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_REPAIR_H #define OPENMW_MWGUI_REPAIR_H #include "windowbase.hpp" #include "../mwmechanics/repair.hpp" namespace MWGui { class ItemWidget; class Repair : public WindowBase { public: Repair(); virtual void open(); virtual void exit(); void startRepairItem (const MWWorld::Ptr& item); protected: MyGUI::Widget* mRepairBox; MyGUI::ScrollView* mRepairView; MyGUI::Widget* mToolBox; ItemWidget* mToolIcon; MyGUI::TextBox* mUsesLabel; MyGUI::TextBox* mQualityLabel; MyGUI::Button* mCancelButton; MWMechanics::Repair mRepair; void updateRepairView(); void onRepairItem (MyGUI::Widget* sender); void onCancel (MyGUI::Widget* sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/review.cpp000066400000000000000000000345551264522266000220400ustar00rootroot00000000000000#include "review.hpp" #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/esmstore.hpp" #include "tooltips.hpp" namespace { void adjustButtonSize(MyGUI::Button *button) { // adjust size of button to fit its text MyGUI::IntSize size = button->getTextSize(); button->setSize(size.width + 24, button->getSize().height); } } namespace MWGui { const int ReviewDialog::sLineHeight = 18; ReviewDialog::ReviewDialog() : WindowModal("openmw_chargen_review.layout") { // Centre dialog center(); // Setup static stats MyGUI::Button* button; getWidget(mNameWidget, "NameText"); getWidget(button, "NameButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked); getWidget(mRaceWidget, "RaceText"); getWidget(button, "RaceButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked); getWidget(mClassWidget, "ClassText"); getWidget(button, "ClassButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked); getWidget(mBirthSignWidget, "SignText"); getWidget(button, "SignButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked); // Setup dynamic stats getWidget(mHealth, "Health"); mHealth->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sHealth", "")); mHealth->setValue(45, 45); getWidget(mMagicka, "Magicka"); mMagicka->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sMagic", "")); mMagicka->setValue(50, 50); getWidget(mFatigue, "Fatigue"); mFatigue->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFatigue", "")); mFatigue->setValue(160, 160); // Setup attributes Widgets::MWAttributePtr attribute; for (int idx = 0; idx < ESM::Attribute::Length; ++idx) { getWidget(attribute, std::string("Attribute") + MyGUI::utility::toString(idx)); mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue()); } // Setup skills getWidget(mSkillView, "SkillView"); mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); for (int i = 0; i < ESM::Skill::Length; ++i) { mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue())); mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); } MyGUI::Button* backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); } void ReviewDialog::open() { WindowModal::open(); updateSkillArea(); } void ReviewDialog::setPlayerName(const std::string &name) { mNameWidget->setCaption(name); } void ReviewDialog::setRace(const std::string &raceId) { mRaceId = raceId; const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get().search(mRaceId); if (race) { ToolTips::createRaceToolTip(mRaceWidget, race); mRaceWidget->setCaption(race->mName); } } void ReviewDialog::setClass(const ESM::Class& class_) { mKlass = class_; mClassWidget->setCaption(mKlass.mName); ToolTips::createClassToolTip(mClassWidget, mKlass); } void ReviewDialog::setBirthSign(const std::string& signId) { mBirthSignId = signId; const ESM::BirthSign *sign = MWBase::Environment::get().getWorld()->getStore().get().search(mBirthSignId); if (sign) { mBirthSignWidget->setCaption(sign->mName); ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); } } void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) { mHealth->setValue(static_cast(value.getCurrent()), static_cast(value.getModified())); std::string valStr = MyGUI::utility::toString(value.getCurrent()) + "/" + MyGUI::utility::toString(value.getModified()); mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); } void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) { mMagicka->setValue(static_cast(value.getCurrent()), static_cast(value.getModified())); std::string valStr = MyGUI::utility::toString(value.getCurrent()) + "/" + MyGUI::utility::toString(value.getModified()); mMagicka->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr); } void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) { mFatigue->setValue(static_cast(value.getCurrent()), static_cast(value.getModified())); std::string valStr = MyGUI::utility::toString(value.getCurrent()) + "/" + MyGUI::utility::toString(value.getModified()); mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); } void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value) { std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); if (attr == mAttributeWidgets.end()) return; attr->second->setAttributeValue(value); } void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value) { mSkillValues[skillId] = value; MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; if (widget) { float modified = static_cast(value.getModified()), base = static_cast(value.getBase()); std::string text = MyGUI::utility::toString(std::floor(modified)); std::string state = "normal"; if (modified > base) state = "increased"; else if (modified < base) state = "decreased"; widget->setCaption(text); widget->_setWidgetState(state); } } void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) { mMajorSkills = major; mMinorSkills = minor; // Update misc skills with the remaining skills not in major or minor std::set skillSet; std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); mMiscSkills.clear(); for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) { int skill = *it; if (skillSet.find(skill) == skillSet.end()) mMiscSkills.push_back(skill); } updateSkillArea(); } void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); mSkillWidgets.push_back(separator); coord1.top += separator->getHeight(); coord2.top += separator->getHeight(); } void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); groupWidget->setCaption(label); mSkillWidgets.push_back(groupWidget); coord1.top += sLineHeight; coord2.top += sLineHeight; } MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { MyGUI::TextBox* skillNameWidget; MyGUI::TextBox* skillValueWidget; skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); skillValueWidget->setCaption(value); skillValueWidget->_setWidgetState(state); skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillValueWidget); coord1.top += sLineHeight; coord2.top += sLineHeight; return skillValueWidget; } void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { MyGUI::TextBox* skillNameWidget; skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); mSkillWidgets.push_back(skillNameWidget); coord1.top += sLineHeight; coord2.top += sLineHeight; } void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { // Add a line separator if there are items above if (!mSkillWidgets.empty()) { addSeparator(coord1, coord2); } addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); SkillList::const_iterator end = skills.end(); for (SkillList::const_iterator it = skills.begin(); it != end; ++it) { int skillId = *it; if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes continue; assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; int base = stat.getBase(); int modified = stat.getModified(); std::string state = "normal"; if (modified > base) state = "increased"; else if (modified < base) state = "decreased"; MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), MyGUI::utility::toString(static_cast(modified)), state, coord1, coord2); for (int i=0; i<2; ++i) { ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId); } mSkillWidgetMap[skillId] = widget; } } void ReviewDialog::updateSkillArea() { for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } mSkillWidgets.clear(); const int valueSize = 40; MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); if (!mMajorSkills.empty()) addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); if (!mMinorSkills.empty()) addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); if (!mMiscSkills.empty()) addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); mSkillView->setVisibleVScroll(true); } // widget controls void ReviewDialog::onOkClicked(MyGUI::Widget* _sender) { eventDone(this); } void ReviewDialog::onBackClicked(MyGUI::Widget* _sender) { eventBack(); } void ReviewDialog::onNameClicked(MyGUI::Widget* _sender) { eventActivateDialog(NAME_DIALOG); } void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender) { eventActivateDialog(RACE_DIALOG); } void ReviewDialog::onClassClicked(MyGUI::Widget* _sender) { eventActivateDialog(CLASS_DIALOG); } void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender) { eventActivateDialog(BIRTHSIGN_DIALOG); } void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (mSkillView->getViewOffset().top + _rel*0.3 > 0) mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); else mSkillView->setViewOffset(MyGUI::IntPoint(0, static_cast(mSkillView->getViewOffset().top + _rel*0.3))); } } openmw-openmw-0.38.0/apps/openmw/mwgui/review.hpp000066400000000000000000000065271264522266000220430ustar00rootroot00000000000000#ifndef MWGUI_REVIEW_H #define MWGUI_REVIEW_H #include #include #include "windowbase.hpp" #include "widgets.hpp" namespace MWGui { class WindowManager; } namespace MWGui { class ReviewDialog : public WindowModal { public: enum Dialogs { NAME_DIALOG, RACE_DIALOG, CLASS_DIALOG, BIRTHSIGN_DIALOG }; typedef std::vector SkillList; ReviewDialog(); void setPlayerName(const std::string &name); void setRace(const std::string &raceId); void setClass(const ESM::Class& class_); void setBirthSign (const std::string &signId); void setHealth(const MWMechanics::DynamicStat& value); void setMagicka(const MWMechanics::DynamicStat& value); void setFatigue(const MWMechanics::DynamicStat& value); void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value); void configureSkills(const SkillList& major, const SkillList& minor); void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value); virtual void open(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; /** Event : Back button clicked.\n signature : void method()\n */ EventHandle_Void eventBack; /** Event : Dialog finished, OK button clicked.\n signature : void method()\n */ EventHandle_WindowBase eventDone; EventHandle_Int eventActivateDialog; protected: void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); void onNameClicked(MyGUI::Widget* _sender); void onRaceClicked(MyGUI::Widget* _sender); void onClassClicked(MyGUI::Widget* _sender); void onBirthSignClicked(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); private: void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); MyGUI::TextBox* addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void updateSkillArea(); static const int sLineHeight; MyGUI::TextBox *mNameWidget, *mRaceWidget, *mClassWidget, *mBirthSignWidget; MyGUI::ScrollView* mSkillView; Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue; std::map mAttributeWidgets; SkillList mMajorSkills, mMinorSkills, mMiscSkills; std::map mSkillValues; std::map mSkillWidgetMap; std::string mName, mRaceId, mBirthSignId; ESM::Class mKlass; std::vector mSkillWidgets; //< Skills and other information }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/savegamedialog.cpp000066400000000000000000000366031264522266000235030ustar00rootroot00000000000000#include "savegamedialog.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/statemanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwstate/character.hpp" #include "confirmationdialog.hpp" #include "widgets.hpp" namespace MWGui { SaveGameDialog::SaveGameDialog() : WindowModal("openmw_savegame_dialog.layout") , mSaving(true) , mCurrentCharacter(NULL) , mCurrentSlot(NULL) { getWidget(mScreenshot, "Screenshot"); getWidget(mCharacterSelection, "SelectCharacter"); getWidget(mInfoText, "InfoText"); getWidget(mOkButton, "OkButton"); getWidget(mCancelButton, "CancelButton"); getWidget(mDeleteButton, "DeleteButton"); getWidget(mSaveList, "SaveList"); getWidget(mSaveNameEdit, "SaveNameEdit"); getWidget(mSpacer, "Spacer"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked); mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); mSaveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &SaveGameDialog::onKeyButtonPressed); mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept); mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged); } void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos) { onSlotSelected(sender, pos); accept(); } void SaveGameDialog::onSlotMouseClick(MyGUI::ListBox* sender, size_t pos) { onSlotSelected(sender, pos); if (pos != MyGUI::ITEM_NONE && MyGUI::InputManager::getInstance().isShiftPressed()) confirmDeleteSave(); } void SaveGameDialog::confirmDeleteSave() { ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->askForConfirmation("#{sMessage3}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteSlotConfirmed); dialog->eventCancelClicked.clear(); } void SaveGameDialog::onDeleteSlotConfirmed() { MWBase::Environment::get().getStateManager()->deleteGame (mCurrentCharacter, mCurrentSlot); mSaveList->removeItemAt(mSaveList->getIndexSelected()); onSlotSelected(mSaveList, MyGUI::ITEM_NONE); // The character might be deleted now size_t previousIndex = mCharacterSelection->getIndexSelected(); open(); if (mCharacterSelection->getItemCount()) { size_t nextCharacter = std::min(previousIndex, mCharacterSelection->getItemCount()-1); mCharacterSelection->setIndexSelected(nextCharacter); onCharacterSelected(mCharacterSelection, nextCharacter); } } void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender) { // This might have previously been a save slot from the list. If so, that is no longer the case mSaveList->setIndexSelected(MyGUI::ITEM_NONE); onSlotSelected(mSaveList, MyGUI::ITEM_NONE); } void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender) { accept(); } void SaveGameDialog::open() { WindowModal::open(); mSaveNameEdit->setCaption (""); if (mSaving) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit); center(); mCharacterSelection->setCaption(""); mCharacterSelection->removeAllItems(); mCurrentCharacter = NULL; mCurrentSlot = NULL; mSaveList->removeAllItems(); onSlotSelected(mSaveList, MyGUI::ITEM_NONE); MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); if (mgr->characterBegin() == mgr->characterEnd()) return; mCurrentCharacter = mgr->getCurrentCharacter (false); std::string directory = Misc::StringUtils::lowerCase (Settings::Manager::getString ("character", "Saves")); size_t selectedIndex = MyGUI::ITEM_NONE; for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) { if (it->begin()!=it->end()) { std::stringstream title; title << it->getSignature().mPlayerName; // For a custom class, we will not find it in the store (unless we loaded the savegame first). // Fall back to name stored in savegame header in that case. std::string className; if (it->getSignature().mPlayerClassId.empty()) className = it->getSignature().mPlayerClassName; else { // Find the localised name for this class from the store const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get().search( it->getSignature().mPlayerClassId); if (class_) className = class_->mName; else className = "?"; // From an older savegame format that did not support custom classes properly. } title << " (Level " << it->getSignature().mPlayerLevel << " " << className << ")"; mCharacterSelection->addItem (title.str()); if (mCurrentCharacter == &*it || (!mCurrentCharacter && !mSaving && directory==Misc::StringUtils::lowerCase ( it->begin()->mPath.parent_path().filename().string()))) { mCurrentCharacter = &*it; selectedIndex = mCharacterSelection->getItemCount()-1; } } } mCharacterSelection->setIndexSelected(selectedIndex); if (selectedIndex == MyGUI::ITEM_NONE) mCharacterSelection->setCaption("Select Character ..."); fillSaveList(); } void SaveGameDialog::exit() { setVisible(false); } void SaveGameDialog::setLoadOrSave(bool load) { mSaving = !load; mSaveNameEdit->setVisible(!load); mCharacterSelection->setUserString("Hidden", load ? "false" : "true"); mCharacterSelection->setVisible(load); mSpacer->setUserString("Hidden", load ? "false" : "true"); mDeleteButton->setUserString("Hidden", load ? "false" : "true"); mDeleteButton->setVisible(load); if (!load) { mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter (false); } center(); } void SaveGameDialog::onCancelButtonClicked(MyGUI::Widget *sender) { exit(); } void SaveGameDialog::onDeleteButtonClicked(MyGUI::Widget *sender) { if (mCurrentSlot) confirmDeleteSave(); } void SaveGameDialog::onConfirmationGiven() { accept(true); } void SaveGameDialog::accept(bool reallySure) { // Remove for MyGUI 3.2.2 MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); if (mSaving) { // If overwriting an existing slot, ask for confirmation first if (mCurrentSlot != NULL && !reallySure) { ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->askForConfirmation("#{sMessage4}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationGiven); dialog->eventCancelClicked.clear(); return; } if (mSaveNameEdit->getCaption().empty()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}"); return; } } setVisible(false); MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_MainMenu); if (mSaving) { MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), mCurrentSlot); } else { assert (mCurrentCharacter && mCurrentSlot); MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot->mPath.string()); } } void SaveGameDialog::onKeyButtonPressed(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character) { if (key == MyGUI::KeyCode::Delete && mCurrentSlot) confirmDeleteSave(); } void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) { accept(); } void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos) { MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); unsigned int i=0; const MWState::Character* character = NULL; for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it, ++i) { if (i == pos) character = &*it; } assert(character && "Can't find selected character"); mCurrentCharacter = character; mCurrentSlot = NULL; fillSaveList(); } void SaveGameDialog::fillSaveList() { mSaveList->removeAllItems(); if (!mCurrentCharacter) return; for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) { mSaveList->addItem(it->mProfile.mDescription); } // When loading, Auto-select the first save, if there is one if (mSaveList->getItemCount() && !mSaving) { mSaveList->setIndexSelected(0); onSlotSelected(mSaveList, 0); // Give key focus to save list so we can confirm the selection with Enter MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); } else onSlotSelected(mSaveList, MyGUI::ITEM_NONE); } std::string formatTimeplayed(const double timeInSeconds) { int timePlayed = (int)floor(timeInSeconds); int days = timePlayed / 60 / 60 / 24; int hours = (timePlayed / 60 / 60) % 24; int minutes = (timePlayed / 60) % 60; int seconds = timePlayed % 60; std::stringstream stream; stream << std::setfill('0') << std::setw(2) << days << ":"; stream << std::setfill('0') << std::setw(2) << hours << ":"; stream << std::setfill('0') << std::setw(2) << minutes << ":"; stream << std::setfill('0') << std::setw(2) << seconds; return stream.str(); } void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos) { mOkButton->setEnabled(pos != MyGUI::ITEM_NONE || mSaving); mDeleteButton->setEnabled(pos != MyGUI::ITEM_NONE); if (pos == MyGUI::ITEM_NONE || !mCurrentCharacter) { mCurrentSlot = NULL; mInfoText->setCaption(""); mScreenshot->setImageTexture(""); return; } if (mSaving) mSaveNameEdit->setCaption(sender->getItemNameAt(pos)); mCurrentSlot = NULL; unsigned int i=0; for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i) { if (i == pos) mCurrentSlot = &*it; } if (!mCurrentSlot) throw std::runtime_error("Can't find selected slot"); std::stringstream text; time_t time = mCurrentSlot->mTimeStamp; struct tm* timeinfo; timeinfo = localtime(&time); // Use system/environment locale settings for datetime formatting char* oldLctime = setlocale(LC_TIME, NULL); setlocale(LC_TIME, ""); const int size=1024; char buffer[size]; if (std::strftime(buffer, size, "%x %X", timeinfo) > 0) text << buffer << "\n"; // reset setlocale(LC_TIME, oldLctime); text << "#{sLevel} " << mCurrentSlot->mProfile.mPlayerLevel << "\n"; text << "#{sCell=" << mCurrentSlot->mProfile.mPlayerCell << "}\n"; int hour = int(mCurrentSlot->mProfile.mInGameTime.mGameHour); bool pm = hour >= 12; if (hour >= 13) hour -= 12; if (hour == 0) hour = 12; text << mCurrentSlot->mProfile.mInGameTime.mDay << " " << MWBase::Environment::get().getWorld()->getMonthName(mCurrentSlot->mProfile.mInGameTime.mMonth) << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); if (Settings::Manager::getBool("timeplayed","Saves")) { text << "\n" << "Time played: " << formatTimeplayed(mCurrentSlot->mProfile.mTimePlayed); } mInfoText->setCaptionWithReplacing(text.str()); // Decode screenshot const std::vector& data = mCurrentSlot->mProfile.mScreenshot; Files::IMemStream instream (&data[0], data.size()); osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg"); if (!readerwriter) { std::cerr << "Can't open savegame screenshot, no jpg readerwriter found" << std::endl; return; } osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(instream); if (!result.success()) { std::cerr << "Failed to read savegame screenshot: " << result.message() << " code " << result.status() << std::endl; return; } osg::ref_ptr texture (new osg::Texture2D); texture->setImage(result.getImage()); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture->setResizeNonPowerOfTwoHint(false); texture->setUnRefImageDataAfterApply(true); mScreenshotTexture.reset(new osgMyGUI::OSGTexture(texture)); mScreenshot->setRenderItemTexture(mScreenshotTexture.get()); mScreenshot->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); } } openmw-openmw-0.38.0/apps/openmw/mwgui/savegamedialog.hpp000066400000000000000000000036001264522266000234770ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_SAVEGAMEDIALOG_H #define OPENMW_MWGUI_SAVEGAMEDIALOG_H #include "windowbase.hpp" namespace MWState { class Character; struct Slot; } namespace MWGui { class SaveGameDialog : public MWGui::WindowModal { public: SaveGameDialog(); virtual void open(); virtual void exit(); void setLoadOrSave(bool load); private: void confirmDeleteSave(); void onKeyButtonPressed(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character); void onCancelButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender); void onDeleteButtonClicked (MyGUI::Widget* sender); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); // Slot selected (mouse click or arrow keys) void onSlotSelected (MyGUI::ListBox* sender, size_t pos); // Slot activated (double click or enter key) void onSlotActivated (MyGUI::ListBox* sender, size_t pos); // Slot clicked with mouse void onSlotMouseClick(MyGUI::ListBox* sender, size_t pos); void onDeleteSlotConfirmed(); void onEditSelectAccept (MyGUI::EditBox* sender); void onSaveNameChanged (MyGUI::EditBox* sender); void onConfirmationGiven(); void accept(bool reallySure=false); void fillSaveList(); std::auto_ptr mScreenshotTexture; MyGUI::ImageBox* mScreenshot; bool mSaving; MyGUI::ComboBox* mCharacterSelection; MyGUI::EditBox* mInfoText; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; MyGUI::Button* mDeleteButton; MyGUI::ListBox* mSaveList; MyGUI::EditBox* mSaveNameEdit; MyGUI::Widget* mSpacer; const MWState::Character* mCurrentCharacter; const MWState::Slot* mCurrentSlot; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/screenfader.cpp000066400000000000000000000104361264522266000230100ustar00rootroot00000000000000#include "screenfader.hpp" #include #include namespace MWGui { FadeOp::FadeOp(ScreenFader * fader, float time, float targetAlpha) : mFader(fader), mRemainingTime(time), mTargetTime(time), mTargetAlpha(targetAlpha), mStartAlpha(0.f), mRunning(false) { } bool FadeOp::isRunning() { return mRunning; } void FadeOp::start() { if (mRunning) return; mRemainingTime = mTargetTime; mStartAlpha = mFader->getCurrentAlpha(); mRunning = true; } void FadeOp::update(float dt) { if (!mRunning) return; if (mRemainingTime <= 0 || mStartAlpha == mTargetAlpha) { finish(); return; } float currentAlpha = mFader->getCurrentAlpha(); if (mStartAlpha > mTargetAlpha) { currentAlpha -= dt/mTargetTime * (mStartAlpha-mTargetAlpha); if (currentAlpha < mTargetAlpha) currentAlpha = mTargetAlpha; } else { currentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha); if (currentAlpha > mTargetAlpha) currentAlpha = mTargetAlpha; } mFader->notifyAlphaChanged(currentAlpha); mRemainingTime -= dt; } void FadeOp::finish() { mRunning = false; mFader->notifyOperationFinished(); } ScreenFader::ScreenFader(const std::string & texturePath, const std::string& layout, const MyGUI::FloatCoord& texCoordOverride) : WindowBase(layout) , mCurrentAlpha(0.f) , mFactor(1.f) , mRepeat(false) { mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); setVisible(false); MyGUI::ImageBox* imageBox = mMainWidget->castType(false); if (imageBox) { imageBox->setImageTexture(texturePath); const MyGUI::IntSize imageSize = imageBox->getImageSize(); imageBox->setImageCoord(MyGUI::IntCoord(texCoordOverride.left * imageSize.width, texCoordOverride.top * imageSize.height, texCoordOverride.width * imageSize.width, texCoordOverride.height * imageSize.height)); } } void ScreenFader::update(float dt) { if (!mQueue.empty()) { if (!mQueue.front()->isRunning()) mQueue.front()->start(); mQueue.front()->update(dt); } } void ScreenFader::applyAlpha() { setVisible(true); mMainWidget->setAlpha(1.f-((1.f-mCurrentAlpha) * mFactor)); } void ScreenFader::fadeIn(float time) { queue(time, 1.f); } void ScreenFader::fadeOut(const float time) { queue(time, 0.f); } void ScreenFader::fadeTo(const int percent, const float time) { queue(time, percent/100.f); } void ScreenFader::setFactor(float factor) { mFactor = factor; applyAlpha(); } void ScreenFader::setRepeat(bool repeat) { mRepeat = repeat; } void ScreenFader::queue(float time, float targetAlpha) { if (time < 0.f) return; if (time == 0.f) { mCurrentAlpha = targetAlpha; applyAlpha(); return; } mQueue.push_back(FadeOp::Ptr(new FadeOp(this, time, targetAlpha))); } bool ScreenFader::isEmpty() { return mQueue.empty(); } void ScreenFader::clearQueue() { mQueue.clear(); } void ScreenFader::notifyAlphaChanged(float alpha) { if (mCurrentAlpha == alpha) return; mCurrentAlpha = alpha; if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f) mMainWidget->setVisible(false); else applyAlpha(); } void ScreenFader::notifyOperationFinished() { FadeOp::Ptr op = mQueue.front(); mQueue.pop_front(); if (mRepeat) mQueue.push_back(op); } float ScreenFader::getCurrentAlpha() { return mCurrentAlpha; } } openmw-openmw-0.38.0/apps/openmw/mwgui/screenfader.hpp000066400000000000000000000030431264522266000230110ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_SCREENFADER_H #define OPENMW_MWGUI_SCREENFADER_H #include #include #include "windowbase.hpp" namespace MWGui { class ScreenFader; class FadeOp { public: typedef boost::shared_ptr Ptr; FadeOp(ScreenFader * fader, float time, float targetAlpha); bool isRunning(); void start(); void update(float dt); void finish(); private: ScreenFader * mFader; float mRemainingTime; float mTargetTime; float mTargetAlpha; float mStartAlpha; bool mRunning; }; class ScreenFader : public WindowBase { public: ScreenFader(const std::string & texturePath, const std::string& layout = "openmw_screen_fader.layout", const MyGUI::FloatCoord& texCoordOverride = MyGUI::FloatCoord(0,0,1,1)); void update(float dt); void fadeIn(const float time); void fadeOut(const float time); void fadeTo(const int percent, const float time); void setFactor (float factor); void setRepeat(bool repeat); void queue(float time, float targetAlpha); bool isEmpty(); void clearQueue(); void notifyAlphaChanged(float alpha); void notifyOperationFinished(); float getCurrentAlpha(); private: void applyAlpha(); float mCurrentAlpha; float mFactor; bool mRepeat; // repeat queued operations without removing them std::deque mQueue; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/scrollwindow.cpp000066400000000000000000000066031264522266000232560ustar00rootroot00000000000000#include "scrollwindow.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/actiontake.hpp" #include "formatting.hpp" namespace { void adjustButton (Gui::ImageButton* button) { MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); button->setSize(button->getRequestedSize()); if (button->getAlign().isRight()) button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); } } namespace MWGui { ScrollWindow::ScrollWindow () : WindowBase("openmw_scroll.layout") , mTakeButtonShow(true) , mTakeButtonAllowed(true) { getWidget(mTextView, "TextView"); getWidget(mCloseButton, "CloseButton"); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); getWidget(mTakeButton, "TakeButton"); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); adjustButton(mCloseButton); adjustButton(mTakeButton); center(); } void ScrollWindow::openScroll (MWWorld::Ptr scroll, bool showTakeButton) { // no 3d sounds because the object could be in a container. MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); mScroll = scroll; MWWorld::LiveCellRef *ref = mScroll.get(); Formatting::BookFormatter formatter; formatter.markupToWidget(mTextView, ref->mBase->mText, 390, mTextView->getHeight()); MyGUI::IntSize size = mTextView->getChildAt(0)->getSize(); // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mTextView->setVisibleVScroll(false); if (size.height > mTextView->getSize().height) mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); else mTextView->setCanvasSize(410, mTextView->getSize().height); mTextView->setVisibleVScroll(true); mTextView->setViewOffset(MyGUI::IntPoint(0,0)); setTakeButtonShow(showTakeButton); } void ScrollWindow::exit() { MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); } void ScrollWindow::setTakeButtonShow(bool show) { mTakeButtonShow = show; mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); } void ScrollWindow::setInventoryAllowed(bool allowed) { mTakeButtonAllowed = allowed; mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); } void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) { exit(); } void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) { MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0); MWWorld::ActionTake take(mScroll); take.execute (MWMechanics::getPlayer()); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); } } openmw-openmw-0.38.0/apps/openmw/mwgui/scrollwindow.hpp000066400000000000000000000016001264522266000232530ustar00rootroot00000000000000#ifndef MWGUI_SCROLLWINDOW_H #define MWGUI_SCROLLWINDOW_H #include "windowbase.hpp" #include "../mwworld/ptr.hpp" namespace Gui { class ImageButton; } namespace MWGui { class ScrollWindow : public WindowBase { public: ScrollWindow (); void openScroll (MWWorld::Ptr scroll, bool showTakeButton); virtual void exit(); void setInventoryAllowed(bool allowed); protected: void onCloseButtonClicked (MyGUI::Widget* _sender); void onTakeButtonClicked (MyGUI::Widget* _sender); void setTakeButtonShow(bool show); private: Gui::ImageButton* mCloseButton; Gui::ImageButton* mTakeButton; MyGUI::ScrollView* mTextView; MWWorld::Ptr mScroll; bool mTakeButtonShow; bool mTakeButtonAllowed; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/settingswindow.cpp000066400000000000000000000565231264522266000236260ustar00rootroot00000000000000#include "settingswindow.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "confirmationdialog.hpp" namespace { std::string fpsLevelToStr(int level) { if (level == 0) return "#{sOff}"; else //if (level == 1) return "#{sOn}"; } std::string textureMipmappingToStr(const std::string& val) { if (val == "linear") return "Trilinear"; if (val == "nearest") return "Bilinear"; if (val != "none") std::cerr<< "Invalid texture mipmap option: "< split; boost::algorithm::split (split, str, boost::is_any_of("@(x")); assert (split.size() >= 2); boost::trim(split[0]); boost::trim(split[1]); x = MyGUI::utility::parseInt (split[0]); y = MyGUI::utility::parseInt (split[1]); } bool sortResolutions (std::pair left, std::pair right) { if (left.first == right.first) return left.second > right.second; return left.first > right.first; } std::string getAspect (int x, int y) { int gcd = boost::math::gcd (x, y); int xaspect = x / gcd; int yaspect = y / gcd; // special case: 8 : 5 is usually referred to as 16:10 if (xaspect == 8 && yaspect == 5) return "16 : 10"; return MyGUI::utility::toString(xaspect) + " : " + MyGUI::utility::toString(yaspect); } const char* checkButtonType = "CheckButton"; const char* sliderType = "Slider"; std::string getSettingType(MyGUI::Widget* widget) { return widget->getUserString("SettingType"); } std::string getSettingName(MyGUI::Widget* widget) { return widget->getUserString("SettingName"); } std::string getSettingCategory(MyGUI::Widget* widget) { return widget->getUserString("SettingCategory"); } std::string getSettingValueType(MyGUI::Widget* widget) { return widget->getUserString("SettingValueType"); } void getSettingMinMax(MyGUI::Widget* widget, float& min, float& max) { const char* settingMin = "SettingMin"; const char* settingMax = "SettingMax"; min = 0.f; max = 1.f; if (!widget->getUserString(settingMin).empty()) min = MyGUI::utility::parseFloat(widget->getUserString(settingMin)); if (!widget->getUserString(settingMax).empty()) max = MyGUI::utility::parseFloat(widget->getUserString(settingMax)); } } namespace MWGui { void SettingsWindow::configureWidgets(MyGUI::Widget* widget) { MyGUI::EnumeratorWidgetPtr widgets = widget->getEnumerator(); while (widgets.next()) { MyGUI::Widget* current = widgets.current(); std::string type = getSettingType(current); if (type == checkButtonType) { std::string initialValue = Settings::Manager::getBool(getSettingName(current), getSettingCategory(current)) ? "#{sOn}" : "#{sOff}"; current->castType()->setCaptionWithReplacing(initialValue); current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); } if (type == sliderType) { MyGUI::ScrollBar* scroll = current->castType(); if (getSettingValueType(current) == "Float") { // TODO: ScrollBar isn't meant for this. should probably use a dedicated FloatSlider widget float min,max; getSettingMinMax(scroll, min, max); float value = Settings::Manager::getFloat(getSettingName(current), getSettingCategory(current)); value = std::max(min, std::min(value, max)); value = (value-min)/(max-min); scroll->setScrollPosition(static_cast(value * (scroll->getScrollRange() - 1))); } else { int value = Settings::Manager::getInt(getSettingName(current), getSettingCategory(current)); scroll->setScrollPosition(value); } scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); } configureWidgets(current); } } SettingsWindow::SettingsWindow() : WindowBase("openmw_settings_window.layout"), mKeyboardMode(true) { configureWidgets(mMainWidget); setTitle("#{sOptions}"); getWidget(mSettingsTab, "SettingsTab"); getWidget(mOkButton, "OkButton"); getWidget(mResolutionList, "ResolutionList"); getWidget(mFullscreenButton, "FullscreenButton"); getWidget(mVSyncButton, "VSyncButton"); getWidget(mWindowBorderButton, "WindowBorderButton"); getWidget(mFOVSlider, "FOVSlider"); getWidget(mAnisotropySlider, "AnisotropySlider"); getWidget(mTextureFilteringButton, "TextureFilteringButton"); getWidget(mAnisotropyLabel, "AnisotropyLabel"); getWidget(mAnisotropyBox, "AnisotropyBox"); getWidget(mShadersButton, "ShadersButton"); getWidget(mShadowsEnabledButton, "ShadowsEnabledButton"); getWidget(mShadowsTextureSize, "ShadowsTextureSize"); getWidget(mControlsBox, "ControlsBox"); getWidget(mResetControlsButton, "ResetControlsButton"); getWidget(mDifficultySlider, "DifficultySlider"); getWidget(mKeyboardSwitch, "KeyboardButton"); getWidget(mControllerSwitch, "ControllerButton"); getWidget(mWaterTextureSize, "WaterTextureSize"); #ifndef WIN32 // hide gamma controls since it currently does not work under Linux MyGUI::ScrollBar *gammaSlider; getWidget(gammaSlider, "GammaSlider"); gammaSlider->setVisible(false); MyGUI::TextBox *textBox; getWidget(textBox, "GammaText"); textBox->setVisible(false); getWidget(textBox, "GammaTextDark"); textBox->setVisible(false); getWidget(textBox, "GammaTextLight"); textBox->setVisible(false); #endif mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &SettingsWindow::onWindowResize); mSettingsTab->eventTabChangeSelect += MyGUI::newDelegate(this, &SettingsWindow::onTabChanged); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mTextureFilteringButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onTextureFilteringChanged); mResolutionList->eventListChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onResolutionSelected); mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged); mShadowsTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onShadowTextureSizeChanged); mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked); mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked); center(); mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings); // fill resolution list int screen = Settings::Manager::getInt("screen", "Video"); int numDisplayModes = SDL_GetNumDisplayModes(screen); std::vector < std::pair > resolutions; for (int i = 0; i < numDisplayModes; i++) { SDL_DisplayMode mode; SDL_GetDisplayMode(screen, i, &mode); resolutions.push_back(std::make_pair(mode.w, mode.h)); } std::sort(resolutions.begin(), resolutions.end(), sortResolutions); for (std::vector < std::pair >::const_iterator it=resolutions.begin(); it!=resolutions.end(); ++it) { std::string str = MyGUI::utility::toString(it->first) + " x " + MyGUI::utility::toString(it->second) + " (" + getAspect(it->first,it->second) + ")"; if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE) mResolutionList->addItem(str); } highlightCurrentResolution(); std::string tmip = Settings::Manager::getString("texture mipmap", "General"); mTextureFilteringButton->setCaption(textureMipmappingToStr(tmip)); mAnisotropyLabel->setCaption("Anisotropy (" + MyGUI::utility::toString(Settings::Manager::getInt("anisotropy", "General")) + ")"); int waterTextureSize = Settings::Manager::getInt ("rtt size", "Water"); if (waterTextureSize >= 512) mWaterTextureSize->setIndexSelected(0); if (waterTextureSize >= 1024) mWaterTextureSize->setIndexSelected(1); if (waterTextureSize >= 2048) mWaterTextureSize->setIndexSelected(2); mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows")); if (!Settings::Manager::getBool("shaders", "Objects")) { mShadowsEnabledButton->setEnabled(false); } MyGUI::TextBox* fovText; getWidget(fovText, "FovText"); fovText->setCaption("Field of View (" + MyGUI::utility::toString(int(Settings::Manager::getInt("field of view", "Camera"))) + ")"); MyGUI::TextBox* diffText; getWidget(diffText, "DifficultyText"); diffText->setCaptionWithReplacing("#{sDifficulty} (" + MyGUI::utility::toString(int(Settings::Manager::getInt("difficulty", "Game"))) + ")"); mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video")); mKeyboardSwitch->setStateSelected(true); mControllerSwitch->setStateSelected(false); } void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) { resetScrollbars(); } void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender) { exit(); } void SettingsWindow::onResolutionSelected(MyGUI::ListBox* _sender, size_t index) { if (index == MyGUI::ITEM_NONE) return; ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->askForConfirmation("#{sNotifyMessage67}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept); dialog->eventCancelClicked.clear(); dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel); } void SettingsWindow::onResolutionAccept() { std::string resStr = mResolutionList->getItemNameAt(mResolutionList->getIndexSelected()); int resX, resY; parseResolution (resX, resY, resStr); Settings::Manager::setInt("resolution x", "Video", resX); Settings::Manager::setInt("resolution y", "Video", resY); apply(); } void SettingsWindow::onResolutionCancel() { highlightCurrentResolution(); } void SettingsWindow::highlightCurrentResolution() { mResolutionList->setIndexSelected(MyGUI::ITEM_NONE); int currentX = Settings::Manager::getInt("resolution x", "Video"); int currentY = Settings::Manager::getInt("resolution y", "Video"); for (size_t i=0; igetItemCount(); ++i) { int resX, resY; parseResolution (resX, resY, mResolutionList->getItemNameAt(i)); if (resX == currentX && resY == currentY) { mResolutionList->setIndexSelected(i); break; } } } void SettingsWindow::onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos) { int size = 0; if (pos == 0) size = 512; else if (pos == 1) size = 1024; else if (pos == 2) size = 2048; Settings::Manager::setInt("rtt size", "Water", size); apply(); } void SettingsWindow::onShadowTextureSizeChanged(MyGUI::ComboBox *_sender, size_t pos) { Settings::Manager::setString("texture size", "Shadows", _sender->getItemNameAt(pos)); apply(); } void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender) { std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On"); std::string off = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOff", "On"); bool newState; if (_sender->castType()->getCaption() == on) { _sender->castType()->setCaption(off); newState = false; } else { _sender->castType()->setCaption(on); newState = true; } if (_sender == mShadersButton) { if (newState == false) { // shadows not supported mShadowsEnabledButton->setEnabled(false); mShadowsEnabledButton->setCaptionWithReplacing("#{sOff}"); Settings::Manager::setBool("enabled", "Shadows", false); } else { mShadowsEnabledButton->setEnabled(true); } } if (_sender == mFullscreenButton) { // check if this resolution is supported in fullscreen if (mResolutionList->getIndexSelected() != MyGUI::ITEM_NONE) { std::string resStr = mResolutionList->getItemNameAt(mResolutionList->getIndexSelected()); int resX, resY; parseResolution (resX, resY, resStr); Settings::Manager::setInt("resolution x", "Video", resX); Settings::Manager::setInt("resolution y", "Video", resY); } bool supported = false; for (unsigned int i=0; igetItemCount(); ++i) { std::string resStr = mResolutionList->getItemNameAt(i); int resX, resY; parseResolution (resX, resY, resStr); if (resX == Settings::Manager::getInt("resolution x", "Video") && resY == Settings::Manager::getInt("resolution y", "Video")) supported = true; } if (!supported) { std::string msg = "This resolution is not supported in Fullscreen mode. Please select a resolution from the list."; MWBase::Environment::get().getWindowManager()-> messageBox(msg); _sender->castType()->setCaption(off); return; } mWindowBorderButton->setEnabled(!newState); } if (getSettingType(_sender) == checkButtonType) { Settings::Manager::setBool(getSettingName(_sender), getSettingCategory(_sender), newState); apply(); return; } } void SettingsWindow::onTextureFilteringChanged(MyGUI::ComboBox* _sender, size_t pos) { if(pos == 0) Settings::Manager::setString("texture mipmap", "General", "nearest"); else if(pos == 1) Settings::Manager::setString("texture mipmap", "General", "linear"); else std::cerr<< "Unexpected option pos "<getScrollRange()-1); float min,max; getSettingMinMax(scroller, min, max); value = min + (max-min) * value; Settings::Manager::setFloat(getSettingName(scroller), getSettingCategory(scroller), value); if (scroller == mFOVSlider) { MyGUI::TextBox* fovText; getWidget(fovText, "FovText"); fovText->setCaption("Field of View (" + MyGUI::utility::toString(int(value)) + ")"); } if (scroller == mDifficultySlider) { MyGUI::TextBox* diffText; getWidget(diffText, "DifficultyText"); diffText->setCaptionWithReplacing("#{sDifficulty} (" + MyGUI::utility::toString(int(value)) + ")"); } } else { Settings::Manager::setInt(getSettingName(scroller), getSettingCategory(scroller), pos); if (scroller == mAnisotropySlider) { mAnisotropyLabel->setCaption("Anisotropy (" + MyGUI::utility::toString(pos) + ")"); } } apply(); } } void SettingsWindow::apply() { const Settings::CategorySettingVector changed = Settings::Manager::apply(); MWBase::Environment::get().getWorld()->processChangedSettings(changed); MWBase::Environment::get().getSoundManager()->processChangedSettings(changed); MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); MWBase::Environment::get().getInputManager()->processChangedSettings(changed); } void SettingsWindow::onKeyboardSwitchClicked(MyGUI::Widget* _sender) { if(mKeyboardMode) return; mKeyboardMode = true; mKeyboardSwitch->setStateSelected(true); mControllerSwitch->setStateSelected(false); updateControlsBox(); resetScrollbars(); } void SettingsWindow::onControllerSwitchClicked(MyGUI::Widget* _sender) { if(!mKeyboardMode) return; mKeyboardMode = false; mKeyboardSwitch->setStateSelected(false); mControllerSwitch->setStateSelected(true); updateControlsBox(); resetScrollbars(); } void SettingsWindow::updateControlsBox() { while (mControlsBox->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mControlsBox->getChildAt(0)); MWBase::Environment::get().getWindowManager()->removeStaticMessageBox(); std::vector actions; if(mKeyboardMode) actions = MWBase::Environment::get().getInputManager()->getActionKeySorting(); else actions = MWBase::Environment::get().getInputManager()->getActionControllerSorting(); const int h = 18; const int w = mControlsBox->getWidth() - 28; int curH = 0; for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) { std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); if (desc == "") continue; std::string binding; if(mKeyboardMode) binding = MWBase::Environment::get().getInputManager()->getActionKeyBindingName(*it); else binding = MWBase::Environment::get().getInputManager()->getActionControllerBindingName(*it); Gui::SharedStateButton* leftText = mControlsBox->createWidget("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); leftText->setCaptionWithReplacing(desc); Gui::SharedStateButton* rightText = mControlsBox->createWidget("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); rightText->setCaptionWithReplacing(binding); rightText->setTextAlign (MyGUI::Align::Right); rightText->setUserData(*it); // save the action id for callbacks rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); rightText->eventMouseWheel += MyGUI::newDelegate(this, &SettingsWindow::onInputTabMouseWheel); curH += h; Gui::ButtonGroup group; group.push_back(leftText); group.push_back(rightText); Gui::SharedStateButton::createButtonGroup(group); } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mControlsBox->setVisibleVScroll(false); mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(curH, mControlsBox->getHeight())); mControlsBox->setVisibleVScroll(true); } void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) { int actionId = *_sender->getUserData(); _sender->castType()->setCaptionWithReplacing("#{sNone}"); MWBase::Environment::get().getWindowManager ()->staticMessageBox ("#{sControlsMenu3}"); MWBase::Environment::get().getWindowManager ()->disallowMouse(); MWBase::Environment::get().getInputManager ()->enableDetectingBindingMode (actionId, mKeyboardMode); } void SettingsWindow::onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel) { if (mControlsBox->getViewOffset().top + _rel*0.3f > 0) mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0)); else mControlsBox->setViewOffset(MyGUI::IntPoint(0, static_cast(mControlsBox->getViewOffset().top + _rel*0.3f))); } void SettingsWindow::onResetDefaultBindings(MyGUI::Widget* _sender) { ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->askForConfirmation("#{sNotifyMessage66}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindingsAccept); dialog->eventCancelClicked.clear(); } void SettingsWindow::onResetDefaultBindingsAccept() { if(mKeyboardMode) MWBase::Environment::get().getInputManager ()->resetToDefaultKeyBindings (); else MWBase::Environment::get().getInputManager()->resetToDefaultControllerBindings(); updateControlsBox (); } void SettingsWindow::open() { updateControlsBox (); resetScrollbars(); } void SettingsWindow::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Settings); } void SettingsWindow::onWindowResize(MyGUI::Window *_sender) { updateControlsBox(); } void SettingsWindow::resetScrollbars() { mResolutionList->setScrollPosition(0); mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0)); } } openmw-openmw-0.38.0/apps/openmw/mwgui/settingswindow.hpp000066400000000000000000000053061264522266000236240ustar00rootroot00000000000000#ifndef MWGUI_SETTINGS_H #define MWGUI_SETTINGS_H #include "windowbase.hpp" namespace MWGui { class WindowManager; } namespace MWGui { class SettingsWindow : public WindowBase { public: SettingsWindow(); virtual void open(); virtual void exit(); void updateControlsBox(); protected: MyGUI::TabControl* mSettingsTab; MyGUI::Button* mOkButton; // graphics MyGUI::ListBox* mResolutionList; MyGUI::Button* mFullscreenButton; MyGUI::Button* mVSyncButton; MyGUI::Button* mWindowBorderButton; MyGUI::ScrollBar* mFOVSlider; MyGUI::ScrollBar* mDifficultySlider; MyGUI::ScrollBar* mAnisotropySlider; MyGUI::ComboBox* mTextureFilteringButton; MyGUI::TextBox* mAnisotropyLabel; MyGUI::Widget* mAnisotropyBox; MyGUI::Button* mShadersButton; MyGUI::ComboBox* mWaterTextureSize; MyGUI::Button* mShadowsEnabledButton; MyGUI::ComboBox* mShadowsTextureSize; // controls MyGUI::ScrollView* mControlsBox; MyGUI::Button* mResetControlsButton; MyGUI::Button* mKeyboardSwitch; MyGUI::Button* mControllerSwitch; bool mKeyboardMode; //if true, setting up the keyboard. Otherwise, it's controller void onTabChanged(MyGUI::TabControl* _sender, size_t index); void onOkButtonClicked(MyGUI::Widget* _sender); void onTextureFilteringChanged(MyGUI::ComboBox* _sender, size_t pos); void onSliderChangePosition(MyGUI::ScrollBar* scroller, size_t pos); void onButtonToggled(MyGUI::Widget* _sender); void onResolutionSelected(MyGUI::ListBox* _sender, size_t index); void onResolutionAccept(); void onResolutionCancel(); void highlightCurrentResolution(); void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos); void onShadowTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos); void onRebindAction(MyGUI::Widget* _sender); void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel); void onResetDefaultBindings(MyGUI::Widget* _sender); void onResetDefaultBindingsAccept (); void onKeyboardSwitchClicked(MyGUI::Widget* _sender); void onControllerSwitchClicked(MyGUI::Widget* _sender); void onWindowResize(MyGUI::Window* _sender); void apply(); void configureWidgets(MyGUI::Widget* widget); private: void resetScrollbars(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/sortfilteritemmodel.cpp000066400000000000000000000164161264522266000246300ustar00rootroot00000000000000#include "sortfilteritemmodel.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwworld/class.hpp" #include "../mwworld/nullaction.hpp" namespace { bool compareType(const std::string& type1, const std::string& type2) { // this defines the sorting order of types. types that are first in the vector appear before other types. std::vector mapping; mapping.push_back( typeid(ESM::Weapon).name() ); mapping.push_back( typeid(ESM::Armor).name() ); mapping.push_back( typeid(ESM::Clothing).name() ); mapping.push_back( typeid(ESM::Potion).name() ); mapping.push_back( typeid(ESM::Ingredient).name() ); mapping.push_back( typeid(ESM::Apparatus).name() ); mapping.push_back( typeid(ESM::Book).name() ); mapping.push_back( typeid(ESM::Light).name() ); mapping.push_back( typeid(ESM::Miscellaneous).name() ); mapping.push_back( typeid(ESM::Lockpick).name() ); mapping.push_back( typeid(ESM::Repair).name() ); mapping.push_back( typeid(ESM::Probe).name() ); assert( std::find(mapping.begin(), mapping.end(), type1) != mapping.end() ); assert( std::find(mapping.begin(), mapping.end(), type2) != mapping.end() ); return std::find(mapping.begin(), mapping.end(), type1) < std::find(mapping.begin(), mapping.end(), type2); } struct Compare { bool mSortByType; Compare() : mSortByType(true) {} bool operator() (const MWGui::ItemStack& left, const MWGui::ItemStack& right) { if (mSortByType && left.mType != right.mType) return left.mType < right.mType; if (left.mBase.getTypeName() == right.mBase.getTypeName()) { std::string leftName = Misc::StringUtils::lowerCase(left.mBase.getClass().getName(left.mBase)); std::string rightName = Misc::StringUtils::lowerCase(right.mBase.getClass().getName(right.mBase)); return leftName.compare(rightName) < 0; } else return compareType(left.mBase.getTypeName(), right.mBase.getTypeName()); } }; } namespace MWGui { SortFilterItemModel::SortFilterItemModel(ItemModel *sourceModel) : mCategory(Category_All) , mFilter(0) , mSortByType(true) { mSourceModel = sourceModel; } void SortFilterItemModel::addDragItem (const MWWorld::Ptr& dragItem, size_t count) { mDragItems.push_back(std::make_pair(dragItem, count)); } void SortFilterItemModel::clearDragItems() { mDragItems.clear(); } bool SortFilterItemModel::filterAccepts (const ItemStack& item) { MWWorld::Ptr base = item.mBase; int category = 0; if (base.getTypeName() == typeid(ESM::Armor).name() || base.getTypeName() == typeid(ESM::Clothing).name()) category = Category_Apparel; else if (base.getTypeName() == typeid(ESM::Weapon).name()) category = Category_Weapon; else if (base.getTypeName() == typeid(ESM::Ingredient).name() || base.getTypeName() == typeid(ESM::Potion).name()) category = Category_Magic; else if (base.getTypeName() == typeid(ESM::Miscellaneous).name() || base.getTypeName() == typeid(ESM::Ingredient).name() || base.getTypeName() == typeid(ESM::Repair).name() || base.getTypeName() == typeid(ESM::Lockpick).name() || base.getTypeName() == typeid(ESM::Light).name() || base.getTypeName() == typeid(ESM::Apparatus).name() || base.getTypeName() == typeid(ESM::Book).name() || base.getTypeName() == typeid(ESM::Probe).name()) category = Category_Misc; if (item.mFlags & ItemStack::Flag_Enchanted) category |= Category_Magic; if (!(category & mCategory)) return false; if ((mFilter & Filter_OnlyIngredients) && base.getTypeName() != typeid(ESM::Ingredient).name()) return false; if ((mFilter & Filter_OnlyEnchanted) && !(item.mFlags & ItemStack::Flag_Enchanted)) return false; if ((mFilter & Filter_OnlyChargedSoulstones) && (base.getTypeName() != typeid(ESM::Miscellaneous).name() || base.getCellRef().getSoul() == "")) return false; if ((mFilter & Filter_OnlyEnchantable) && (item.mFlags & ItemStack::Flag_Enchanted || (base.getTypeName() != typeid(ESM::Armor).name() && base.getTypeName() != typeid(ESM::Clothing).name() && base.getTypeName() != typeid(ESM::Weapon).name() && base.getTypeName() != typeid(ESM::Book).name()))) return false; if ((mFilter & Filter_OnlyEnchantable) && base.getTypeName() == typeid(ESM::Book).name() && !base.get()->mBase->mData.mIsScroll) return false; if ((mFilter & Filter_OnlyUsableItems) && typeid(*base.getClass().use(base)) == typeid(MWWorld::NullAction) && base.getClass().getScript(base).empty()) return false; return true; } ItemStack SortFilterItemModel::getItem (ModelIndex index) { if (index < 0) throw std::runtime_error("Invalid index supplied"); if (mItems.size() <= static_cast(index)) throw std::runtime_error("Item index out of range"); return mItems[index]; } size_t SortFilterItemModel::getItemCount() { return mItems.size(); } void SortFilterItemModel::setCategory (int category) { mCategory = category; } void SortFilterItemModel::setFilter (int filter) { mFilter = filter; } void SortFilterItemModel::update() { mSourceModel->update(); size_t count = mSourceModel->getItemCount(); mItems.clear(); for (size_t i=0; igetItem(i); for (std::vector >::iterator it = mDragItems.begin(); it != mDragItems.end(); ++it) { if (item.mBase == it->first) { if (item.mCount < it->second) throw std::runtime_error("Dragging more than present in the model"); item.mCount -= it->second; } } if (item.mCount > 0 && filterAccepts(item)) mItems.push_back(item); } Compare cmp; cmp.mSortByType = mSortByType; std::sort(mItems.begin(), mItems.end(), cmp); } } openmw-openmw-0.38.0/apps/openmw/mwgui/sortfilteritemmodel.hpp000066400000000000000000000030151264522266000246240ustar00rootroot00000000000000#ifndef MWGUI_SORT_FILTER_ITEM_MODEL_H #define MWGUI_SORT_FILTER_ITEM_MODEL_H #include "itemmodel.hpp" namespace MWGui { class SortFilterItemModel : public ProxyItemModel { public: SortFilterItemModel (ItemModel* sourceModel); virtual void update(); bool filterAccepts (const ItemStack& item); virtual ItemStack getItem (ModelIndex index); virtual size_t getItemCount(); /// Dragged items are not displayed. void addDragItem (const MWWorld::Ptr& dragItem, size_t count); void clearDragItems(); void setCategory (int category); void setFilter (int filter); /// Use ItemStack::Type for sorting? void setSortByType(bool sort) { mSortByType = sort; } static const int Category_Weapon = (1<<1); static const int Category_Apparel = (1<<2); static const int Category_Misc = (1<<3); static const int Category_Magic = (1<<4); static const int Category_All = 255; static const int Filter_OnlyIngredients = (1<<0); static const int Filter_OnlyEnchanted = (1<<1); static const int Filter_OnlyEnchantable = (1<<2); static const int Filter_OnlyChargedSoulstones = (1<<3); static const int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action private: std::vector mItems; std::vector > mDragItems; int mCategory; int mFilter; bool mSortByType; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/soulgemdialog.cpp000066400000000000000000000020501264522266000233530ustar00rootroot00000000000000#include "soulgemdialog.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "messagebox.hpp" namespace MWGui { void SoulgemDialog::show(const MWWorld::Ptr &soulgem) { mSoulgem = soulgem; std::vector buttons; buttons.push_back("#{sRechargeEnchantment}"); buttons.push_back("#{sMake Enchantment}"); mManager->createInteractiveMessageBox("#{sDoYouWantTo}", buttons); mManager->eventButtonPressed += MyGUI::newDelegate(this, &SoulgemDialog::onButtonPressed); } void SoulgemDialog::onButtonPressed(int button) { if (button == 0) { MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Recharge); MWBase::Environment::get().getWindowManager()->startRecharge(mSoulgem); } else { MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting); MWBase::Environment::get().getWindowManager()->startSelfEnchanting(mSoulgem); } } } openmw-openmw-0.38.0/apps/openmw/mwgui/soulgemdialog.hpp000066400000000000000000000007341264522266000233670ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_SOULGEMDIALOG_H #define OPENMW_MWGUI_SOULGEMDIALOG_H #include "../mwworld/ptr.hpp" namespace MWGui { class MessageBoxManager; class SoulgemDialog { public: SoulgemDialog (MessageBoxManager* manager) : mManager(manager) {} void show (const MWWorld::Ptr& soulgem); void onButtonPressed(int button); private: MessageBoxManager* mManager; MWWorld::Ptr mSoulgem; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/spellbuyingwindow.cpp000066400000000000000000000156761264522266000243270ustar00rootroot00000000000000#include "spellbuyingwindow.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWGui { const int SpellBuyingWindow::sLineHeight = 18; SpellBuyingWindow::SpellBuyingWindow() : WindowBase("openmw_spell_buying_window.layout") , mLastPos(0) , mCurrentY(0) { setCoord(0, 0, 450, 300); getWidget(mCancelButton, "CancelButton"); getWidget(mPlayerGold, "PlayerGold"); getWidget(mSpellsView, "SpellsView"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked); } void SpellBuyingWindow::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying); } void SpellBuyingWindow::addSpell(const std::string& spellId) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellId); int price = static_cast(spell->mData.mCost*store.get().find("fSpellValueMult")->getFloat()); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); // TODO: refactor to use MyGUI::ListBox MyGUI::Button* toAdd = mSpellsView->createWidget( price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default ); mCurrentY += sLineHeight; toAdd->setUserData(price); toAdd->setCaptionWithReplacing(spell->mName+" - "+MyGUI::utility::toString(price)+"#{sgp}"); toAdd->setSize(toAdd->getTextSize().width,sLineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); toAdd->setUserString("ToolTipType", "Spell"); toAdd->setUserString("Spell", spellId); toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick); mSpellsWidgetMap.insert(std::make_pair (toAdd, spellId)); } void SpellBuyingWindow::clearSpells() { mSpellsView->setViewOffset(MyGUI::IntPoint(0,0)); mCurrentY = 0; while (mSpellsView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mSpellsView->getChildAt(0)); mSpellsWidgetMap.clear(); } void SpellBuyingWindow::startSpellBuying(const MWWorld::Ptr& actor) { center(); mPtr = actor; clearSpells(); MWMechanics::Spells& merchantSpells = actor.getClass().getCreatureStats (actor).getSpells(); for (MWMechanics::Spells::TIterator iter = merchantSpells.begin(); iter!=merchantSpells.end(); ++iter) { const ESM::Spell* spell = iter->first; if (spell->mData.mType!=ESM::Spell::ST_Spell) continue; // don't try to sell diseases, curses or powers if (actor.getClass().isNpc()) { const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find( actor.get()->mBase->mRace); if (race->mPowers.exists(spell->mId)) continue; } if (playerHasSpell(iter->first->mId)) continue; addSpell (iter->first->mId); } updateLabels(); // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mSpellsView->setVisibleVScroll(false); mSpellsView->setCanvasSize (MyGUI::IntSize(mSpellsView->getWidth(), std::max(mSpellsView->getHeight(), mCurrentY))); mSpellsView->setVisibleVScroll(true); } bool SpellBuyingWindow::playerHasSpell(const std::string &id) { MWWorld::Ptr player = MWMechanics::getPlayer(); return player.getClass().getCreatureStats(player).getSpells().hasSpell(id); } void SpellBuyingWindow::onSpellButtonClick(MyGUI::Widget* _sender) { int price = *_sender->getUserData(); MWWorld::Ptr player = MWMechanics::getPlayer(); if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) return; MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); // add gold to NPC trading gold pool MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); npcStats.setGoldPool(npcStats.getGoldPool() + price); startSpellBuying(mPtr); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); } void SpellBuyingWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { exit(); } void SpellBuyingWindow::updateLabels() { MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, mPlayerGold->getHeight()); } void SpellBuyingWindow::onReferenceUnavailable() { // remove both Spells and Dialogue (since you always trade with the NPC/creature that you have previously talked to) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } void SpellBuyingWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (mSpellsView->getViewOffset().top + _rel*0.3 > 0) mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0)); else mSpellsView->setViewOffset(MyGUI::IntPoint(0, static_cast(mSpellsView->getViewOffset().top + _rel*0.3f))); } } openmw-openmw-0.38.0/apps/openmw/mwgui/spellbuyingwindow.hpp000066400000000000000000000022671264522266000243240ustar00rootroot00000000000000#ifndef MWGUI_SpellBuyingWINDOW_H #define MWGUI_SpellBuyingWINDOW_H #include "windowbase.hpp" #include "referenceinterface.hpp" namespace MyGUI { class Gui; class Widget; } namespace MWGui { class WindowManager; } namespace MWGui { class SpellBuyingWindow : public ReferenceInterface, public WindowBase { public: SpellBuyingWindow(); void startSpellBuying(const MWWorld::Ptr& actor); virtual void exit(); protected: MyGUI::Button* mCancelButton; MyGUI::TextBox* mPlayerGold; MyGUI::ScrollView* mSpellsView; std::map mSpellsWidgetMap; void onCancelButtonClicked(MyGUI::Widget* _sender); void onSpellButtonClick(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); void addSpell(const std::string& spellID); void clearSpells(); int mLastPos,mCurrentY; static const int sLineHeight; void updateLabels(); virtual void onReferenceUnavailable(); bool playerHasSpell (const std::string& id); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/spellcreationdialog.cpp000066400000000000000000000645511264522266000245620ustar00rootroot00000000000000#include "spellcreationdialog.hpp" #include #include #include #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "tooltips.hpp" #include "class.hpp" #include "widgets.hpp" namespace { bool sortMagicEffects (short id1, short id2) { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); return gmst.find(ESM::MagicEffect::effectIdToString (id1))->getString() < gmst.find(ESM::MagicEffect::effectIdToString (id2))->getString(); } void init(ESM::ENAMstruct& effect) { effect.mArea = 0; effect.mDuration = 0; effect.mEffectID = -1; effect.mMagnMax = 0; effect.mMagnMin = 0; effect.mRange = 0; effect.mSkill = -1; effect.mAttribute = -1; } } namespace MWGui { EditEffectDialog::EditEffectDialog() : WindowModal("openmw_edit_effect.layout") , mEditing(false) , mMagicEffect(NULL) , mConstantEffect(false) { init(mEffect); init(mOldEffect); getWidget(mCancelButton, "CancelButton"); getWidget(mOkButton, "OkButton"); getWidget(mDeleteButton, "DeleteButton"); getWidget(mRangeButton, "RangeButton"); getWidget(mMagnitudeMinValue, "MagnitudeMinValue"); getWidget(mMagnitudeMaxValue, "MagnitudeMaxValue"); getWidget(mDurationValue, "DurationValue"); getWidget(mAreaValue, "AreaValue"); getWidget(mMagnitudeMinSlider, "MagnitudeMinSlider"); getWidget(mMagnitudeMaxSlider, "MagnitudeMaxSlider"); getWidget(mDurationSlider, "DurationSlider"); getWidget(mAreaSlider, "AreaSlider"); getWidget(mEffectImage, "EffectImage"); getWidget(mEffectName, "EffectName"); getWidget(mAreaText, "AreaText"); getWidget(mDurationBox, "DurationBox"); getWidget(mAreaBox, "AreaBox"); getWidget(mMagnitudeBox, "MagnitudeBox"); mRangeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onRangeButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onOkButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onCancelButtonClicked); mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onDeleteButtonClicked); mMagnitudeMinSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMinChanged); mMagnitudeMaxSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); } void EditEffectDialog::setConstantEffect(bool constant) { mConstantEffect = constant; } void EditEffectDialog::open() { WindowModal::open(); center(); } void EditEffectDialog::exit() { setVisible(false); if(mEditing) eventEffectModified(mOldEffect); else eventEffectRemoved(mEffect); } void EditEffectDialog::newEffect (const ESM::MagicEffect *effect) { bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0; bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; if (!allowSelf && !allowTouch && !allowTarget) return; // TODO: Show an error message popup? setMagicEffect(effect); mEditing = false; mDeleteButton->setVisible (false); mEffect.mRange = ESM::RT_Self; if (!allowSelf) mEffect.mRange = ESM::RT_Touch; if (!allowTouch) mEffect.mRange = ESM::RT_Target; mEffect.mMagnMin = 1; mEffect.mMagnMax = 1; mEffect.mDuration = 1; mEffect.mArea = 0; mEffect.mSkill = -1; mEffect.mAttribute = -1; eventEffectAdded(mEffect); onRangeButtonClicked(mRangeButton); mMagnitudeMinSlider->setScrollPosition (0); mMagnitudeMaxSlider->setScrollPosition (0); mAreaSlider->setScrollPosition (0); mDurationSlider->setScrollPosition (0); mDurationValue->setCaption("1"); mMagnitudeMinValue->setCaption("1"); mMagnitudeMaxValue->setCaption("- 1"); mAreaValue->setCaption("0"); setVisible(true); } void EditEffectDialog::editEffect (ESM::ENAMstruct effect) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); setMagicEffect(magicEffect); mOldEffect = effect; mEffect = effect; mEditing = true; mDeleteButton->setVisible (true); mMagnitudeMinSlider->setScrollPosition (effect.mMagnMin-1); mMagnitudeMaxSlider->setScrollPosition (effect.mMagnMax-1); mAreaSlider->setScrollPosition (effect.mArea); mDurationSlider->setScrollPosition (effect.mDuration-1); onMagnitudeMinChanged (mMagnitudeMinSlider, effect.mMagnMin-1); onMagnitudeMaxChanged (mMagnitudeMinSlider, effect.mMagnMax-1); onAreaChanged (mAreaSlider, effect.mArea); onDurationChanged (mDurationSlider, effect.mDuration-1); eventEffectModified(mEffect); } void EditEffectDialog::setMagicEffect (const ESM::MagicEffect *effect) { mEffectImage->setImageTexture(MWBase::Environment::get().getWindowManager()->correctIconPath(effect->mIcon)); mEffectName->setCaptionWithReplacing("#{"+ESM::MagicEffect::effectIdToString (effect->mIndex)+"}"); mEffect.mEffectID = effect->mIndex; mMagicEffect = effect; updateBoxes(); } void EditEffectDialog::updateBoxes() { static int startY = mMagnitudeBox->getPosition().top; int curY = startY; mMagnitudeBox->setVisible (false); mDurationBox->setVisible (false); mAreaBox->setVisible (false); if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) { mMagnitudeBox->setPosition(mMagnitudeBox->getPosition().left, curY); mMagnitudeBox->setVisible (true); curY += mMagnitudeBox->getSize().height; } if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)&&mConstantEffect==false) { mDurationBox->setPosition(mDurationBox->getPosition().left, curY); mDurationBox->setVisible (true); curY += mDurationBox->getSize().height; } if (mEffect.mRange != ESM::RT_Self) { mAreaBox->setPosition(mAreaBox->getPosition().left, curY); mAreaBox->setVisible (true); //curY += mAreaBox->getSize().height; } } void EditEffectDialog::onRangeButtonClicked (MyGUI::Widget* sender) { mEffect.mRange = (mEffect.mRange+1)%3; // cycle through range types until we find something that's allowed // does not handle the case where nothing is allowed (this should be prevented before opening the Add Effect dialog) bool allowSelf = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0; bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; if (mEffect.mRange == ESM::RT_Self && !allowSelf) mEffect.mRange = (mEffect.mRange+1)%3; if (mEffect.mRange == ESM::RT_Touch && !allowTouch) mEffect.mRange = (mEffect.mRange+1)%3; if (mEffect.mRange == ESM::RT_Target && !allowTarget) mEffect.mRange = (mEffect.mRange+1)%3; if(mEffect.mRange == ESM::RT_Self) { mAreaSlider->setScrollPosition(0); onAreaChanged(mAreaSlider,0); } if (mEffect.mRange == ESM::RT_Self) mRangeButton->setCaptionWithReplacing ("#{sRangeSelf}"); else if (mEffect.mRange == ESM::RT_Target) mRangeButton->setCaptionWithReplacing ("#{sRangeTarget}"); else if (mEffect.mRange == ESM::RT_Touch) mRangeButton->setCaptionWithReplacing ("#{sRangeTouch}"); updateBoxes(); eventEffectModified(mEffect); } void EditEffectDialog::onDeleteButtonClicked (MyGUI::Widget* sender) { setVisible(false); eventEffectRemoved(mEffect); } void EditEffectDialog::onOkButtonClicked (MyGUI::Widget* sender) { setVisible(false); } void EditEffectDialog::onCancelButtonClicked (MyGUI::Widget* sender) { exit(); } void EditEffectDialog::setSkill (int skill) { mEffect.mSkill = skill; eventEffectModified(mEffect); } void EditEffectDialog::setAttribute (int attribute) { mEffect.mAttribute = attribute; eventEffectModified(mEffect); } void EditEffectDialog::onMagnitudeMinChanged (MyGUI::ScrollBar* sender, size_t pos) { mMagnitudeMinValue->setCaption(MyGUI::utility::toString(pos+1)); mEffect.mMagnMin = pos+1; // trigger the check again (see below) onMagnitudeMaxChanged(mMagnitudeMaxSlider, mMagnitudeMaxSlider->getScrollPosition ()); eventEffectModified(mEffect); } void EditEffectDialog::onMagnitudeMaxChanged (MyGUI::ScrollBar* sender, size_t pos) { // make sure the max value is actually larger or equal than the min value size_t magnMin = std::abs(mEffect.mMagnMin); // should never be < 0, this is just here to avoid the compiler warning if (pos+1 < magnMin) { pos = mEffect.mMagnMin-1; sender->setScrollPosition (pos); } mEffect.mMagnMax = pos+1; mMagnitudeMaxValue->setCaption("- " + MyGUI::utility::toString(pos+1)); eventEffectModified(mEffect); } void EditEffectDialog::onDurationChanged (MyGUI::ScrollBar* sender, size_t pos) { mDurationValue->setCaption(MyGUI::utility::toString(pos+1)); mEffect.mDuration = pos+1; eventEffectModified(mEffect); } void EditEffectDialog::onAreaChanged (MyGUI::ScrollBar* sender, size_t pos) { mAreaValue->setCaption(MyGUI::utility::toString(pos)); mEffect.mArea = pos; eventEffectModified(mEffect); } // ------------------------------------------------------------------------------------------------ SpellCreationDialog::SpellCreationDialog() : WindowBase("openmw_spellcreation_dialog.layout") , EffectEditorBase(EffectEditorBase::Spellmaking) { getWidget(mNameEdit, "NameEdit"); getWidget(mMagickaCost, "MagickaCost"); getWidget(mSuccessChance, "SuccessChance"); getWidget(mAvailableEffectsList, "AvailableEffects"); getWidget(mUsedEffectsView, "UsedEffects"); getWidget(mPriceLabel, "PriceLabel"); getWidget(mBuyButton, "BuyButton"); getWidget(mCancelButton, "CancelButton"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onCancelButtonClicked); mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onBuyButtonClicked); setWidgets(mAvailableEffectsList, mUsedEffectsView); } void SpellCreationDialog::startSpellMaking (MWWorld::Ptr actor) { mPtr = actor; mNameEdit->setCaption(""); startEditing(); } void SpellCreationDialog::onCancelButtonClicked (MyGUI::Widget* sender) { exit(); } void SpellCreationDialog::onBuyButtonClicked (MyGUI::Widget* sender) { if (mEffects.size() <= 0) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}"); return; } if (mNameEdit->getCaption () == "") { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage10}"); return; } if (mMagickaCost->getCaption() == "0") { MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu8}"); return; } MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); if (MyGUI::utility::parseInt(mPriceLabel->getCaption()) > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; } mSpell.mName = mNameEdit->getCaption(); int price = MyGUI::utility::parseInt(mPriceLabel->getCaption()); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); // add gold to NPC trading gold pool MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); npcStats.setGoldPool(npcStats.getGoldPool() + price); MWBase::Environment::get().getSoundManager()->playSound ("Mysticism Hit", 1.0, 1.0); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->createRecord(mSpell); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); spells.add (spell->mId); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation); } void SpellCreationDialog::open() { center(); } void SpellCreationDialog::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_SpellCreation); } void SpellCreationDialog::onReferenceUnavailable () { MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation); } void SpellCreationDialog::notifyEffectsChanged () { if (mEffects.empty()) { mMagickaCost->setCaption("0"); mPriceLabel->setCaption("0"); mSuccessChance->setCaption("0"); return; } float y = 0; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { float x = 0.5f * (it->mMagnMin + it->mMagnMax); const ESM::MagicEffect* effect = store.get().find(it->mEffectID); x *= 0.1f * effect->mData.mBaseCost; x *= 1 + it->mDuration; x += 0.05f * std::max(1, it->mArea) * effect->mData.mBaseCost; float fEffectCostMult = store.get().find("fEffectCostMult")->getFloat(); y += x * fEffectCostMult; y = std::max(1.f,y); if (it->mRange == ESM::RT_Target) y *= 1.5; } ESM::EffectList effectList; effectList.mList = mEffects; mSpell.mEffects = effectList; mSpell.mData.mCost = int(y); mSpell.mData.mType = ESM::Spell::ST_Spell; mSpell.mData.mFlags = 0; mMagickaCost->setCaption(MyGUI::utility::toString(int(y))); float fSpellMakingValueMult = store.get().find("fSpellMakingValueMult")->getFloat(); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, static_cast(y * fSpellMakingValueMult),true); mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); float chance = MWMechanics::getSpellSuccessChance(&mSpell, MWMechanics::getPlayer()); mSuccessChance->setCaption(MyGUI::utility::toString(int(chance))); } // ------------------------------------------------------------------------------------------------ EffectEditorBase::EffectEditorBase(Type type) : mAvailableEffectsList(NULL) , mUsedEffectsView(NULL) , mAddEffectDialog() , mSelectAttributeDialog(NULL) , mSelectSkillDialog(NULL) , mSelectedEffect(0) , mSelectedKnownEffectId(0) , mConstantEffect(false) , mType(type) { mAddEffectDialog.eventEffectAdded += MyGUI::newDelegate(this, &EffectEditorBase::onEffectAdded); mAddEffectDialog.eventEffectModified += MyGUI::newDelegate(this, &EffectEditorBase::onEffectModified); mAddEffectDialog.eventEffectRemoved += MyGUI::newDelegate(this, &EffectEditorBase::onEffectRemoved); mAddEffectDialog.setVisible (false); } EffectEditorBase::~EffectEditorBase() { } void EffectEditorBase::startEditing () { // get the list of magic effects that are known to the player MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); std::vector knownEffects; for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ESM::Spell* spell = it->first; // only normal spells count if (spell->mData.mType != ESM::Spell::ST_Spell) continue; const std::vector& list = spell->mEffects.mList; for (std::vector::const_iterator it2 = list.begin(); it2 != list.end(); ++it2) { const ESM::MagicEffect * effect = MWBase::Environment::get().getWorld()->getStore().get().find(it2->mEffectID); // skip effects that do not allow spellmaking/enchanting int requiredFlags = (mType == Spellmaking) ? ESM::MagicEffect::AllowSpellmaking : ESM::MagicEffect::AllowEnchanting; if (!(effect->mData.mFlags & requiredFlags)) continue; if (std::find(knownEffects.begin(), knownEffects.end(), it2->mEffectID) == knownEffects.end()) knownEffects.push_back(it2->mEffectID); } } std::sort(knownEffects.begin(), knownEffects.end(), sortMagicEffects); mAvailableEffectsList->clear (); int i=0; for (std::vector::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it) { mAvailableEffectsList->addItem(MWBase::Environment::get().getWorld ()->getStore ().get().find( ESM::MagicEffect::effectIdToString (*it))->getString()); mButtonMapping[i] = *it; ++i; } mAvailableEffectsList->adjustSize (); mAvailableEffectsList->scrollToTop(); for (std::vector::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it) { std::string name = MWBase::Environment::get().getWorld ()->getStore ().get().find( ESM::MagicEffect::effectIdToString (*it))->getString(); MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); ToolTips::createMagicEffectToolTip (w, *it); } mEffects.clear(); updateEffectsView (); } void EffectEditorBase::setWidgets (Gui::MWList *availableEffectsList, MyGUI::ScrollView *usedEffectsView) { mAvailableEffectsList = availableEffectsList; mUsedEffectsView = usedEffectsView; mAvailableEffectsList->eventWidgetSelected += MyGUI::newDelegate(this, &EffectEditorBase::onAvailableEffectClicked); } void EffectEditorBase::onSelectAttribute () { const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(mSelectedKnownEffectId); mAddEffectDialog.newEffect(effect); mAddEffectDialog.setAttribute (mSelectAttributeDialog->getAttributeId()); MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog); mSelectAttributeDialog = 0; } void EffectEditorBase::onSelectSkill () { const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(mSelectedKnownEffectId); mAddEffectDialog.newEffect(effect); mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId()); MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog); mSelectSkillDialog = 0; } void EffectEditorBase::onAttributeOrSkillCancel () { if (mSelectSkillDialog) MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog); if (mSelectAttributeDialog) MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog); mSelectSkillDialog = 0; mSelectAttributeDialog = 0; } void EffectEditorBase::onAvailableEffectClicked (MyGUI::Widget* sender) { if (mEffects.size() >= 8) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}"); return; } int buttonId = *sender->getUserData(); mSelectedKnownEffectId = mButtonMapping[buttonId]; const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(mSelectedKnownEffectId); if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) { delete mSelectSkillDialog; mSelectSkillDialog = new SelectSkillDialog(); mSelectSkillDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); mSelectSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectSkill); mSelectSkillDialog->setVisible (true); } else if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) { delete mSelectAttributeDialog; mSelectAttributeDialog = new SelectAttributeDialog(); mSelectAttributeDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); mSelectAttributeDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectAttribute); mSelectAttributeDialog->setVisible (true); } else { for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { if (it->mEffectID == mSelectedKnownEffectId) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}"); return; } } mAddEffectDialog.newEffect(effect); } } void EffectEditorBase::onEffectModified (ESM::ENAMstruct effect) { mEffects[mSelectedEffect] = effect; updateEffectsView(); } void EffectEditorBase::onEffectRemoved (ESM::ENAMstruct effect) { mEffects.erase(mEffects.begin() + mSelectedEffect); updateEffectsView(); } void EffectEditorBase::updateEffectsView () { MyGUI::EnumeratorWidgetPtr oldWidgets = mUsedEffectsView->getEnumerator (); MyGUI::Gui::getInstance ().destroyWidgets (oldWidgets); MyGUI::IntSize size(0,0); int i = 0; for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { Widgets::SpellEffectParams params; params.mEffectID = it->mEffectID; params.mSkill = it->mSkill; params.mAttribute = it->mAttribute; params.mDuration = it->mDuration; params.mMagnMin = it->mMagnMin; params.mMagnMax = it->mMagnMax; params.mRange = it->mRange; params.mArea = it->mArea; params.mIsConstant = mConstantEffect; MyGUI::Button* button = mUsedEffectsView->createWidget("", MyGUI::IntCoord(0, size.height, 0, 24), MyGUI::Align::Default); button->setUserData(i); button->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onEditEffect); button->setNeedMouseFocus (true); Widgets::MWSpellEffectPtr effect = button->createWidget("MW_EffectImage", MyGUI::IntCoord(0,0,0,24), MyGUI::Align::Default); effect->setNeedMouseFocus (false); effect->setSpellEffect (params); effect->setSize(effect->getRequestedWidth (), 24); button->setSize(effect->getRequestedWidth (), 24); size.width = std::max(size.width, effect->getRequestedWidth ()); size.height += 24; ++i; } // Canvas size must be expressed with HScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mUsedEffectsView->setVisibleHScroll(false); mUsedEffectsView->setCanvasSize(size); mUsedEffectsView->setVisibleHScroll(true); notifyEffectsChanged(); } void EffectEditorBase::onEffectAdded (ESM::ENAMstruct effect) { mEffects.push_back(effect); mSelectedEffect=mEffects.size()-1; updateEffectsView(); } void EffectEditorBase::onEditEffect (MyGUI::Widget *sender) { int id = *sender->getUserData(); mSelectedEffect = id; mAddEffectDialog.editEffect (mEffects[id]); mAddEffectDialog.setVisible (true); } void EffectEditorBase::setConstantEffect(bool constant) { mAddEffectDialog.setConstantEffect(constant); mConstantEffect = constant; } } openmw-openmw-0.38.0/apps/openmw/mwgui/spellcreationdialog.hpp000066400000000000000000000110101264522266000245450ustar00rootroot00000000000000#ifndef MWGUI_SPELLCREATION_H #define MWGUI_SPELLCREATION_H #include #include #include "windowbase.hpp" #include "referenceinterface.hpp" namespace Gui { class MWList; } namespace MWGui { class SelectSkillDialog; class SelectAttributeDialog; class EditEffectDialog : public WindowModal { public: EditEffectDialog(); virtual void open(); virtual void exit(); void setConstantEffect(bool constant); void setSkill(int skill); void setAttribute(int attribute); void newEffect (const ESM::MagicEffect* effect); void editEffect (ESM::ENAMstruct effect); typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Effect; EventHandle_Effect eventEffectAdded; EventHandle_Effect eventEffectModified; EventHandle_Effect eventEffectRemoved; protected: MyGUI::Button* mCancelButton; MyGUI::Button* mOkButton; MyGUI::Button* mDeleteButton; MyGUI::Button* mRangeButton; MyGUI::Widget* mDurationBox; MyGUI::Widget* mMagnitudeBox; MyGUI::Widget* mAreaBox; MyGUI::TextBox* mMagnitudeMinValue; MyGUI::TextBox* mMagnitudeMaxValue; MyGUI::TextBox* mDurationValue; MyGUI::TextBox* mAreaValue; MyGUI::ScrollBar* mMagnitudeMinSlider; MyGUI::ScrollBar* mMagnitudeMaxSlider; MyGUI::ScrollBar* mDurationSlider; MyGUI::ScrollBar* mAreaSlider; MyGUI::TextBox* mAreaText; MyGUI::ImageBox* mEffectImage; MyGUI::TextBox* mEffectName; bool mEditing; protected: void onRangeButtonClicked (MyGUI::Widget* sender); void onDeleteButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender); void onCancelButtonClicked (MyGUI::Widget* sender); void onMagnitudeMinChanged (MyGUI::ScrollBar* sender, size_t pos); void onMagnitudeMaxChanged (MyGUI::ScrollBar* sender, size_t pos); void onDurationChanged (MyGUI::ScrollBar* sender, size_t pos); void onAreaChanged (MyGUI::ScrollBar* sender, size_t pos); void setMagicEffect(const ESM::MagicEffect* effect); void updateBoxes(); protected: ESM::ENAMstruct mEffect; ESM::ENAMstruct mOldEffect; const ESM::MagicEffect* mMagicEffect; bool mConstantEffect; }; class EffectEditorBase { public: enum Type { Spellmaking, Enchanting }; EffectEditorBase(Type type); virtual ~EffectEditorBase(); void setConstantEffect(bool constant); protected: std::map mButtonMapping; // maps button ID to effect ID Gui::MWList* mAvailableEffectsList; MyGUI::ScrollView* mUsedEffectsView; EditEffectDialog mAddEffectDialog; SelectAttributeDialog* mSelectAttributeDialog; SelectSkillDialog* mSelectSkillDialog; int mSelectedEffect; short mSelectedKnownEffectId; bool mConstantEffect; std::vector mEffects; void onEffectAdded(ESM::ENAMstruct effect); void onEffectModified(ESM::ENAMstruct effect); void onEffectRemoved(ESM::ENAMstruct effect); void onAvailableEffectClicked (MyGUI::Widget* sender); void onAttributeOrSkillCancel(); void onSelectAttribute(); void onSelectSkill(); void onEditEffect(MyGUI::Widget* sender); void updateEffectsView(); void startEditing(); void setWidgets (Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView); virtual void notifyEffectsChanged () {} private: Type mType; }; class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase { public: SpellCreationDialog(); virtual void open(); virtual void exit(); void startSpellMaking(MWWorld::Ptr actor); protected: virtual void onReferenceUnavailable (); void onCancelButtonClicked (MyGUI::Widget* sender); void onBuyButtonClicked (MyGUI::Widget* sender); virtual void notifyEffectsChanged (); MyGUI::EditBox* mNameEdit; MyGUI::TextBox* mMagickaCost; MyGUI::TextBox* mSuccessChance; MyGUI::Button* mBuyButton; MyGUI::Button* mCancelButton; MyGUI::TextBox* mPriceLabel; ESM::Spell mSpell; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/spellicons.cpp000066400000000000000000000222371264522266000227040ustar00rootroot00000000000000#include "spellicons.hpp" #include #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "tooltips.hpp" namespace MWGui { void EffectSourceVisitor::visit (MWMechanics::EffectKey key, const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) { MagicEffectInfo newEffectSource; newEffectSource.mKey = key; newEffectSource.mMagnitude = static_cast(magnitude); newEffectSource.mPermanent = mIsPermanent; newEffectSource.mRemainingTime = remainingTime; newEffectSource.mSource = sourceName; newEffectSource.mTotalTime = totalTime; mEffectSources[key.mId].push_back(newEffectSource); } void SpellIcons::updateWidgets(MyGUI::Widget *parent, bool adjustSize) { // TODO: Tracking add/remove/expire would be better than force updating every frame MWWorld::Ptr player = MWMechanics::getPlayer(); const MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); EffectSourceVisitor visitor; // permanent item enchantments & permanent spells visitor.mIsPermanent = true; MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); store.visitEffectSources(visitor); stats.getSpells().visitEffectSources(visitor); // now add lasting effects visitor.mIsPermanent = false; stats.getActiveSpells().visitEffectSources(visitor); std::map >& effects = visitor.mEffectSources; int w=2; for (std::map >::const_iterator it = effects.begin(); it != effects.end(); ++it) { const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld ()->getStore ().get().find(it->first); float remainingDuration = 0; float totalDuration = 0; std::string sourcesDescription; static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get().find("fMagicStartIconBlink")->getFloat(); for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) { if (effectIt != it->second.begin()) sourcesDescription += "\n"; // if at least one of the effect sources is permanent, the effect will never wear off if (effectIt->mPermanent) { remainingDuration = fadeTime; totalDuration = fadeTime; } else { remainingDuration = std::max(remainingDuration, effectIt->mRemainingTime); totalDuration = std::max(totalDuration, effectIt->mTotalTime); } sourcesDescription += effectIt->mSource; if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) sourcesDescription += " (" + MWBase::Environment::get().getWindowManager()->getGameSettingString( ESM::Skill::sSkillNameIds[effectIt->mKey.mArg], "") + ")"; if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) sourcesDescription += " (" + MWBase::Environment::get().getWindowManager()->getGameSettingString( ESM::Attribute::sGmstAttributeIds[effectIt->mKey.mArg], "") + ")"; ESM::MagicEffect::MagnitudeDisplayType displayType = effect->getMagnitudeDisplayType(); if (displayType == ESM::MagicEffect::MDT_TimesInt) { std::string timesInt = MWBase::Environment::get().getWindowManager()->getGameSettingString("sXTimesINT", ""); std::stringstream formatter; formatter << std::fixed << std::setprecision(1) << " " << (effectIt->mMagnitude / 10.0f) << timesInt; sourcesDescription += formatter.str(); } else if ( displayType != ESM::MagicEffect::MDT_None ) { sourcesDescription += ": " + MyGUI::utility::toString(effectIt->mMagnitude); if ( displayType == ESM::MagicEffect::MDT_Percentage ) sourcesDescription += MWBase::Environment::get().getWindowManager()->getGameSettingString("spercent", ""); else if ( displayType == ESM::MagicEffect::MDT_Feet ) sourcesDescription += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfeet", ""); else if ( displayType == ESM::MagicEffect::MDT_Level ) { sourcesDescription += " " + ((effectIt->mMagnitude > 1) ? MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevels", "") : MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevel", "") ); } else // ESM::MagicEffect::MDT_Points { sourcesDescription += " " + ((effectIt->mMagnitude > 1) ? MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", "") : MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", "") ); } } if (effectIt->mRemainingTime > -1 && Settings::Manager::getBool("show effect duration","Game")) { sourcesDescription += " #{sDuration}: "; float duration = effectIt->mRemainingTime; if (duration > 3600) { int hour = duration / 3600; duration -= hour*3600; sourcesDescription += MWGui::ToolTips::toString(hour) + "h"; } if (duration > 60) { int minute = duration / 60; duration -= minute*60; sourcesDescription += MWGui::ToolTips::toString(minute) + "m"; } if (duration > 0.1) { sourcesDescription += MWGui::ToolTips::toString(duration) + "s"; } } } if (remainingDuration > 0.f) { MyGUI::ImageBox* image; if (mWidgetMap.find(it->first) == mWidgetMap.end()) { image = parent->createWidget ("ImageBox", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default); mWidgetMap[it->first] = image; image->setImageTexture(MWBase::Environment::get().getWindowManager()->correctIconPath(effect->mIcon)); std::string name = ESM::MagicEffect::effectIdToString (it->first); ToolTipInfo tooltipInfo; tooltipInfo.caption = "#{" + name + "}"; tooltipInfo.icon = effect->mIcon; tooltipInfo.imageSize = 16; tooltipInfo.wordWrap = false; image->setUserData(tooltipInfo); image->setUserString("ToolTipType", "ToolTipInfo"); } else image = mWidgetMap[it->first]; image->setPosition(w,2); image->setVisible(true); w += 16; ToolTipInfo* tooltipInfo = image->getUserData(); tooltipInfo->text = sourcesDescription; // Fade out if (totalDuration >= fadeTime && fadeTime > 0.f) image->setAlpha(std::min(remainingDuration/fadeTime, 1.f)); } else if (mWidgetMap.find(it->first) != mWidgetMap.end()) { mWidgetMap[it->first]->setVisible(false); } } if (adjustSize) { int s = w + 2; if (effects.empty()) s = 0; int diff = parent->getWidth() - s; parent->setSize(s, parent->getHeight()); parent->setPosition(parent->getLeft()+diff, parent->getTop()); } // hide inactive effects for (std::map::iterator it = mWidgetMap.begin(); it != mWidgetMap.end(); ++it) { if (effects.find(it->first) == effects.end()) it->second->setVisible(false); } } } openmw-openmw-0.38.0/apps/openmw/mwgui/spellicons.hpp000066400000000000000000000027561264522266000227150ustar00rootroot00000000000000#ifndef MWGUI_SPELLICONS_H #define MWGUI_SPELLICONS_H #include #include #include "../mwmechanics/magiceffects.hpp" namespace MyGUI { class Widget; class ImageBox; } namespace ESM { struct ENAMstruct; struct EffectList; } namespace MWGui { // information about a single magic effect source as required for display in the tooltip struct MagicEffectInfo { MagicEffectInfo() : mMagnitude(0) , mRemainingTime(0.f) , mTotalTime(0.f) , mPermanent(false) {} std::string mSource; // display name for effect source (e.g. potion name) MWMechanics::EffectKey mKey; int mMagnitude; float mRemainingTime; float mTotalTime; bool mPermanent; // the effect is permanent }; class EffectSourceVisitor : public MWMechanics::EffectSourceVisitor { public: bool mIsPermanent; std::map > mEffectSources; virtual ~EffectSourceVisitor() {} virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1); }; class SpellIcons { public: void updateWidgets(MyGUI::Widget* parent, bool adjustSize); private: std::map mWidgetMap; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/spellmodel.cpp000066400000000000000000000115131264522266000226640ustar00rootroot00000000000000#include "spellmodel.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" namespace { bool sortSpells(const MWGui::Spell& left, const MWGui::Spell& right) { if (left.mType != right.mType) return left.mType < right.mType; std::string leftName = Misc::StringUtils::lowerCase(left.mName); std::string rightName = Misc::StringUtils::lowerCase(right.mName); return leftName.compare(rightName) < 0; } } namespace MWGui { SpellModel::SpellModel(const MWWorld::Ptr &actor) : mActor(actor) { } void SpellModel::update() { mSpells.clear(); MWMechanics::CreatureStats& stats = mActor.getClass().getCreatureStats(mActor); const MWMechanics::Spells& spells = stats.getSpells(); const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ESM::Spell* spell = it->first; if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell) continue; Spell newSpell; newSpell.mName = spell->mName; if (spell->mData.mType == ESM::Spell::ST_Spell) { newSpell.mType = Spell::Type_Spell; std::string cost = boost::lexical_cast(spell->mData.mCost); std::string chance = boost::lexical_cast(int(MWMechanics::getSpellSuccessChance(spell, mActor))); newSpell.mCostColumn = cost + "/" + chance; } else newSpell.mType = Spell::Type_Power; newSpell.mId = spell->mId; newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == spell->mId); newSpell.mActive = true; mSpells.push_back(newSpell); } MWWorld::InventoryStore& invStore = mActor.getClass().getInventoryStore(mActor); for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { MWWorld::Ptr item = *it; const std::string enchantId = item.getClass().getEnchantment(item); if (enchantId.empty()) continue; const ESM::Enchantment* enchant = esmStore.get().search(enchantId); if (!enchant) { std::cerr << "Can't find enchantment '" << enchantId << "' on item " << item.getCellRef().getRefId() << std::endl; continue; } if (enchant->mData.mType != ESM::Enchantment::WhenUsed && enchant->mData.mType != ESM::Enchantment::CastOnce) continue; Spell newSpell; newSpell.mItem = item; newSpell.mId = item.getCellRef().getRefId(); newSpell.mName = item.getClass().getName(item); newSpell.mType = Spell::Type_EnchantedItem; newSpell.mSelected = invStore.getSelectedEnchantItem() == it; // FIXME: move to mwmechanics if (enchant->mData.mType == ESM::Enchantment::CastOnce) { newSpell.mCostColumn = "100/100"; newSpell.mActive = false; } else { if (!item.getClass().getEquipmentSlots(item).first.empty() && item.getClass().canBeEquipped(item, mActor).first == 0) continue; int castCost = MWMechanics::getEffectiveEnchantmentCastCost(static_cast(enchant->mData.mCost), mActor); std::string cost = boost::lexical_cast(castCost); int currentCharge = int(item.getCellRef().getEnchantmentCharge()); if (currentCharge == -1) currentCharge = enchant->mData.mCharge; std::string charge = boost::lexical_cast(currentCharge); newSpell.mCostColumn = cost + "/" + charge; newSpell.mActive = invStore.isEquipped(item); } mSpells.push_back(newSpell); } std::stable_sort(mSpells.begin(), mSpells.end(), sortSpells); } size_t SpellModel::getItemCount() const { return mSpells.size(); } Spell SpellModel::getItem(ModelIndex index) const { if (index < 0 || index >= int(mSpells.size())) throw std::runtime_error("invalid spell index supplied"); return mSpells[index]; } } openmw-openmw-0.38.0/apps/openmw/mwgui/spellmodel.hpp000066400000000000000000000023221264522266000226670ustar00rootroot00000000000000#ifndef OPENMW_GUI_SPELLMODEL_H #define OPENMW_GUI_SPELLMODEL_H #include "../mwworld/ptr.hpp" namespace MWGui { struct Spell { enum Type { Type_Power, Type_Spell, Type_EnchantedItem }; Type mType; std::string mName; std::string mCostColumn; // Cost/chance or Cost/charge std::string mId; // Item ID or spell ID MWWorld::Ptr mItem; // Only for Type_EnchantedItem bool mSelected; // Is this the currently selected spell/item (only one can be selected at a time) bool mActive; // (Items only) is the item equipped? Spell() : mType(Type_Spell) , mSelected(false) , mActive(false) { } }; ///@brief Model that lists all usable powers, spells and enchanted items for an actor. class SpellModel { public: SpellModel(const MWWorld::Ptr& actor); typedef int ModelIndex; void update(); Spell getItem (ModelIndex index) const; ///< throws for invalid index size_t getItemCount() const; private: MWWorld::Ptr mActor; std::vector mSpells; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/spellview.cpp000066400000000000000000000251211264522266000225360ustar00rootroot00000000000000#include "spellview.hpp" #include #include #include #include #include namespace MWGui { const char* SpellView::sSpellModelIndex = "SpellModelIndex"; SpellView::LineInfo::LineInfo(MyGUI::Widget* leftWidget, MyGUI::Widget* rightWidget, SpellModel::ModelIndex spellIndex) : mLeftWidget(leftWidget) , mRightWidget(rightWidget) , mSpellIndex(spellIndex) { } SpellView::SpellView() : mScrollView(NULL) , mShowCostColumn(true) , mHighlightSelected(true) { } void SpellView::initialiseOverride() { Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); if (mScrollView == NULL) throw std::runtime_error("Item view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); } void SpellView::registerComponents() { MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } void SpellView::setModel(SpellModel *model) { mModel.reset(model); update(); } SpellModel* SpellView::getModel() { return mModel.get(); } void SpellView::setShowCostColumn(bool show) { if (show != mShowCostColumn) { mShowCostColumn = show; update(); } } void SpellView::setHighlightSelected(bool highlight) { if (highlight != mHighlightSelected) { mHighlightSelected = highlight; update(); } } void SpellView::update() { if (!mModel.get()) return; mModel->update(); int curType = -1; const int spellHeight = 18; mLines.clear(); while (mScrollView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); for (SpellModel::ModelIndex i = 0; igetItemCount()); ++i) { const Spell& spell = mModel->getItem(i); if (curType != spell.mType) { if (spell.mType == Spell::Type_Power) addGroup("#{sPowers}", ""); else if (spell.mType == Spell::Type_Spell) addGroup("#{sSpells}", "#{sCostChance}"); else addGroup("#{sMagicItem}", "#{sCostCharge}"); curType = spell.mType; } const std::string skin = spell.mActive ? "SandTextButton" : "SpellTextUnequipped"; Gui::SharedStateButton* t = mScrollView->createWidget(skin, MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); t->setCaption(spell.mName); t->setTextAlign(MyGUI::Align::Left); adjustSpellWidget(spell, i, t); if (!spell.mCostColumn.empty() && mShowCostColumn) { Gui::SharedStateButton* costChance = mScrollView->createWidget(skin, MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); costChance->setCaption(spell.mCostColumn); costChance->setTextAlign(MyGUI::Align::Right); adjustSpellWidget(spell, i, costChance); Gui::ButtonGroup group; group.push_back(t); group.push_back(costChance); Gui::SharedStateButton::createButtonGroup(group); mLines.push_back(LineInfo(t, costChance, i)); } else mLines.push_back(LineInfo(t, (MyGUI::Widget*)NULL, i)); t->setStateSelected(spell.mSelected); } layoutWidgets(); } void SpellView::incrementalUpdate() { if (!mModel.get()) { return; } mModel->update(); bool fullUpdateRequired = false; SpellModel::ModelIndex maxSpellIndexFound = -1; for (std::vector< LineInfo >::iterator it = mLines.begin(); it != mLines.end(); ++it) { // only update the lines that are "updateable" SpellModel::ModelIndex spellIndex(it->mSpellIndex); if (spellIndex != NoSpellIndex) { Gui::SharedStateButton* nameButton = reinterpret_cast(it->mLeftWidget); // match model against line // if don't match, then major change has happened, so do a full update if (mModel->getItemCount() <= static_cast(spellIndex)) { fullUpdateRequired = true; break; } // more checking for major change. const Spell& spell = mModel->getItem(spellIndex); if (nameButton->getCaption() != spell.mName) { fullUpdateRequired = true; break; } else { maxSpellIndexFound = spellIndex; Gui::SharedStateButton* costButton = reinterpret_cast(it->mRightWidget); if ((costButton != NULL) && (costButton->getCaption() != spell.mCostColumn)) { costButton->setCaption(spell.mCostColumn); } } } } // special case, look for spells added to model that are beyond last updatable item SpellModel::ModelIndex topSpellIndex = mModel->getItemCount() - 1; if (fullUpdateRequired || ((0 <= topSpellIndex) && (maxSpellIndexFound < topSpellIndex))) { update(); } } void SpellView::layoutWidgets() { int height = 0; for (std::vector< LineInfo >::iterator it = mLines.begin(); it != mLines.end(); ++it) { height += (it->mLeftWidget)->getHeight(); } bool scrollVisible = height > mScrollView->getHeight(); int width = mScrollView->getWidth() - (scrollVisible ? 18 : 0); height = 0; for (std::vector< LineInfo >::iterator it = mLines.begin(); it != mLines.end(); ++it) { int lineHeight = (it->mLeftWidget)->getHeight(); (it->mLeftWidget)->setCoord(4, height, width - 8, lineHeight); if (it->mRightWidget) { (it->mRightWidget)->setCoord(4, height, width - 8, lineHeight); MyGUI::TextBox* second = (it->mRightWidget)->castType(false); if (second) (it->mLeftWidget)->setSize(width - 8 - second->getTextSize().width, lineHeight); } height += lineHeight; } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mScrollView->setVisibleVScroll(false); mScrollView->setCanvasSize(mScrollView->getWidth(), std::max(mScrollView->getHeight(), height)); mScrollView->setVisibleVScroll(true); } void SpellView::addGroup(const std::string &label, const std::string& label2) { if (mScrollView->getChildCount() > 0) { MyGUI::ImageBox* separator = mScrollView->createWidget("MW_HLine", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18), MyGUI::Align::Left | MyGUI::Align::Top); separator->setNeedMouseFocus(false); mLines.push_back(LineInfo(separator, (MyGUI::Widget*)NULL, NoSpellIndex)); } MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top); groupWidget->setCaptionWithReplacing(label); groupWidget->setTextAlign(MyGUI::Align::Left); groupWidget->setNeedMouseFocus(false); if (label2 != "") { MyGUI::TextBox* groupWidget2 = mScrollView->createWidget("SandBrightText", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top); groupWidget2->setCaptionWithReplacing(label2); groupWidget2->setTextAlign(MyGUI::Align::Right); groupWidget2->setNeedMouseFocus(false); mLines.push_back(LineInfo(groupWidget, groupWidget2, NoSpellIndex)); } else mLines.push_back(LineInfo(groupWidget, (MyGUI::Widget*)NULL, NoSpellIndex)); } void SpellView::setSize(const MyGUI::IntSize &_value) { bool changed = (_value.width != getWidth() || _value.height != getHeight()); Base::setSize(_value); if (changed) layoutWidgets(); } void SpellView::setCoord(const MyGUI::IntCoord &_value) { bool changed = (_value.width != getWidth() || _value.height != getHeight()); Base::setCoord(_value); if (changed) layoutWidgets(); } void SpellView::adjustSpellWidget(const Spell &spell, SpellModel::ModelIndex index, MyGUI::Widget *widget) { if (spell.mType == Spell::Type_EnchantedItem) { widget->setUserData(spell.mItem); widget->setUserString("ToolTipType", "ItemPtr"); } else { widget->setUserString("ToolTipType", "Spell"); widget->setUserString("Spell", spell.mId); } widget->setUserString(sSpellModelIndex, MyGUI::utility::toString(index)); widget->eventMouseWheel += MyGUI::newDelegate(this, &SpellView::onMouseWheelMoved); widget->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellView::onSpellSelected); } SpellModel::ModelIndex SpellView::getSpellModelIndex(MyGUI::Widget* widget) { return MyGUI::utility::parseInt(widget->getUserString(sSpellModelIndex)); } void SpellView::onSpellSelected(MyGUI::Widget* _sender) { eventSpellClicked(getSpellModelIndex(_sender)); } void SpellView::onMouseWheelMoved(MyGUI::Widget* _sender, int _rel) { if (mScrollView->getViewOffset().top + _rel*0.3f > 0) mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); else mScrollView->setViewOffset(MyGUI::IntPoint(0, static_cast(mScrollView->getViewOffset().top + _rel*0.3f))); } void SpellView::resetScrollbars() { mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); } } openmw-openmw-0.38.0/apps/openmw/mwgui/spellview.hpp000066400000000000000000000050751264522266000225510ustar00rootroot00000000000000#ifndef OPENMW_GUI_SPELLVIEW_H #define OPENMW_GUI_SPELLVIEW_H #include #include #include "spellmodel.hpp" namespace MyGUI { class ScrollView; } namespace MWGui { class SpellModel; ///@brief Displays a SpellModel in a list widget class SpellView : public MyGUI::Widget { MYGUI_RTTI_DERIVED(SpellView) public: SpellView(); /// Register needed components with MyGUI's factory manager static void registerComponents (); /// Should the cost/chance column be shown? void setShowCostColumn(bool show); void setHighlightSelected(bool highlight); /// Takes ownership of \a model void setModel (SpellModel* model); SpellModel* getModel(); void update(); /// simplified update called each frame void incrementalUpdate(); typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ModelIndex; /// Fired when a spell was clicked EventHandle_ModelIndex eventSpellClicked; virtual void initialiseOverride(); virtual void setSize(const MyGUI::IntSize& _value); virtual void setCoord(const MyGUI::IntCoord& _value); void resetScrollbars(); private: MyGUI::ScrollView* mScrollView; std::auto_ptr mModel; /// tracks a row in the spell view struct LineInfo { /// the widget on the left side of the row MyGUI::Widget* mLeftWidget; /// the widget on the left side of the row (if there is one) MyGUI::Widget* mRightWidget; /// index to item in mModel that row is showing information for SpellModel::ModelIndex mSpellIndex; LineInfo(MyGUI::Widget* leftWidget, MyGUI::Widget* rightWidget, SpellModel::ModelIndex spellIndex); }; /// magic number indicating LineInfo does not correspond to an item in mModel enum { NoSpellIndex = -1 }; std::vector< LineInfo > mLines; bool mShowCostColumn; bool mHighlightSelected; void layoutWidgets(); void addGroup(const std::string& label1, const std::string& label2); void adjustSpellWidget(const Spell& spell, SpellModel::ModelIndex index, MyGUI::Widget* widget); void onSpellSelected(MyGUI::Widget* _sender); void onMouseWheelMoved(MyGUI::Widget* _sender, int _rel); SpellModel::ModelIndex getSpellModelIndex(MyGUI::Widget* _sender); static const char* sSpellModelIndex; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/spellwindow.cpp000066400000000000000000000147331264522266000231020ustar00rootroot00000000000000#include "spellwindow.hpp" #include #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "spellicons.hpp" #include "confirmationdialog.hpp" #include "spellview.hpp" namespace MWGui { SpellWindow::SpellWindow(DragAndDrop* drag) : WindowPinnableBase("openmw_spell_window.layout") , NoDrop(drag, mMainWidget) , mSpellView(NULL) , mUpdateTimer(0.0f) { mSpellIcons = new SpellIcons(); getWidget(mSpellView, "SpellView"); getWidget(mEffectBox, "EffectsBox"); mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected); setCoord(498, 300, 302, 300); } SpellWindow::~SpellWindow() { delete mSpellIcons; } void SpellWindow::onPinToggled() { MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned); } void SpellWindow::onTitleDoubleClicked() { if (!mPinned) MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); } void SpellWindow::open() { updateSpells(); } void SpellWindow::onFrame(float dt) { if (mMainWidget->getVisible()) { NoDrop::onFrame(dt); mUpdateTimer += dt; if (0.5f < mUpdateTimer) { mUpdateTimer = 0; mSpellView->incrementalUpdate(); } } } void SpellWindow::updateSpells() { mSpellIcons->updateWidgets(mEffectBox, false); mSpellView->setModel(new SpellModel(MWMechanics::getPlayer())); } void SpellWindow::onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped) { MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); // retrieve ContainerStoreIterator to the item MWWorld::ContainerStoreIterator it = store.begin(); for (; it != store.end(); ++it) { if (*it == item) { break; } } if (it == store.end()) throw std::runtime_error("can't find selected item"); // equip, if it can be equipped and is not already equipped if (!alreadyEquipped && !item.getClass().getEquipmentSlots(item).first.empty()) { MWBase::Environment::get().getWindowManager()->useItem(item); // make sure that item was successfully equipped if (!store.isEquipped(item)) return; } store.setSelectedEnchantItem(it); // to reset WindowManager::mSelectedSpell immediately MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(*it); updateSpells(); } void SpellWindow::askDeleteSpell(const std::string &spellId) { // delete spell, if allowed const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellId); if (spell->mData.mFlags & ESM::Spell::F_Always || spell->mData.mType == ESM::Spell::ST_Power) { MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}"); } else { // ask for confirmation mSpellToDelete = spellId; ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); std::string question = MWBase::Environment::get().getWindowManager()->getGameSettingString("sQuestionDeleteSpell", "Delete %s?"); question = boost::str(boost::format(question) % spell->mName); dialog->askForConfirmation(question); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SpellWindow::onDeleteSpellAccept); dialog->eventCancelClicked.clear(); } } void SpellWindow::onModelIndexSelected(SpellModel::ModelIndex index) { const Spell& spell = mSpellView->getModel()->getItem(index); if (spell.mType == Spell::Type_EnchantedItem) { onEnchantedItemSelected(spell.mItem, spell.mActive); } else { if (MyGUI::InputManager::getInstance().isShiftPressed()) askDeleteSpell(spell.mId); else onSpellSelected(spell.mId); } } void SpellWindow::onSpellSelected(const std::string& spellId) { MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); store.setSelectedEnchantItem(store.end()); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); updateSpells(); } void SpellWindow::onDeleteSpellAccept() { MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); if (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == mSpellToDelete) MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); spells.remove(mSpellToDelete); updateSpells(); } void SpellWindow::cycle(bool next) { mSpellView->setModel(new SpellModel(MWMechanics::getPlayer())); SpellModel::ModelIndex selected = 0; for (SpellModel::ModelIndex i = 0; igetModel()->getItemCount()); ++i) { if (mSpellView->getModel()->getItem(i).mSelected) selected = i; } selected += next ? 1 : -1; int itemcount = mSpellView->getModel()->getItemCount(); if (itemcount == 0) return; selected = (selected + itemcount) % itemcount; const Spell& spell = mSpellView->getModel()->getItem(selected); if (spell.mType == Spell::Type_EnchantedItem) onEnchantedItemSelected(spell.mItem, spell.mActive); else onSpellSelected(spell.mId); } } openmw-openmw-0.38.0/apps/openmw/mwgui/spellwindow.hpp000066400000000000000000000021421264522266000230760ustar00rootroot00000000000000#ifndef MWGUI_SPELLWINDOW_H #define MWGUI_SPELLWINDOW_H #include "windowpinnablebase.hpp" #include "../mwworld/ptr.hpp" #include "spellmodel.hpp" namespace MWGui { class SpellIcons; class SpellView; class SpellWindow : public WindowPinnableBase, public NoDrop { public: SpellWindow(DragAndDrop* drag); virtual ~SpellWindow(); void updateSpells(); void onFrame(float dt); /// Cycle to next/previous spell void cycle(bool next); protected: MyGUI::Widget* mEffectBox; std::string mSpellToDelete; void onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped); void onSpellSelected(const std::string& spellId); void onModelIndexSelected(SpellModel::ModelIndex index); void onDeleteSpellAccept(); void askDeleteSpell(const std::string& spellId); virtual void onPinToggled(); virtual void onTitleDoubleClicked(); virtual void open(); SpellView* mSpellView; SpellIcons* mSpellIcons; private: float mUpdateTimer; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/statswindow.cpp000066400000000000000000000631471264522266000231240ustar00rootroot00000000000000#include "statswindow.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "tooltips.hpp" namespace MWGui { const int StatsWindow::sLineHeight = 18; StatsWindow::StatsWindow (DragAndDrop* drag) : WindowPinnableBase("openmw_stats_window.layout") , NoDrop(drag, mMainWidget) , mSkillView(NULL) , mMajorSkills() , mMinorSkills() , mMiscSkills() , mSkillValues() , mSkillWidgetMap() , mFactionWidgetMap() , mFactions() , mBirthSignId() , mReputation(0) , mBounty(0) , mSkillWidgets() , mChanged(true) { setCoord(0,0,498, 342); const char *names[][2] = { { "Attrib1", "sAttributeStrength" }, { "Attrib2", "sAttributeIntelligence" }, { "Attrib3", "sAttributeWillpower" }, { "Attrib4", "sAttributeAgility" }, { "Attrib5", "sAttributeSpeed" }, { "Attrib6", "sAttributeEndurance" }, { "Attrib7", "sAttributePersonality" }, { "Attrib8", "sAttributeLuck" }, { 0, 0 } }; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); for (int i=0; names[i][0]; ++i) { setText (names[i][0], store.get().find (names[i][1])->getString()); } getWidget(mSkillView, "SkillView"); getWidget(mLeftPane, "LeftPane"); getWidget(mRightPane, "RightPane"); for (int i = 0; i < ESM::Skill::Length; ++i) { mSkillValues.insert(std::pair(i, MWMechanics::SkillValue())); mSkillWidgetMap.insert(std::pair(i, (MyGUI::TextBox*)NULL)); } MyGUI::Window* t = mMainWidget->castType(); t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize); } void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (mSkillView->getViewOffset().top + _rel*0.3 > 0) mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); else mSkillView->setViewOffset(MyGUI::IntPoint(0, static_cast(mSkillView->getViewOffset().top + _rel*0.3))); } void StatsWindow::onWindowResize(MyGUI::Window* window) { mLeftPane->setCoord( MyGUI::IntCoord(0, 0, static_cast(0.44*window->getSize().width), window->getSize().height) ); mRightPane->setCoord( MyGUI::IntCoord(static_cast(0.44*window->getSize().width), 0, static_cast(0.56*window->getSize().width), window->getSize().height) ); // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), mSkillView->getCanvasSize().height); mSkillView->setVisibleVScroll(true); } void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) { MyGUI::ProgressBar* pt; getWidget(pt, name); pt->setProgressRange(max); pt->setProgressPosition(val); std::stringstream out; out << val << "/" << max; setText(tname, out.str().c_str()); } void StatsWindow::setPlayerName(const std::string& playerName) { mMainWidget->castType()->setCaption(playerName); } void StatsWindow::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { static const char *ids[] = { "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", "AttribVal6", "AttribVal7", "AttribVal8", 0 }; for (int i=0; ids[i]; ++i) if (ids[i]==id) { std::ostringstream valueString; valueString << value.getModified(); setText (id, valueString.str()); MyGUI::TextBox* box; getWidget(box, id); if (value.getModified()>value.getBase()) box->_setWidgetState("increased"); else if (value.getModified()_setWidgetState("decreased"); else box->_setWidgetState("normal"); break; } } void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { int current = std::max(0, static_cast(value.getCurrent())); int modified = static_cast(value.getModified()); setBar (id, id + "T", current, modified); // health, magicka, fatigue tooltip MyGUI::Widget* w; std::string valStr = MyGUI::utility::toString(current) + "/" + MyGUI::utility::toString(modified); if (id == "HBar") { getWidget(w, "Health"); w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); } else if (id == "MBar") { getWidget(w, "Magicka"); w->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr); } else if (id == "FBar") { getWidget(w, "Fatigue"); w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); } } void StatsWindow::setValue (const std::string& id, const std::string& value) { if (id=="name") setPlayerName (value); else if (id=="race") setText ("RaceText", value); else if (id=="class") setText ("ClassText", value); } void StatsWindow::setValue (const std::string& id, int value) { if (id=="level") { std::ostringstream text; text << value; setText("LevelText", text.str()); } } void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) { mSkillValues[parSkill] = value; MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; if (widget) { int modified = value.getModified(), base = value.getBase(); std::string text = MyGUI::utility::toString(modified); std::string state = "normal"; if (modified > base) state = "increased"; else if (modified < base) state = "decreased"; widget->setCaption(text); widget->_setWidgetState(state); } } void StatsWindow::configureSkills (const std::vector& major, const std::vector& minor) { mMajorSkills = major; mMinorSkills = minor; // Update misc skills with the remaining skills not in major or minor std::set skillSet; std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); mMiscSkills.clear(); for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) { int skill = *it; if (skillSet.find(skill) == skillSet.end()) mMiscSkills.push_back(skill); } updateSkillArea(); } void StatsWindow::onFrame (float dt) { if (!mMainWidget->getVisible()) return; NoDrop::onFrame(dt); MWWorld::Ptr player = MWMechanics::getPlayer(); const MWMechanics::NpcStats &PCstats = player.getClass().getNpcStats(player); // level progress MyGUI::Widget* levelWidget; for (int i=0; i<2; ++i) { int max = MWBase::Environment::get().getWorld()->getStore().get().find("iLevelUpTotal")->getInt(); getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); levelWidget->setUserString("RangePosition_LevelProgress", MyGUI::utility::toString(PCstats.getLevelProgress())); levelWidget->setUserString("Range_LevelProgress", MyGUI::utility::toString(max)); levelWidget->setUserString("Caption_LevelProgressText", MyGUI::utility::toString(PCstats.getLevelProgress()) + "/" + MyGUI::utility::toString(max)); } setFactions(PCstats.getFactionRanks()); setExpelled(PCstats.getExpelled ()); const std::string &signId = MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); setBirthSign(signId); setReputation (PCstats.getReputation ()); setBounty (PCstats.getBounty ()); if (mChanged) updateSkillArea(); } void StatsWindow::setFactions (const FactionList& factions) { if (mFactions != factions) { mFactions = factions; mChanged = true; } } void StatsWindow::setExpelled (const std::set& expelled) { if (mExpelled != expelled) { mExpelled = expelled; mChanged = true; } } void StatsWindow::setBirthSign (const std::string& signId) { if (signId != mBirthSignId) { mBirthSignId = signId; mChanged = true; } } void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); mSkillWidgets.push_back(separator); coord1.top += separator->getHeight(); coord2.top += separator->getHeight(); } void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); groupWidget->setCaption(label); groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); mSkillWidgets.push_back(groupWidget); coord1.top += sLineHeight; coord2.top += sLineHeight; } MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { MyGUI::TextBox *skillNameWidget, *skillValueWidget; skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); skillValueWidget->setCaption(value); skillValueWidget->_setWidgetState(state); skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); // resize dynamically according to text size int textWidthPlusMargin = skillValueWidget->getTextSize().width + 12; skillValueWidget->setCoord(coord2.left + coord2.width - textWidthPlusMargin, coord2.top, textWidthPlusMargin, coord2.height); skillNameWidget->setSize(skillNameWidget->getSize() + MyGUI::IntSize(coord2.width - textWidthPlusMargin, 0)); mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillValueWidget); coord1.top += sLineHeight; coord2.top += sLineHeight; return skillValueWidget; } MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { MyGUI::TextBox* skillNameWidget; skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); int textWidth = skillNameWidget->getTextSize().width; skillNameWidget->setSize(textWidth, skillNameWidget->getHeight()); mSkillWidgets.push_back(skillNameWidget); coord1.top += sLineHeight; coord2.top += sLineHeight; return skillNameWidget; } void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { // Add a line separator if there are items above if (!mSkillWidgets.empty()) { addSeparator(coord1, coord2); } addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); SkillList::const_iterator end = skills.end(); for (SkillList::const_iterator it = skills.begin(); it != end; ++it) { int skillId = *it; if (skillId < 0 || skillId >= ESM::Skill::Length) // Skip unknown skill indexes continue; const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; int base = stat.getBase(); int modified = stat.getModified(); MWWorld::Ptr player = MWMechanics::getPlayer(); const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); float progressRequirement = player.getClass().getNpcStats(player).getSkillProgressRequirement(skillId, *esmStore.get().find(player.get()->mBase->mClass)); // This is how vanilla MW displays the progress bar (I think). Note it's slightly inaccurate, // due to the int casting in the skill levelup logic. Also the progress label could in rare cases // reach 100% without the skill levelling up. // Leaving the original display logic for now, for consistency with ess-imported savegames. int progressPercent = int(float(stat.getProgress()) / float(progressRequirement) * 100.f + 0.5f); const ESM::Skill* skill = esmStore.get().find(skillId); std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; const ESM::Attribute* attr = esmStore.get().find(skill->mData.mAttribute); std::string state = "normal"; if (modified > base) state = "increased"; else if (modified < base) state = "decreased"; MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), MyGUI::utility::toString(static_cast(modified)), state, coord1, coord2); for (int i=0; i<2; ++i) { mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "SkillToolTip"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillName", "#{"+skillNameId+"}"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillDescription", skill->mDescription); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ImageTexture_SkillImage", icon); if (base < 100) { mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Visible_SkillMaxed", "false"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("UserData^Hidden_SkillMaxed", "true"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Visible_SkillProgressVBox", "true"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("UserData^Hidden_SkillProgressVBox", "false"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", MyGUI::utility::toString(progressPercent)+"/100"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", MyGUI::utility::toString(progressPercent)); } else { mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Visible_SkillMaxed", "true"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("UserData^Hidden_SkillMaxed", "false"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Visible_SkillProgressVBox", "false"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("UserData^Hidden_SkillProgressVBox", "true"); } } mSkillWidgetMap[skillId] = widget; } } void StatsWindow::updateSkillArea() { mChanged = false; for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } mSkillWidgets.clear(); const int valueSize = 40; MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); if (!mMajorSkills.empty()) addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); if (!mMinorSkills.empty()) addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); if (!mMiscSkills.empty()) addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::ESMStore &store = world->getStore(); const ESM::NPC *player = world->getPlayerPtr().get()->mBase; // race tooltip const ESM::Race* playerRace = store.get().find(player->mRace); MyGUI::Widget* raceWidget; getWidget(raceWidget, "RaceText"); ToolTips::createRaceToolTip(raceWidget, playerRace); getWidget(raceWidget, "Race_str"); ToolTips::createRaceToolTip(raceWidget, playerRace); // class tooltip MyGUI::Widget* classWidget; const ESM::Class *playerClass = store.get().find(player->mClass); getWidget(classWidget, "ClassText"); ToolTips::createClassToolTip(classWidget, *playerClass); getWidget(classWidget, "Class_str"); ToolTips::createClassToolTip(classWidget, *playerClass); if (!mFactions.empty()) { MWWorld::Ptr player = MWMechanics::getPlayer(); const MWMechanics::NpcStats &PCstats = player.getClass().getNpcStats(player); const std::set &expelled = PCstats.getExpelled(); bool firstFaction=true; FactionList::const_iterator end = mFactions.end(); for (FactionList::const_iterator it = mFactions.begin(); it != end; ++it) { const ESM::Faction *faction = store.get().find(it->first); if (faction->mData.mIsHidden == 1) continue; if (firstFaction) { // Add a line separator if there are items above if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFaction", "Faction"), coord1, coord2); firstFaction = false; } MyGUI::Widget* w = addItem(faction->mName, coord1, coord2); std::string text; text += std::string("#{fontcolourhtml=header}") + faction->mName; if (expelled.find(it->first) != expelled.end()) text += "\n#{fontcolourhtml=normal}#{sExpelled}"; else { int rank = it->second; rank = std::max(0, std::min(9, rank)); text += std::string("\n#{fontcolourhtml=normal}") + faction->mRanks[rank]; if (rank < 9) { // player doesn't have max rank yet text += std::string("\n\n#{fontcolourhtml=header}#{sNextRank} ") + faction->mRanks[rank+1]; ESM::RankData rankData = faction->mData.mRankData[rank+1]; const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); text += "\n#{fontcolourhtml=normal}#{" + attr1->mName + "}: " + MyGUI::utility::toString(rankData.mAttribute1) + ", #{" + attr2->mName + "}: " + MyGUI::utility::toString(rankData.mAttribute2); text += "\n\n#{fontcolourhtml=header}#{sFavoriteSkills}"; text += "\n#{fontcolourhtml=normal}"; bool firstSkill = true; for (int i=0; i<7; ++i) { if (faction->mData.mSkills[i] != -1) { if (!firstSkill) text += ", "; firstSkill = false; text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkills[i]]+"}"; } } text += "\n"; if (rankData.mSkill1 > 0) text += "\n#{sNeedOneSkill} " + MyGUI::utility::toString(rankData.mSkill1); if (rankData.mSkill2 > 0) text += "\n#{sNeedTwoSkills} " + MyGUI::utility::toString(rankData.mSkill2); } } w->setUserString("ToolTipType", "Layout"); w->setUserString("ToolTipLayout", "TextToolTip"); w->setUserString("Caption_Text", text); } } if (!mBirthSignId.empty()) { // Add a line separator if there are items above if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBirthSign", "Sign"), coord1, coord2); const ESM::BirthSign *sign = store.get().find(mBirthSignId); MyGUI::Widget* w = addItem(sign->mName, coord1, coord2); ToolTips::createBirthsignToolTip(w, mBirthSignId); } // Add a line separator if there are items above if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sReputation", "Reputation"), MyGUI::utility::toString(static_cast(mReputation)), "normal", coord1, coord2); for (int i=0; i<2; ++i) { mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); } addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBounty", "Bounty"), MyGUI::utility::toString(static_cast(mBounty)), "normal", coord1, coord2); for (int i=0; i<2; ++i) { mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); mSkillView->setVisibleVScroll(true); } void StatsWindow::onPinToggled() { MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned); } void StatsWindow::onTitleDoubleClicked() { if (!mPinned) MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); } } openmw-openmw-0.38.0/apps/openmw/mwgui/statswindow.hpp000066400000000000000000000066671264522266000231350ustar00rootroot00000000000000#ifndef MWGUI_STATS_WINDOW_H #define MWGUI_STATS_WINDOW_H #include "../mwmechanics/stat.hpp" #include "windowpinnablebase.hpp" #include namespace MWGui { class WindowManager; class StatsWindow : public WindowPinnableBase, public NoDrop { public: typedef std::map FactionList; typedef std::vector SkillList; StatsWindow(DragAndDrop* drag); /// automatically updates all the data in the stats window, but only if it has changed. void onFrame(float dt); void setBar(const std::string& name, const std::string& tname, int val, int max); void setPlayerName(const std::string& playerName); /// Set value for the given ID. void setValue (const std::string& id, const MWMechanics::AttributeValue& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const std::string& value); void setValue (const std::string& id, int value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); void configureSkills (const SkillList& major, const SkillList& minor); void setReputation (int reputation) { if (reputation != mReputation) mChanged = true; this->mReputation = reputation; } void setBounty (int bounty) { if (bounty != mBounty) mChanged = true; this->mBounty = bounty; } void updateSkillArea(); virtual void open() { onWindowResize(mMainWidget->castType()); } private: void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); MyGUI::TextBox* addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); MyGUI::Widget* addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void setFactions (const FactionList& factions); void setExpelled (const std::set& expelled); void setBirthSign (const std::string &signId); void onWindowResize(MyGUI::Window* window); void onMouseWheel(MyGUI::Widget* _sender, int _rel); static const int sLineHeight; MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; MyGUI::ScrollView* mSkillView; SkillList mMajorSkills, mMinorSkills, mMiscSkills; std::map mSkillValues; std::map mSkillWidgetMap; std::map mFactionWidgetMap; FactionList mFactions; ///< Stores a list of factions and the current rank std::string mBirthSignId; int mReputation, mBounty; std::vector mSkillWidgets; //< Skills and other information std::set mExpelled; bool mChanged; protected: virtual void onPinToggled(); virtual void onTitleDoubleClicked(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/textinput.cpp000066400000000000000000000042351264522266000225730ustar00rootroot00000000000000#include "textinput.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include #include namespace MWGui { TextInputDialog::TextInputDialog() : WindowModal("openmw_text_input.layout") { // Centre dialog center(); getWidget(mTextEdit, "TextEdit"); mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } void TextInputDialog::setNextButtonShow(bool shown) { MyGUI::Button* okButton; getWidget(okButton, "OKButton"); if (shown) okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); else okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); } void TextInputDialog::setTextLabel(const std::string &label) { setText("LabelT", label); } void TextInputDialog::open() { WindowModal::open(); // Make sure the edit box has focus MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTextEdit); } // widget controls void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) { if (mTextEdit->getCaption() == "") { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget (mTextEdit); } else eventDone(this); } void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) { onOkClicked(_sender); } std::string TextInputDialog::getTextInput() const { return mTextEdit->getCaption(); } void TextInputDialog::setTextInput(const std::string &text) { mTextEdit->setCaption(text); } } openmw-openmw-0.38.0/apps/openmw/mwgui/textinput.hpp000066400000000000000000000014321264522266000225740ustar00rootroot00000000000000#ifndef MWGUI_TEXT_INPUT_H #define MWGUI_TEXT_INPUT_H #include "windowbase.hpp" namespace MWGui { class WindowManager; } namespace MWGui { class TextInputDialog : public WindowModal { public: TextInputDialog(); std::string getTextInput() const; void setTextInput(const std::string &text); void setNextButtonShow(bool shown); void setTextLabel(const std::string &label); virtual void open(); /** Event : Dialog finished, OK button clicked.\n signature : void method()\n */ EventHandle_WindowBase eventDone; protected: void onOkClicked(MyGUI::Widget* _sender); void onTextAccepted(MyGUI::Edit* _sender); private: MyGUI::EditBox* mTextEdit; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/timeadvancer.cpp000066400000000000000000000025041264522266000231660ustar00rootroot00000000000000#include "timeadvancer.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" namespace MWGui { TimeAdvancer::TimeAdvancer(float delay) : mRunning(false), mCurHour(0), mHours(1), mInterruptAt(-1), mDelay(delay), mRemainingTime(delay) { } void TimeAdvancer::run(int hours, int interruptAt) { mHours = hours; mCurHour = 0; mInterruptAt = interruptAt; mRemainingTime = mDelay; mRunning = true; } void TimeAdvancer::stop() { mRunning = false; } void TimeAdvancer::onFrame(float dt) { if (!mRunning) return; if (mCurHour == mInterruptAt) { stop(); eventInterrupted(); return; } mRemainingTime -= dt; while (mRemainingTime <= 0) { mRemainingTime += mDelay; ++mCurHour; if (mCurHour <= mHours) eventProgressChanged(mCurHour, mHours); else { stop(); eventFinished(); return; } } } int TimeAdvancer::getHours() { return mHours; } bool TimeAdvancer::isRunning() { return mRunning; } } openmw-openmw-0.38.0/apps/openmw/mwgui/timeadvancer.hpp000066400000000000000000000015731264522266000232000ustar00rootroot00000000000000#ifndef MWGUI_TIMEADVANCER_H #define MWGUI_TIMEADVANCER_H #include namespace MWGui { class TimeAdvancer { public: TimeAdvancer(float delay); void run(int hours, int interruptAt=-1); void stop(); void onFrame(float dt); int getHours(); bool isRunning(); // signals typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate2 EventHandle_IntInt; EventHandle_IntInt eventProgressChanged; EventHandle_Void eventInterrupted; EventHandle_Void eventFinished; private: bool mRunning; int mCurHour; int mHours; int mInterruptAt; float mDelay; float mRemainingTime; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/tooltips.cpp000066400000000000000000001034371264522266000224100ustar00rootroot00000000000000#include "tooltips.hpp" #include #include #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/actorutil.hpp" #include "mapwindow.hpp" #include "inventorywindow.hpp" #include "itemmodel.hpp" namespace MWGui { std::string ToolTips::sSchoolNames[] = {"#{sSchoolAlteration}", "#{sSchoolConjuration}", "#{sSchoolDestruction}", "#{sSchoolIllusion}", "#{sSchoolMysticism}", "#{sSchoolRestoration}"}; ToolTips::ToolTips() : Layout("openmw_tooltips.layout") , mFocusToolTipX(0.0) , mFocusToolTipY(0.0) , mHorizontalScrollIndex(0) , mDelay(0.0) , mRemainingDelay(0.0) , mLastMouseX(0) , mLastMouseY(0) , mEnabled(true) , mFullHelp(false) , mShowOwned(0) { getWidget(mDynamicToolTipBox, "DynamicToolTipBox"); mDynamicToolTipBox->setVisible(false); // turn off mouse focus so that getMouseFocusWidget returns the correct widget, // even if the mouse is over the tooltip mDynamicToolTipBox->setNeedMouseFocus(false); mMainWidget->setNeedMouseFocus(false); mDelay = Settings::Manager::getFloat("tooltip delay", "GUI"); mRemainingDelay = mDelay; for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) { mMainWidget->getChildAt(i)->setVisible(false); } mShowOwned = Settings::Manager::getInt("show owned", "Game"); } void ToolTips::setEnabled(bool enabled) { mEnabled = enabled; } void ToolTips::onFrame(float frameDuration) { while (mDynamicToolTipBox->getChildCount()) { MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); } // start by hiding everything for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) { mMainWidget->getChildAt(i)->setVisible(false); } const MyGUI::IntSize &viewSize = MyGUI::RenderManager::getInstance().getViewSize(); if (!mEnabled) { return; } bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); if (guiMode) { const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition(); if (MWBase::Environment::get().getWindowManager()->getWorldMouseOver() && ((MWBase::Environment::get().getWindowManager()->getMode() == GM_Console) || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Container) || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Inventory))) { if (mFocusObject.isEmpty ()) return; const MWWorld::Class& objectclass = mFocusObject.getClass(); MyGUI::IntSize tooltipSize; if ((!objectclass.hasToolTip(mFocusObject))&&(MWBase::Environment::get().getWindowManager()->getMode() == GM_Console)) { setCoord(0, 0, 300, 300); mDynamicToolTipBox->setVisible(true); ToolTipInfo info; info.caption = mFocusObject.getClass().getName(mFocusObject); if (info.caption.empty()) info.caption=mFocusObject.getCellRef().getRefId(); info.icon=""; tooltipSize = createToolTip(info, true); } else tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true); MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition(); position(tooltipPosition, tooltipSize, viewSize); setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); } else { if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY) { mRemainingDelay -= frameDuration; } else { mHorizontalScrollIndex = 0; mRemainingDelay = mDelay; } mLastMouseX = mousePos.left; mLastMouseY = mousePos.top; if (mRemainingDelay > 0) return; MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); if (focus == 0) return; MyGUI::IntSize tooltipSize; // try to go 1 level up until there is a widget that has tooltip // this is necessary because some skin elements are actually separate widgets int i=0; while (!focus->isUserString("ToolTipType")) { focus = focus->getParent(); if (!focus) return; ++i; } std::string type = focus->getUserString("ToolTipType"); if (type == "") { return; } // special handling for markers on the local map: the tooltip should only be visible // if the marker is not hidden due to the fog of war. if (type == "MapMarker") { LocalMapBase::MarkerUserData data = *focus->getUserData(); if (!data.isPositionExplored()) return; ToolTipInfo info; info.text = data.caption; info.notes = data.notes; tooltipSize = createToolTip(info, false); } else if (type == "ItemPtr") { mFocusObject = *focus->getUserData(); tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false); } else if (type == "ItemModelIndex") { std::pair pair = *focus->getUserData >(); mFocusObject = pair.second->getItem(pair.first).mBase; tooltipSize = getToolTipViaPtr(pair.second->getItem(pair.first).mCount, false); } else if (type == "ToolTipInfo") { tooltipSize = createToolTip(*focus->getUserData(), false); } else if (type == "AvatarItemSelection") { MyGUI::IntCoord avatarPos = focus->getAbsoluteCoord(); MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top); MWWorld::Ptr item = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarSelectedItem (relMousePos.left, relMousePos.top); mFocusObject = item; if (!mFocusObject.isEmpty ()) tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false); } else if (type == "Spell") { ToolTipInfo info; const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find(focus->getUserString("Spell")); info.caption = spell->mName; Widgets::SpellEffectList effects; std::vector::const_iterator end = spell->mEffects.mList.end(); for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) { Widgets::SpellEffectParams params; params.mEffectID = it->mEffectID; params.mSkill = it->mSkill; params.mAttribute = it->mAttribute; params.mDuration = it->mDuration; params.mMagnMin = it->mMagnMin; params.mMagnMax = it->mMagnMax; params.mRange = it->mRange; params.mArea = it->mArea; params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability); params.mNoTarget = false; effects.push_back(params); } if (MWMechanics::spellIncreasesSkill(spell)) // display school of spells that contribute to skill progress { MWWorld::Ptr player = MWMechanics::getPlayer(); int school = MWMechanics::getSpellSchool(spell, player); info.text = "#{sSchool}: " + sSchoolNames[school]; } info.effects = effects; tooltipSize = createToolTip(info, false); } else if (type == "Layout") { // tooltip defined in the layout MyGUI::Widget* tooltip; getWidget(tooltip, focus->getUserString("ToolTipLayout")); tooltip->setVisible(true); std::map userStrings = focus->getUserStrings(); for (std::map::iterator it = userStrings.begin(); it != userStrings.end(); ++it) { size_t underscorePos = it->first.find("_"); if (underscorePos == std::string::npos) continue; std::string key = it->first.substr(0, underscorePos); std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); std::string type = "Property"; size_t caretPos = key.find("^"); if (caretPos != std::string::npos) { type = key.substr(0, caretPos); key.erase(key.begin(), key.begin() + caretPos + 1); } MyGUI::Widget* w; getWidget(w, widgetName); if (type == "Property") w->setProperty(key, it->second); else if (type == "UserData") w->setUserString(key, it->second); } tooltipSize = tooltip->getSize(); tooltip->setCoord(0, 0, tooltipSize.width, tooltipSize.height); } else throw std::runtime_error ("unknown tooltip type"); MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition(); position(tooltipPosition, tooltipSize, viewSize); setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); } } else { if (!mFocusObject.isEmpty()) { MyGUI::IntSize tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount()); setCoord(viewSize.width/2 - tooltipSize.width/2, std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), tooltipSize.width, tooltipSize.height); mDynamicToolTipBox->setVisible(true); } } } void ToolTips::position(MyGUI::IntPoint& position, MyGUI::IntSize size, MyGUI::IntSize viewportSize) { position += MyGUI::IntPoint(0, 32) - MyGUI::IntPoint(static_cast(MyGUI::InputManager::getInstance().getMousePosition().left / float(viewportSize.width) * size.width), 0); if ((position.left + size.width) > viewportSize.width) { position.left = viewportSize.width - size.width; } if ((position.top + size.height) > viewportSize.height) { position.top = MyGUI::InputManager::getInstance().getMousePosition().top - size.height - 8; } } void ToolTips::setFocusObject(const MWWorld::ConstPtr& focus) { mFocusObject = focus; } MyGUI::IntSize ToolTips::getToolTipViaPtr (int count, bool image) { // this the maximum width of the tooltip before it starts word-wrapping setCoord(0, 0, 300, 300); MyGUI::IntSize tooltipSize; const MWWorld::Class& object = mFocusObject.getClass(); if (!object.hasToolTip(mFocusObject)) { mDynamicToolTipBox->setVisible(false); } else { mDynamicToolTipBox->setVisible(true); ToolTipInfo info = object.getToolTipInfo(mFocusObject, count); if (!image) info.icon = ""; tooltipSize = createToolTip(info, true); } return tooltipSize; } bool ToolTips::checkOwned() { if(!mFocusObject.isEmpty()) { const MWWorld::CellRef& cellref = mFocusObject.getCellRef(); MWWorld::Ptr ptr = MWMechanics::getPlayer(); MWWorld::Ptr victim; MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager(); bool allowed = mm->isAllowedToUse(ptr, cellref, victim); return !allowed; } else { return false; } } MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info, bool isFocusObject) { mDynamicToolTipBox->setVisible(true); if(mShowOwned == 1 || mShowOwned == 3) { if(isFocusObject && checkOwned()) { mDynamicToolTipBox->changeWidgetSkin("HUD_Box_NoTransp_Owned"); } else { mDynamicToolTipBox->changeWidgetSkin("HUD_Box_NoTransp"); } } std::string caption = info.caption; std::string image = info.icon; int imageSize = (image != "") ? info.imageSize : 0; std::string text = info.text; // remove the first newline (easier this way) if (text.size() > 0 && text[0] == '\n') text.erase(0, 1); const ESM::Enchantment* enchant = 0; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); if (info.enchant != "") { enchant = store.get().find(info.enchant); if (enchant->mData.mType == ESM::Enchantment::CastOnce) text += "\n#{sItemCastOnce}"; else if (enchant->mData.mType == ESM::Enchantment::WhenStrikes) text += "\n#{sItemCastWhenStrikes}"; else if (enchant->mData.mType == ESM::Enchantment::WhenUsed) text += "\n#{sItemCastWhenUsed}"; else if (enchant->mData.mType == ESM::Enchantment::ConstantEffect) text += "\n#{sItemCastConstant}"; } // this the maximum width of the tooltip before it starts word-wrapping setCoord(0, 0, 300, 300); const MyGUI::IntPoint padding(8, 8); const int maximumWidth = 500; const int imageCaptionHPadding = (caption != "" ? 8 : 0); const int imageCaptionVPadding = (caption != "" ? 4 : 0); std::string realImage = MWBase::Environment::get().getWindowManager()->correctIconPath(image); MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); captionWidget->setProperty("Static", "true"); captionWidget->setCaptionWithReplacing(caption); MyGUI::IntSize captionSize = captionWidget->getTextSize(); int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); textWidget->setProperty("Static", "true"); textWidget->setProperty("MultiLine", "true"); textWidget->setProperty("WordWrap", info.wordWrap ? "true" : "false"); textWidget->setCaptionWithReplacing(text); textWidget->setTextAlign(MyGUI::Align::HCenter | MyGUI::Align::Top); MyGUI::IntSize textSize = textWidget->getTextSize(); captionSize += MyGUI::IntSize(imageSize, 0); // adjust for image MyGUI::IntSize totalSize = MyGUI::IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth), ((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight ); for (std::vector::const_iterator it = info.notes.begin(); it != info.notes.end(); ++it) { MyGUI::ImageBox* icon = mDynamicToolTipBox->createWidget("MarkerButton", MyGUI::IntCoord(padding.left, totalSize.height+padding.top, 8, 8), MyGUI::Align::Default); icon->setColour(MyGUI::Colour(1.0f, 0.3f, 0.3f)); MyGUI::EditBox* edit = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(padding.left+8+4, totalSize.height+padding.top, 300-padding.left-8-4, 300-totalSize.height), MyGUI::Align::Default); edit->setEditMultiLine(true); edit->setEditWordWrap(true); edit->setCaption(*it); edit->setSize(edit->getWidth(), edit->getTextSize().height); icon->setPosition(icon->getLeft(),(edit->getTop()+edit->getBottom())/2-icon->getHeight()/2); totalSize.height += std::max(edit->getHeight(), icon->getHeight()); totalSize.width = std::max(totalSize.width, edit->getWidth()+8+4); } if (!info.effects.empty()) { MyGUI::Widget* effectArea = mDynamicToolTipBox->createWidget("", MyGUI::IntCoord(padding.left, totalSize.height, 300-padding.left, 300-totalSize.height), MyGUI::Align::Stretch); MyGUI::IntCoord coord(0, 6, totalSize.width, 24); Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget ("MW_StatName", coord, MyGUI::Align::Default); effectsWidget->setEffectList(info.effects); std::vector effectItems; effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0); totalSize.height += coord.top-6; totalSize.width = std::max(totalSize.width, coord.width); } if (info.enchant != "") { assert(enchant); MyGUI::Widget* enchantArea = mDynamicToolTipBox->createWidget("", MyGUI::IntCoord(padding.left, totalSize.height, 300-padding.left, 300-totalSize.height), MyGUI::Align::Stretch); MyGUI::IntCoord coord(0, 6, totalSize.width, 24); Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget ("MW_StatName", coord, MyGUI::Align::Default); enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); std::vector enchantEffectItems; int flag = (enchant->mData.mType == ESM::Enchantment::ConstantEffect) ? Widgets::MWEffectList::EF_Constant : 0; enchantWidget->createEffectWidgets(enchantEffectItems, enchantArea, coord, true, flag); totalSize.height += coord.top-6; totalSize.width = std::max(totalSize.width, coord.width); if (enchant->mData.mType == ESM::Enchantment::WhenStrikes || enchant->mData.mType == ESM::Enchantment::WhenUsed) { int maxCharge = enchant->mData.mCharge; int charge = (info.remainingEnchantCharge == -1) ? maxCharge : info.remainingEnchantCharge; const int chargeWidth = 204; MyGUI::TextBox* chargeText = enchantArea->createWidget("SandText", MyGUI::IntCoord(0, 0, 10, 18), MyGUI::Align::Default, "ToolTipEnchantChargeText"); chargeText->setCaptionWithReplacing("#{sCharges}"); const int chargeTextWidth = chargeText->getTextSize().width + 5; const int chargeAndTextWidth = chargeWidth + chargeTextWidth; totalSize.width = std::max(totalSize.width, chargeAndTextWidth); chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); MyGUI::IntCoord chargeCoord; if (totalSize.width < chargeWidth) { totalSize.width = chargeWidth; chargeCoord = MyGUI::IntCoord(0, coord.top+6, chargeWidth, 18); } else { chargeCoord = MyGUI::IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18); } Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget ("MW_ChargeBar", chargeCoord, MyGUI::Align::Default); chargeWidget->setValue(charge, maxCharge); totalSize.height += 24; } } captionWidget->setCoord( (totalSize.width - captionSize.width)/2 + imageSize, (captionHeight-captionSize.height)/2, captionSize.width-imageSize, captionSize.height); //if its too long we do hscroll with the caption if (captionSize.width > maximumWidth) { mHorizontalScrollIndex = mHorizontalScrollIndex + 2; if (mHorizontalScrollIndex > captionSize.width){ mHorizontalScrollIndex = -totalSize.width; } int horizontal_scroll = mHorizontalScrollIndex; if (horizontal_scroll < 40){ horizontal_scroll = 40; }else{ horizontal_scroll = 80 - mHorizontalScrollIndex; } captionWidget->setPosition (MyGUI::IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); } else { captionWidget->setPosition (captionWidget->getPosition() + padding); } textWidget->setPosition (textWidget->getPosition() + MyGUI::IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter if (image != "") { MyGUI::ImageBox* imageWidget = mDynamicToolTipBox->createWidget("ImageBox", MyGUI::IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize), MyGUI::Align::Left | MyGUI::Align::Top); imageWidget->setImageTexture(realImage); imageWidget->setPosition (imageWidget->getPosition() + padding); } totalSize += MyGUI::IntSize(padding.left*2, padding.top*2); return totalSize; } std::string ToolTips::toString(const float value) { std::ostringstream stream; if (value != int(value)) stream << std::setprecision(3); stream << value; return stream.str(); } std::string ToolTips::toString(const int value) { std::ostringstream stream; stream << value; return stream.str(); } std::string ToolTips::getValueString(const int value, const std::string& prefix) { if (value == 0) return ""; else return "\n" + prefix + ": " + toString(value); } std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) { if (text == "") return ""; else return "\n" + prefix + ": " + text; } std::string ToolTips::getCountString(const int value) { if (value == 1) return ""; else return " (" + MyGUI::utility::toString(value) + ")"; } std::string ToolTips::getCellRefString(const MWWorld::CellRef& cellref) { std::string ret; ret += getMiscString(cellref.getOwner(), "Owner"); ret += getMiscString(cellref.getFaction(), "Faction"); if (cellref.getFactionRank() > 0) ret += getValueString(cellref.getFactionRank(), "Rank"); std::vector > itemOwners = MWBase::Environment::get().getMechanicsManager()->getStolenItemOwners(cellref.getRefId()); for (std::vector >::const_iterator it = itemOwners.begin(); it != itemOwners.end(); ++it) { if (it->second == std::numeric_limits::max()) ret += std::string("\nStolen from ") + it->first; // for legacy (ESS) savegames else ret += std::string("\nStolen ") + MyGUI::utility::toString(it->second) + " from " + it->first; } ret += getMiscString(cellref.getGlobalVariable(), "Global"); return ret; } bool ToolTips::toggleFullHelp() { mFullHelp = !mFullHelp; return mFullHelp; } bool ToolTips::getFullHelp() const { return mFullHelp; } void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) { mFocusToolTipX = (min_x + max_x) / 2; mFocusToolTipY = min_y; } void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId) { if (skillId == -1) return; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const ESM::Skill* skill = store.get().find(skillId); assert(skill); const ESM::Attribute* attr = store.get().find(skill->mData.mAttribute); assert(attr); std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipLayout", "SkillNoProgressToolTip"); widget->setUserString("Caption_SkillNoProgressName", "#{"+skillNameId+"}"); widget->setUserString("Caption_SkillNoProgressDescription", skill->mDescription); widget->setUserString("Caption_SkillNoProgressAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); widget->setUserString("ImageTexture_SkillNoProgressImage", icon); } void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) { if (attributeId == -1) return; std::string icon = ESM::Attribute::sAttributeIcons[attributeId]; std::string name = ESM::Attribute::sGmstAttributeIds[attributeId]; std::string desc = ESM::Attribute::sGmstAttributeDescIds[attributeId]; widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipLayout", "AttributeToolTip"); widget->setUserString("Caption_AttributeName", "#{"+name+"}"); widget->setUserString("Caption_AttributeDescription", "#{"+desc+"}"); widget->setUserString("ImageTexture_AttributeImage", icon); } void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId) { widget->setUserString("Caption_CenteredCaption", name); std::string specText; // get all skills of this specialisation const MWWorld::Store &skills = MWBase::Environment::get().getWorld()->getStore().get(); MWWorld::Store::iterator it = skills.begin(); for (; it != skills.end(); ++it) { if (it->second.mData.mSpecialization == specId) specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->first] + "}"; } widget->setUserString("Caption_CenteredCaptionText", specText); widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); widget->setUserString("ToolTipType", "Layout"); } void ToolTips::createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::BirthSign *sign = store.get().find(birthsignId); widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipLayout", "BirthSignToolTip"); widget->setUserString("ImageTexture_BirthSignImage", MWBase::Environment::get().getWindowManager()->correctTexturePath(sign->mTexture)); std::string text; text += sign->mName; text += "\n#{fontcolourhtml=normal}" + sign->mDescription; std::vector abilities, powers, spells; std::vector::const_iterator it = sign->mPowers.mList.begin(); std::vector::const_iterator end = sign->mPowers.mList.end(); for (; it != end; ++it) { const std::string &spellId = *it; const ESM::Spell *spell = store.get().search(spellId); if (!spell) continue; // Skip spells which cannot be found ESM::Spell::SpellType type = static_cast(spell->mData.mType); if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) continue; // We only want spell, ability and powers. if (type == ESM::Spell::ST_Ability) abilities.push_back(spellId); else if (type == ESM::Spell::ST_Power) powers.push_back(spellId); else if (type == ESM::Spell::ST_Spell) spells.push_back(spellId); } struct { const std::vector &spells; std::string label; } categories[3] = { {abilities, "sBirthsignmenu1"}, {powers, "sPowers"}, {spells, "sBirthsignmenu2"} }; for (int category = 0; category < 3; ++category) { for (std::vector::const_iterator it = categories[category].spells.begin(); it != categories[category].spells.end(); ++it) { if (it == categories[category].spells.begin()) { text += std::string("\n#{fontcolourhtml=header}") + std::string("#{") + categories[category].label + "}"; } const std::string &spellId = *it; const ESM::Spell *spell = store.get().find(spellId); text += "\n#{fontcolourhtml=normal}" + spell->mName; } } widget->setUserString("Caption_BirthSignText", text); } void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace) { widget->setUserString("Caption_CenteredCaption", playerRace->mName); widget->setUserString("Caption_CenteredCaptionText", playerRace->mDescription); widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); } void ToolTips::createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass) { if (playerClass.mName == "") return; int spec = playerClass.mData.mSpecialization; std::string specStr; if (spec == 0) specStr = "#{sSpecializationCombat}"; else if (spec == 1) specStr = "#{sSpecializationMagic}"; else if (spec == 2) specStr = "#{sSpecializationStealth}"; widget->setUserString("Caption_ClassName", playerClass.mName); widget->setUserString("Caption_ClassDescription", playerClass.mDescription); widget->setUserString("Caption_ClassSpecialisation", "#{sSpecialization}: " + specStr); widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipLayout", "ClassToolTip"); } void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id) { const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld ()->getStore ().get().find(id); const std::string &name = ESM::MagicEffect::effectIdToString (id); std::string icon = effect->mIcon; int slashPos = icon.rfind('\\'); icon.insert(slashPos+1, "b_"); icon = MWBase::Environment::get().getWindowManager()->correctIconPath(icon); widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); widget->setUserString("Caption_MagicEffectName", "#{" + name + "}"); widget->setUserString("Caption_MagicEffectDescription", effect->mDescription); widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + sSchoolNames[effect->mData.mSchool]); widget->setUserString("ImageTexture_MagicEffectImage", icon); } void ToolTips::setDelay(float delay) { mDelay = delay; mRemainingDelay = mDelay; } } openmw-openmw-0.38.0/apps/openmw/mwgui/tooltips.hpp000066400000000000000000000100741264522266000224070ustar00rootroot00000000000000#ifndef MWGUI_TOOLTIPS_H #define MWGUI_TOOLTIPS_H #include "layout.hpp" #include "../mwworld/ptr.hpp" #include "widgets.hpp" namespace ESM { struct Class; struct Race; } namespace MWGui { // Info about tooltip that is supplied by the MWWorld::Class object struct ToolTipInfo { public: ToolTipInfo() : imageSize(32) , remainingEnchantCharge(-1) , isPotion(false) , wordWrap(true) {} std::string caption; std::string text; std::string icon; int imageSize; // enchantment (for cloth, armor, weapons) std::string enchant; int remainingEnchantCharge; // effects (for potions, ingredients) Widgets::SpellEffectList effects; // local map notes std::vector notes; bool isPotion; // potions do not show target in the tooltip bool wordWrap; }; class ToolTips : public Layout { public: ToolTips(); void onFrame(float frameDuration); void setEnabled(bool enabled); bool toggleFullHelp(); ///< show extra info in item tooltips (owner, script) bool getFullHelp() const; void setDelay(float delay); void setFocusObject(const MWWorld::ConstPtr& focus); void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); ///< set the screen-space position of the tooltip for focused object static std::string getValueString(const int value, const std::string& prefix); ///< @return "prefix: value" or "" if value is 0 static std::string getMiscString(const std::string& text, const std::string& prefix); ///< @return "prefix: text" or "" if text is empty static std::string toString(const float value); static std::string toString(const int value); static std::string getCountString(const int value); ///< @return blank string if count is 1, or else " (value)" static std::string getCellRefString(const MWWorld::CellRef& cellref); ///< Returns a string containing debug tooltip information about the given cellref. // these do not create an actual tooltip, but they fill in the data that is required so the tooltip // system knows what to show in case this widget is hovered static void createSkillToolTip(MyGUI::Widget* widget, int skillId); static void createAttributeToolTip(MyGUI::Widget* widget, int attributeId); static void createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId); static void createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId); static void createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace); static void createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass); static void createMagicEffectToolTip(MyGUI::Widget* widget, short id); bool checkOwned(); /// Returns True if taking mFocusObject would be crime private: MyGUI::Widget* mDynamicToolTipBox; MWWorld::ConstPtr mFocusObject; MyGUI::IntSize getToolTipViaPtr (int count, bool image=true); ///< @return requested tooltip size MyGUI::IntSize createToolTip(const ToolTipInfo& info, bool isFocusObject); ///< @return requested tooltip size /// @param isFocusObject Is the object this tooltips originates from mFocusObject? float mFocusToolTipX; float mFocusToolTipY; /// Adjust position for a tooltip so that it doesn't leave the screen and does not obscure the mouse cursor void position(MyGUI::IntPoint& position, MyGUI::IntSize size, MyGUI::IntSize viewportSize); static std::string sSchoolNames[6]; int mHorizontalScrollIndex; float mDelay; float mRemainingDelay; // remaining time until tooltip will show int mLastMouseX; int mLastMouseY; bool mEnabled; bool mFullHelp; int mShowOwned; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/tradeitemmodel.cpp000066400000000000000000000147701264522266000235330ustar00rootroot00000000000000#include "tradeitemmodel.hpp" #include #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" namespace MWGui { TradeItemModel::TradeItemModel(ItemModel *sourceModel, const MWWorld::Ptr& merchant) : mMerchant(merchant) { mSourceModel = sourceModel; } ItemStack TradeItemModel::getItem (ModelIndex index) { if (index < 0) throw std::runtime_error("Invalid index supplied"); if (mItems.size() <= static_cast(index)) throw std::runtime_error("Item index out of range"); return mItems[index]; } size_t TradeItemModel::getItemCount() { return mItems.size(); } void TradeItemModel::borrowImpl(const ItemStack &item, std::vector &out) { std::vector::iterator it = out.begin(); bool found = false; for (; it != out.end(); ++it) { if (it->mBase == item.mBase) { it->mCount += item.mCount; found = true; break; } } if (!found) out.push_back(item); } void TradeItemModel::unborrowImpl(const ItemStack &item, size_t count, std::vector &out) { std::vector::iterator it = out.begin(); bool found = false; for (; it != out.end(); ++it) { if (it->mBase == item.mBase) { if (it->mCount < count) throw std::runtime_error("Not enough borrowed items to return"); it->mCount -= count; if (it->mCount == 0) out.erase(it); found = true; break; } } if (!found) throw std::runtime_error("Can't find borrowed item to return"); } void TradeItemModel::borrowItemFromUs (ModelIndex itemIndex, size_t count) { ItemStack item = getItem(itemIndex); item.mCount = count; borrowImpl(item, mBorrowedFromUs); } void TradeItemModel::borrowItemToUs (ModelIndex itemIndex, ItemModel* source, size_t count) { ItemStack item = source->getItem(itemIndex); item.mCount = count; borrowImpl(item, mBorrowedToUs); } void TradeItemModel::returnItemBorrowedToUs (ModelIndex itemIndex, size_t count) { ItemStack item = getItem(itemIndex); unborrowImpl(item, count, mBorrowedToUs); } void TradeItemModel::returnItemBorrowedFromUs (ModelIndex itemIndex, ItemModel* source, size_t count) { ItemStack item = source->getItem(itemIndex); unborrowImpl(item, count, mBorrowedFromUs); } void TradeItemModel::adjustEncumbrance(float &encumbrance) { for (std::vector::iterator it = mBorrowedToUs.begin(); it != mBorrowedToUs.end(); ++it) { MWWorld::Ptr item = it->mBase; encumbrance += item.getClass().getWeight(item) * it->mCount; } for (std::vector::iterator it = mBorrowedFromUs.begin(); it != mBorrowedFromUs.end(); ++it) { MWWorld::Ptr item = it->mBase; encumbrance -= item.getClass().getWeight(item) * it->mCount; } encumbrance = std::max(0.f, encumbrance); } void TradeItemModel::abort() { mBorrowedFromUs.clear(); mBorrowedToUs.clear(); } std::vector TradeItemModel::getItemsBorrowedToUs() { return mBorrowedToUs; } void TradeItemModel::transferItems() { std::vector::iterator it = mBorrowedToUs.begin(); for (; it != mBorrowedToUs.end(); ++it) { // get index in the source model ItemModel* sourceModel = it->mCreator; size_t i=0; for (; igetItemCount(); ++i) { if (it->mBase == sourceModel->getItem(i).mBase) break; } if (i == sourceModel->getItemCount()) throw std::runtime_error("The borrowed item disappeared"); const ItemStack& item = sourceModel->getItem(i); // copy the borrowed items to our model copyItem(item, it->mCount); // then remove them from the source model sourceModel->removeItem(item, it->mCount); } mBorrowedToUs.clear(); mBorrowedFromUs.clear(); } void TradeItemModel::update() { mSourceModel->update(); int services = 0; if (!mMerchant.isEmpty()) services = mMerchant.getClass().getServices(mMerchant); mItems.clear(); // add regular items for (size_t i=0; igetItemCount(); ++i) { ItemStack item = mSourceModel->getItem(i); if(!mMerchant.isEmpty()) { MWWorld::Ptr base = item.mBase; if(Misc::StringUtils::ciEqual(base.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId)) continue; if(!base.getClass().canSell(base, services)) continue; // Bound items may not be bought if (item.mFlags & ItemStack::Flag_Bound) continue; // don't show equipped items if(mMerchant.getClass().hasInventoryStore(mMerchant)) { MWWorld::InventoryStore& store = mMerchant.getClass().getInventoryStore(mMerchant); if (store.isEquipped(base)) continue; } } // don't show items that we borrowed to someone else std::vector::iterator it = mBorrowedFromUs.begin(); for (; it != mBorrowedFromUs.end(); ++it) { if (it->mBase == item.mBase) { if (item.mCount < it->mCount) throw std::runtime_error("Lent more items than present"); item.mCount -= it->mCount; } } if (item.mCount > 0) mItems.push_back(item); } // add items borrowed to us std::vector::iterator it = mBorrowedToUs.begin(); for (; it != mBorrowedToUs.end(); ++it) { ItemStack item = *it; item.mType = ItemStack::Type_Barter; mItems.push_back(item); } } } openmw-openmw-0.38.0/apps/openmw/mwgui/tradeitemmodel.hpp000066400000000000000000000034021264522266000235260ustar00rootroot00000000000000#ifndef MWGUI_TRADE_ITEM_MODEL_H #define MWGUI_TRADE_ITEM_MODEL_H #include "itemmodel.hpp" namespace MWGui { class ItemModel; /// @brief An item model that allows 'borrowing' items from another item model. Used for previewing barter offers. /// Also filters items that the merchant does not sell. class TradeItemModel : public ProxyItemModel { public: TradeItemModel (ItemModel* sourceModel, const MWWorld::Ptr& merchant); virtual ItemStack getItem (ModelIndex index); virtual size_t getItemCount(); virtual void update(); void borrowItemFromUs (ModelIndex itemIndex, size_t count); void borrowItemToUs (ModelIndex itemIndex, ItemModel* source, size_t count); ///< @note itemIndex points to an item in \a source void returnItemBorrowedToUs (ModelIndex itemIndex, size_t count); void returnItemBorrowedFromUs (ModelIndex itemIndex, ItemModel* source, size_t count); /// Permanently transfers items that were borrowed to us from another model to this model void transferItems (); /// Aborts trade void abort(); /// Adjusts the given encumbrance by adding weight for items that have been lent to us, /// and removing weight for items we've lent to someone else. void adjustEncumbrance (float& encumbrance); std::vector getItemsBorrowedToUs(); private: void borrowImpl(const ItemStack& item, std::vector& out); void unborrowImpl(const ItemStack& item, size_t count, std::vector& out); std::vector mItems; std::vector mBorrowedToUs; std::vector mBorrowedFromUs; MWWorld::Ptr mMerchant; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/tradewindow.cpp000066400000000000000000000552621264522266000230640ustar00rootroot00000000000000#include "tradewindow.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "inventorywindow.hpp" #include "itemview.hpp" #include "sortfilteritemmodel.hpp" #include "containeritemmodel.hpp" #include "tradeitemmodel.hpp" #include "countdialog.hpp" #include "dialogue.hpp" #include "controllers.hpp" namespace { int getEffectiveValue (MWWorld::Ptr item, int count) { float price = static_cast(item.getClass().getValue(item)); if (item.getClass().hasItemHealth(item)) { price *= item.getClass().getItemHealth(item); price /= item.getClass().getItemMaxHealth(item); } return static_cast(price * count); } } namespace MWGui { const float TradeWindow::sBalanceChangeInitialPause = 0.5f; const float TradeWindow::sBalanceChangeInterval = 0.1f; TradeWindow::TradeWindow() : WindowBase("openmw_trade_window.layout") , mSortModel(NULL) , mTradeModel(NULL) , mItemToSell(-1) , mCurrentBalance(0) , mCurrentMerchantOffer(0) { getWidget(mFilterAll, "AllButton"); getWidget(mFilterWeapon, "WeaponButton"); getWidget(mFilterApparel, "ApparelButton"); getWidget(mFilterMagic, "MagicButton"); getWidget(mFilterMisc, "MiscButton"); getWidget(mMaxSaleButton, "MaxSaleButton"); getWidget(mCancelButton, "CancelButton"); getWidget(mOfferButton, "OfferButton"); getWidget(mPlayerGold, "PlayerGold"); getWidget(mMerchantGold, "MerchantGold"); getWidget(mIncreaseButton, "IncreaseButton"); getWidget(mDecreaseButton, "DecreaseButton"); getWidget(mTotalBalance, "TotalBalance"); getWidget(mTotalBalanceLabel, "TotalBalanceLabel"); getWidget(mBottomPane, "BottomPane"); getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &TradeWindow::onItemSelected); mFilterAll->setStateSelected(true); mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onCancelButtonClicked); mOfferButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onOfferButtonClicked); mMaxSaleButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onMaxSaleButtonClicked); mIncreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &TradeWindow::onIncreaseButtonPressed); mIncreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased); mDecreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &TradeWindow::onDecreaseButtonPressed); mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased); mTotalBalance->eventValueChanged += MyGUI::newDelegate(this, &TradeWindow::onBalanceValueChanged); mTotalBalance->setMinValue(INT_MIN+1); // disallow INT_MIN since abs(INT_MIN) is undefined setCoord(400, 0, 400, 300); } void TradeWindow::restock() { // Restock items on the actor inventory mPtr.getClass().restock(mPtr); // Also restock any containers owned by this merchant, which are also available to buy in the trade window std::vector itemSources; MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources); for (std::vector::iterator it = itemSources.begin(); it != itemSources.end(); ++it) { it->getClass().restock(*it); } } void TradeWindow::startTrade(const MWWorld::Ptr& actor) { mPtr = actor; mCurrentBalance = 0; mCurrentMerchantOffer = 0; restock(); std::vector itemSources; MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources); // Important: actor goes last, so that items purchased by the merchant go into his inventory itemSources.push_back(actor); std::vector worldItems; MWBase::Environment::get().getWorld()->getItemsOwnedBy(actor, worldItems); mTradeModel = new TradeItemModel(new ContainerItemModel(itemSources, worldItems), mPtr); mSortModel = new SortFilterItemModel(mTradeModel); mItemView->setModel (mSortModel); mItemView->resetScrollBars(); updateLabels(); setTitle(actor.getClass().getName(actor)); onFilterChanged(mFilterAll); } void TradeWindow::onFilterChanged(MyGUI::Widget* _sender) { if (_sender == mFilterAll) mSortModel->setCategory(SortFilterItemModel::Category_All); else if (_sender == mFilterWeapon) mSortModel->setCategory(SortFilterItemModel::Category_Weapon); else if (_sender == mFilterApparel) mSortModel->setCategory(SortFilterItemModel::Category_Apparel); else if (_sender == mFilterMagic) mSortModel->setCategory(SortFilterItemModel::Category_Magic); else if (_sender == mFilterMisc) mSortModel->setCategory(SortFilterItemModel::Category_Misc); mFilterAll->setStateSelected(false); mFilterWeapon->setStateSelected(false); mFilterApparel->setStateSelected(false); mFilterMagic->setStateSelected(false); mFilterMisc->setStateSelected(false); _sender->castType()->setStateSelected(true); mItemView->update(); } int TradeWindow::getMerchantServices() { return mPtr.getClass().getServices(mPtr); } void TradeWindow::exit() { mTradeModel->abort(); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->abort(); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onItemSelected (int index) { const ItemStack& item = mSortModel->getItem(index); MWWorld::Ptr object = item.mBase; int count = item.mCount; bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); if (MyGUI::InputManager::getInstance().isControlPressed()) count = 1; if (count > 1 && !shift) { CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); std::string message = "#{sQuanityMenuMessage02}"; dialog->openCountDialog(object.getClass().getName(object), message, count); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &TradeWindow::sellItem); mItemToSell = mSortModel->mapToSource(index); } else { mItemToSell = mSortModel->mapToSource(index); sellItem (NULL, count); } } void TradeWindow::sellItem(MyGUI::Widget* sender, int count) { const ItemStack& item = mTradeModel->getItem(mItemToSell); std::string sound = item.mBase.getClass().getDownSoundId(item.mBase); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); if (item.mType == ItemStack::Type_Barter) { // this was an item borrowed to us by the player mTradeModel->returnItemBorrowedToUs(mItemToSell, count); playerTradeModel->returnItemBorrowedFromUs(mItemToSell, mTradeModel, count); buyFromNpc(item.mBase, count, true); } else { // borrow item to player playerTradeModel->borrowItemToUs(mItemToSell, mTradeModel, count); mTradeModel->borrowItemFromUs(mItemToSell, count); buyFromNpc(item.mBase, count, false); } MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); mItemView->update(); } void TradeWindow::borrowItem (int index, size_t count) { TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); mTradeModel->borrowItemToUs(index, playerTradeModel, count); mItemView->update(); sellToNpc(playerTradeModel->getItem(index).mBase, count, false); } void TradeWindow::returnItem (int index, size_t count) { TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); const ItemStack& item = playerTradeModel->getItem(index); mTradeModel->returnItemBorrowedFromUs(index, playerTradeModel, count); mItemView->update(); sellToNpc(item.mBase, count, true); } void TradeWindow::addOrRemoveGold(int amount, const MWWorld::Ptr& actor) { MWWorld::ContainerStore& store = actor.getClass().getContainerStore(actor); if (amount > 0) { store.add(MWWorld::ContainerStore::sGoldId, amount, actor); } else { store.remove(MWWorld::ContainerStore::sGoldId, - amount, actor); } } void TradeWindow::onOfferButtonClicked(MyGUI::Widget* _sender) { TradeItemModel* playerItemModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); // were there any items traded at all? std::vector playerBought = playerItemModel->getItemsBorrowedToUs(); std::vector merchantBought = mTradeModel->getItemsBorrowedToUs(); if (playerBought.empty() && merchantBought.empty()) { // user notification MWBase::Environment::get().getWindowManager()-> messageBox("#{sBarterDialog11}"); return; } MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); // check if the player can afford this if (mCurrentBalance < 0 && playerGold < std::abs(mCurrentBalance)) { // user notification MWBase::Environment::get().getWindowManager()-> messageBox("#{sBarterDialog1}"); return; } // check if the merchant can afford this if (mCurrentBalance > 0 && getMerchantGold() < mCurrentBalance) { // user notification MWBase::Environment::get().getWindowManager()-> messageBox("#{sBarterDialog2}"); return; } // check if the player is attempting to sell back an item stolen from this actor for (std::vector::iterator it = merchantBought.begin(); it != merchantBought.end(); ++it) { if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(it->mBase.getCellRef().getRefId(), mPtr.getCellRef().getRefId())) { std::string msg = gmst.find("sNotifyMessage49")->getString(); if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase)); MWBase::Environment::get().getWindowManager()->messageBox(msg); MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, it->mBase.getClass().getValue(it->mBase) * it->mCount, true); onCancelButtonClicked(mCancelButton); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); return; } } // TODO: move to mwmechanics // Is the player buying? bool buying = (mCurrentMerchantOffer < 0); if(mCurrentBalance > mCurrentMerchantOffer) { //if npc is a creature: reject (no haggle) if (mPtr.getTypeName() != typeid(ESM::NPC).name()) { MWBase::Environment::get().getWindowManager()-> messageBox("#{sNotifyMessage9}"); return; } int a = abs(mCurrentMerchantOffer); int b = abs(mCurrentBalance); int d = 0; if (buying) d = int(100 * (a - b) / a); else d = int(100 * (b - a) / a); int clampedDisposition = std::max(0, std::min(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(),100)); const MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player); float a1 = static_cast(player.getClass().getSkill(player, ESM::Skill::Mercantile)); float b1 = 0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(); float c1 = 0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(); float d1 = static_cast(mPtr.getClass().getSkill(mPtr, ESM::Skill::Mercantile)); float e1 = 0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(); float f1 = 0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(); float dispositionTerm = gmst.find("fDispositionMod")->getFloat() * (clampedDisposition - 50); float pcTerm = (dispositionTerm - 50 + a1 + b1 + c1) * playerStats.getFatigueTerm(); float npcTerm = (d1 + e1 + f1) * sellerStats.getFatigueTerm(); float x = gmst.find("fBargainOfferMulti")->getFloat() * d + gmst.find("fBargainOfferBase")->getFloat(); if (buying) x += abs(int(pcTerm - npcTerm)); else x += abs(int(npcTerm - pcTerm)); int roll = Misc::Rng::rollDice(100) + 1; if(roll > x || (mCurrentMerchantOffer < 0) != (mCurrentBalance < 0)) //trade refused { MWBase::Environment::get().getWindowManager()-> messageBox("#{sNotifyMessage9}"); int iBarterFailDisposition = gmst.find("iBarterFailDisposition")->getInt(); if (mPtr.getClass().isNpc()) MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterFailDisposition); return; } //skill use! float skillGain = 0.f; int finalPrice = std::abs(mCurrentBalance); int initialMerchantOffer = std::abs(mCurrentMerchantOffer); if (!buying && (finalPrice > initialMerchantOffer) && finalPrice > 0) skillGain = floor(100 * (finalPrice - initialMerchantOffer) / float(finalPrice)); else if (buying && (finalPrice < initialMerchantOffer) && initialMerchantOffer > 0) skillGain = floor(100 * (initialMerchantOffer - finalPrice) / float(initialMerchantOffer)); player.getClass().skillUsageSucceeded(player, ESM::Skill::Mercantile, 0, skillGain); } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); if (mPtr.getClass().isNpc()) MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition); // make the item transfer mTradeModel->transferItems(); playerItemModel->transferItems(); // transfer the gold if (mCurrentBalance != 0) { addOrRemoveGold(mCurrentBalance, player); mPtr.getClass().getCreatureStats(mPtr).setGoldPool( mPtr.getClass().getCreatureStats(mPtr).getGoldPool() - mCurrentBalance ); } MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse( MWBase::Environment::get().getWorld()->getStore().get().find("sBarterDialog5")->getString()); std::string sound = "Item Gold Up"; MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { exit(); } void TradeWindow::onMaxSaleButtonClicked(MyGUI::Widget* _sender) { mCurrentBalance = getMerchantGold(); updateLabels(); } void TradeWindow::addRepeatController(MyGUI::Widget *widget) { MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerRepeatEvent::getClassTypeName()); Controllers::ControllerRepeatEvent* controller = item->castType(); controller->eventRepeatClick += MyGUI::newDelegate(this, &TradeWindow::onRepeatClick); controller->setRepeat(sBalanceChangeInitialPause, sBalanceChangeInterval); MyGUI::ControllerManager::getInstance().addItem(widget, controller); } void TradeWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { addRepeatController(_sender); onIncreaseButtonTriggered(); } void TradeWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { addRepeatController(_sender); onDecreaseButtonTriggered(); } void TradeWindow::onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller) { if (widget == mIncreaseButton) onIncreaseButtonTriggered(); else if (widget == mDecreaseButton) onDecreaseButtonTriggered(); } void TradeWindow::onBalanceButtonReleased(MyGUI::Widget *_sender, int _left, int _top, MyGUI::MouseButton _id) { MyGUI::ControllerManager::getInstance().removeItem(_sender); } void TradeWindow::onBalanceValueChanged(int value) { // Entering a "-" sign inverts the buying/selling state mCurrentBalance = (mCurrentBalance >= 0 ? 1 : -1) * value; updateLabels(); if (value != std::abs(value)) mTotalBalance->setValue(std::abs(value)); } void TradeWindow::onIncreaseButtonTriggered() { // prevent overflows, and prevent entering INT_MIN since abs(INT_MIN) is undefined if (mCurrentBalance == INT_MAX || mCurrentBalance == INT_MIN+1) return; if(mCurrentBalance<=-1) mCurrentBalance -= 1; if(mCurrentBalance>=1) mCurrentBalance += 1; updateLabels(); } void TradeWindow::onDecreaseButtonTriggered() { if(mCurrentBalance<-1) mCurrentBalance += 1; if(mCurrentBalance>1) mCurrentBalance -= 1; updateLabels(); } void TradeWindow::updateLabels() { MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + MyGUI::utility::toString(playerGold)); if (mCurrentBalance > 0) { mTotalBalanceLabel->setCaptionWithReplacing("#{sTotalSold}"); } else { mTotalBalanceLabel->setCaptionWithReplacing("#{sTotalCost}"); } mTotalBalance->setValue(std::abs(mCurrentBalance)); mMerchantGold->setCaptionWithReplacing("#{sSellerGold} " + MyGUI::utility::toString(getMerchantGold())); } void TradeWindow::updateOffer() { TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); int merchantOffer = 0; std::vector playerBorrowed = playerTradeModel->getItemsBorrowedToUs(); for (std::vector::const_iterator it = playerBorrowed.begin(); it != playerBorrowed.end(); ++it) { merchantOffer -= MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, getEffectiveValue(it->mBase, it->mCount), true); } std::vector merchantBorrowed = mTradeModel->getItemsBorrowedToUs(); for (std::vector::const_iterator it = merchantBorrowed.begin(); it != merchantBorrowed.end(); ++it) { merchantOffer += MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, getEffectiveValue(it->mBase, it->mCount), false); } int diff = merchantOffer - mCurrentMerchantOffer; mCurrentMerchantOffer = merchantOffer; mCurrentBalance += diff; updateLabels(); } void TradeWindow::sellToNpc(const MWWorld::Ptr& item, int count, bool boughtItem) { updateOffer(); } void TradeWindow::buyFromNpc(const MWWorld::Ptr& item, int count, bool soldItem) { updateOffer(); } void TradeWindow::onReferenceUnavailable() { // remove both Trade and Dialogue (since you always trade with the NPC/creature that you have previously talked to) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } int TradeWindow::getMerchantGold() { int merchantGold = mPtr.getClass().getCreatureStats(mPtr).getGoldPool(); return merchantGold; } void TradeWindow::resetReference() { ReferenceInterface::resetReference(); mItemView->setModel(NULL); mTradeModel = NULL; mSortModel = NULL; } } openmw-openmw-0.38.0/apps/openmw/mwgui/tradewindow.hpp000066400000000000000000000062651264522266000230700ustar00rootroot00000000000000#ifndef MWGUI_TRADEWINDOW_H #define MWGUI_TRADEWINDOW_H #include "referenceinterface.hpp" #include "windowbase.hpp" namespace Gui { class NumericEditBox; } namespace MyGUI { class ControllerItem; } namespace MWGui { class ItemView; class SortFilterItemModel; class TradeItemModel; class TradeWindow : public WindowBase, public ReferenceInterface { public: TradeWindow(); void startTrade(const MWWorld::Ptr& actor); void borrowItem (int index, size_t count); void returnItem (int index, size_t count); int getMerchantServices(); virtual void exit(); virtual void resetReference(); private: ItemView* mItemView; SortFilterItemModel* mSortModel; TradeItemModel* mTradeModel; static const float sBalanceChangeInitialPause; // in seconds static const float sBalanceChangeInterval; // in seconds MyGUI::Button* mFilterAll; MyGUI::Button* mFilterWeapon; MyGUI::Button* mFilterApparel; MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; MyGUI::Button* mIncreaseButton; MyGUI::Button* mDecreaseButton; MyGUI::TextBox* mTotalBalanceLabel; Gui::NumericEditBox* mTotalBalance; MyGUI::Widget* mBottomPane; MyGUI::Button* mMaxSaleButton; MyGUI::Button* mCancelButton; MyGUI::Button* mOfferButton; MyGUI::TextBox* mPlayerGold; MyGUI::TextBox* mMerchantGold; int mItemToSell; int mCurrentBalance; int mCurrentMerchantOffer; void sellToNpc(const MWWorld::Ptr& item, int count, bool boughtItem); ///< only used for adjusting the gold balance void buyFromNpc(const MWWorld::Ptr& item, int count, bool soldItem); ///< only used for adjusting the gold balance void updateOffer(); void onItemSelected (int index); void sellItem (MyGUI::Widget* sender, int count); void onFilterChanged(MyGUI::Widget* _sender); void onOfferButtonClicked(MyGUI::Widget* _sender); void onCancelButtonClicked(MyGUI::Widget* _sender); void onMaxSaleButtonClicked(MyGUI::Widget* _sender); void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onBalanceButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onBalanceValueChanged(int value); void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller); void addRepeatController(MyGUI::Widget* widget); void onIncreaseButtonTriggered(); void onDecreaseButtonTriggered(); void addOrRemoveGold(int gold, const MWWorld::Ptr& actor); void updateLabels(); virtual void onReferenceUnavailable(); int getMerchantGold(); void restock(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/trainingwindow.cpp000066400000000000000000000164031264522266000235720ustar00rootroot00000000000000#include "trainingwindow.hpp" #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "tooltips.hpp" namespace { // Sorts a container descending by skill value. If skill value is equal, sorts ascending by skill ID. // pair bool sortSkills (const std::pair& left, const std::pair& right) { if (left == right) return false; if (left.second > right.second) return true; else if (left.second < right.second) return false; return left.first < right.first; } } namespace MWGui { TrainingWindow::TrainingWindow() : WindowBase("openmw_trainingwindow.layout") , mFadeTimeRemaining(0) , mTimeAdvancer(0.05f) { getWidget(mTrainingOptions, "TrainingOptions"); getWidget(mCancelButton, "CancelButton"); getWidget(mPlayerGold, "PlayerGold"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onCancelButtonClicked); mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &TrainingWindow::onTrainingProgressChanged); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &TrainingWindow::onTrainingFinished); mProgressBar.setVisible(false); } void TrainingWindow::open() { center(); } void TrainingWindow::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); } void TrainingWindow::startTraining (MWWorld::Ptr actor) { mPtr = actor; MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); // NPC can train you in his best 3 skills std::vector< std::pair > skills; for (int i=0; igetEnumerator (); MyGUI::Gui::getInstance ().destroyWidgets (widgets); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); for (int i=0; i<3; ++i) { int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer (mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); MyGUI::Button* button = mTrainingOptions->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); button->setUserData(skills[i].first); button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); button->setCaptionWithReplacing("#{" + ESM::Skill::sSkillNameIds[skills[i].first] + "} - " + MyGUI::utility::toString(price)); button->setSize(button->getTextSize ().width+12, button->getSize().height); ToolTips::createSkillToolTip (button, skills[i].first); } center(); } void TrainingWindow::onReferenceUnavailable () { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Training); } void TrainingWindow::onCancelButtonClicked (MyGUI::Widget *sender) { exit(); } void TrainingWindow::onTrainingSelected (MyGUI::Widget *sender) { int skillId = *sender->getUserData(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->getInt (); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) return; MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats (mPtr); if (npcStats.getSkill (skillId).getBase () <= pcStats.getSkill (skillId).getBase ()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}"); return; } // You can not train a skill above its governing attribute const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get().find(skillId); if (pcStats.getSkill(skillId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage17}"); return; } // increase skill MWWorld::LiveCellRef *playerRef = player.get(); const ESM::Class *class_ = store.get().find(playerRef->mBase->mClass); pcStats.increaseSkill (skillId, *class_, true); // remove gold player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); // add gold to NPC trading gold pool npcStats.setGoldPool(npcStats.getGoldPool() + price); // go back to game mode MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); // advance time MWBase::Environment::get().getWorld ()->advanceTime (2); MWBase::Environment::get().getMechanicsManager()->rest(false); MWBase::Environment::get().getMechanicsManager()->rest(false); mProgressBar.setVisible(true); mProgressBar.setProgress(0, 2); mTimeAdvancer.run(2); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.25); mFadeTimeRemaining = 0.5; } void TrainingWindow::onTrainingProgressChanged(int cur, int total) { mProgressBar.setProgress(cur, total); } void TrainingWindow::onTrainingFinished() { mProgressBar.setVisible(false); } void TrainingWindow::onFrame(float dt) { mTimeAdvancer.onFrame(dt); if (mFadeTimeRemaining <= 0) return; mFadeTimeRemaining -= dt; if (mFadeTimeRemaining <= 0) MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.25); } } openmw-openmw-0.38.0/apps/openmw/mwgui/trainingwindow.hpp000066400000000000000000000017271264522266000236020ustar00rootroot00000000000000#ifndef MWGUI_TRAININGWINDOW_H #define MWGUI_TRAININGWINDOW_H #include "windowbase.hpp" #include "referenceinterface.hpp" #include "timeadvancer.hpp" #include "waitdialog.hpp" namespace MWGui { class TrainingWindow : public WindowBase, public ReferenceInterface { public: TrainingWindow(); virtual void open(); virtual void exit(); void startTraining(MWWorld::Ptr actor); void onFrame(float dt); protected: virtual void onReferenceUnavailable (); void onCancelButtonClicked (MyGUI::Widget* sender); void onTrainingSelected(MyGUI::Widget* sender); void onTrainingProgressChanged(int cur, int total); void onTrainingFinished(); MyGUI::Widget* mTrainingOptions; MyGUI::Button* mCancelButton; MyGUI::TextBox* mPlayerGold; float mFadeTimeRemaining; WaitDialogProgressBar mProgressBar; TimeAdvancer mTimeAdvancer; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/travelwindow.cpp000066400000000000000000000215001264522266000232460ustar00rootroot00000000000000#include "travelwindow.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/actionteleport.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" namespace MWGui { const int TravelWindow::sLineHeight = 18; TravelWindow::TravelWindow() : WindowBase("openmw_travel_window.layout") , mCurrentY(0) { setCoord(0, 0, 450, 300); getWidget(mCancelButton, "CancelButton"); getWidget(mPlayerGold, "PlayerGold"); getWidget(mSelect, "Select"); getWidget(mDestinations, "Travel"); getWidget(mDestinationsView, "DestinationsView"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onCancelButtonClicked); mDestinations->setCoord(450/2-mDestinations->getTextSize().width/2, mDestinations->getTop(), mDestinations->getTextSize().width, mDestinations->getHeight()); mSelect->setCoord(8, mSelect->getTop(), mSelect->getTextSize().width, mSelect->getHeight()); } void TravelWindow::exit() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); } void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior) { int price = 0; const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); if(interior) { price = gmst.find("fMagesGuildTravel")->getInt(); } else { ESM::Position PlayerPos = player.getRefData().getPosition(); float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); price = static_cast(d / gmst.find("fTravelMult")->getFloat()); } price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); toAdd->setEnabled(price<=playerGold); mCurrentY += sLineHeight; if(interior) toAdd->setUserString("interior","y"); else toAdd->setUserString("interior","n"); std::ostringstream oss; oss << price; toAdd->setUserString("price",oss.str()); toAdd->setCaptionWithReplacing("#{sCell=" + name + "} - " + MyGUI::utility::toString(price)+"#{sgp}"); toAdd->setSize(toAdd->getTextSize().width,sLineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel); toAdd->setUserString("Destination", name); toAdd->setUserData(pos); toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onTravelButtonClick); } void TravelWindow::clearDestinations() { mDestinationsView->setViewOffset(MyGUI::IntPoint(0,0)); mCurrentY = 0; while (mDestinationsView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mDestinationsView->getChildAt(0)); } void TravelWindow::startTravel(const MWWorld::Ptr& actor) { center(); mPtr = actor; clearDestinations(); std::vector transport; if (mPtr.getClass().isNpc()) transport = mPtr.get()->mBase->getTransport(); else if (mPtr.getTypeName() == typeid(ESM::Creature).name()) transport = mPtr.get()->mBase->getTransport(); for(unsigned int i = 0;ipositionToIndex(transport[i].mPos.pos[0], transport[i].mPos.pos[1],x,y); if (cellname == "") { MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(x,y); cellname = MWBase::Environment::get().getWorld()->getCellName(cell); interior = false; } addDestination(cellname,transport[i].mPos,interior); } updateLabels(); // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mDestinationsView->setVisibleVScroll(false); mDestinationsView->setCanvasSize (MyGUI::IntSize(mDestinationsView->getWidth(), std::max(mDestinationsView->getHeight(), mCurrentY))); mDestinationsView->setVisibleVScroll(true); } void TravelWindow::onTravelButtonClick(MyGUI::Widget* _sender) { std::istringstream iss(_sender->getUserString("price")); int price; iss >> price; MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); if (playerGoldisExterior()) // Interior cell -> mages guild transport MWBase::Environment::get().getSoundManager()->playSound("mysticism cast", 1, 1); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); // add gold to NPC trading gold pool MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); npcStats.setGoldPool(npcStats.getGoldPool() + price); MWBase::Environment::get().getWindowManager()->fadeScreenOut(1); ESM::Position pos = *_sender->getUserData(); std::string cellname = _sender->getUserString("Destination"); bool interior = _sender->getUserString("interior") == "y"; if (!interior) { ESM::Position playerPos = player.getRefData().getPosition(); float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length(); int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); for(int i = 0;i < hours;i++) { MWBase::Environment::get().getMechanicsManager ()->rest (true); } MWBase::Environment::get().getWorld()->advanceTime(hours); } MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); // Teleports any followers, too. MWWorld::ActionTeleport action(interior ? cellname : "", pos, true); action.execute(player); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0); MWBase::Environment::get().getWindowManager()->fadeScreenIn(1); } void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { exit(); } void TravelWindow::updateLabels() { MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, mPlayerGold->getHeight()); } void TravelWindow::onReferenceUnavailable() { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } void TravelWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (mDestinationsView->getViewOffset().top + _rel*0.3f > 0) mDestinationsView->setViewOffset(MyGUI::IntPoint(0, 0)); else mDestinationsView->setViewOffset(MyGUI::IntPoint(0, static_cast(mDestinationsView->getViewOffset().top + _rel*0.3f))); } } openmw-openmw-0.38.0/apps/openmw/mwgui/travelwindow.hpp000066400000000000000000000022311264522266000232530ustar00rootroot00000000000000#ifndef MWGUI_TravelWINDOW_H #define MWGUI_TravelWINDOW_H #include "windowbase.hpp" #include "referenceinterface.hpp" namespace MyGUI { class Gui; class Widget; } namespace MWGui { class WindowManager; } namespace MWGui { class TravelWindow : public ReferenceInterface, public WindowBase { public: TravelWindow(); virtual void exit(); void startTravel(const MWWorld::Ptr& actor); protected: MyGUI::Button* mCancelButton; MyGUI::TextBox* mPlayerGold; MyGUI::TextBox* mDestinations; MyGUI::TextBox* mSelect; MyGUI::ScrollView* mDestinationsView; void onCancelButtonClicked(MyGUI::Widget* _sender); void onTravelButtonClick(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); void addDestination(const std::string& name, ESM::Position pos, bool interior); void clearDestinations(); int mCurrentY; static const int sLineHeight; void updateLabels(); virtual void onReferenceUnavailable(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/videowidget.cpp000066400000000000000000000043541264522266000230430ustar00rootroot00000000000000#include "videowidget.hpp" #include #include #include #include #include #include "../mwsound/movieaudiofactory.hpp" namespace MWGui { VideoWidget::VideoWidget() : mVFS(NULL) { mPlayer.reset(new Video::VideoPlayer()); setNeedKeyFocus(true); } void VideoWidget::setVFS(const VFS::Manager *vfs) { mVFS = vfs; } void VideoWidget::playVideo(const std::string &video) { mPlayer->setAudioFactory(new MWSound::MovieAudioFactory()); Files::IStreamPtr videoStream; try { videoStream = mVFS->get(video); } catch (std::exception& e) { std::cerr << "Failed to open video: " << e.what() << std::endl; return; } mPlayer->playVideo(videoStream, video); osg::ref_ptr texture = mPlayer->getVideoTexture(); if (!texture) return; mTexture.reset(new osgMyGUI::OSGTexture(texture)); setRenderItemTexture(mTexture.get()); getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } int VideoWidget::getVideoWidth() { return mPlayer->getVideoWidth(); } int VideoWidget::getVideoHeight() { return mPlayer->getVideoHeight(); } bool VideoWidget::update() { return mPlayer->update(); } void VideoWidget::stop() { mPlayer->close(); } bool VideoWidget::hasAudioStream() { return mPlayer->hasAudioStream(); } void VideoWidget::autoResize(bool stretch) { MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); if (getParent()) screenSize = getParent()->getSize(); if (getVideoHeight() > 0 && !stretch) { double imageaspect = static_cast(getVideoWidth())/getVideoHeight(); int leftPadding = std::max(0, static_cast(screenSize.width - screenSize.height * imageaspect) / 2); int topPadding = std::max(0, static_cast(screenSize.height - screenSize.width / imageaspect) / 2); setCoord(leftPadding, topPadding, screenSize.width - leftPadding*2, screenSize.height - topPadding*2); } else setCoord(0,0,screenSize.width,screenSize.height); } } openmw-openmw-0.38.0/apps/openmw/mwgui/videowidget.hpp000066400000000000000000000025621264522266000230470ustar00rootroot00000000000000#ifndef OPENMW_MWGUI_VIDEOWIDGET_H #define OPENMW_MWGUI_VIDEOWIDGET_H #include namespace Video { class VideoPlayer; } namespace VFS { class Manager; } namespace MWGui { /** * Widget that plays a video. */ class VideoWidget : public MyGUI::Widget { public: MYGUI_RTTI_DERIVED(VideoWidget) VideoWidget(); /// Set the VFS (virtual file system) to find the videos on. void setVFS(const VFS::Manager* vfs); void playVideo (const std::string& video); int getVideoWidth(); int getVideoHeight(); /// @return Is the video still playing? bool update(); /// Return true if a video is currently playing and it has an audio stream. bool hasAudioStream(); /// Stop video and free resources (done automatically on destruction) void stop(); /// Adjust the coordinates of this video widget relative to its parent, /// based on the dimensions of the playing video. /// @param stretch Stretch the video to fill the whole screen? If false, /// black bars may be added to fix the aspect ratio. void autoResize (bool stretch); private: const VFS::Manager* mVFS; std::auto_ptr mTexture; std::auto_ptr mPlayer; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/waitdialog.cpp000066400000000000000000000235251264522266000226560ustar00rootroot00000000000000#include "waitdialog.hpp" #include #include #include #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/statemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwstate/charactermanager.hpp" #include "widgets.hpp" namespace MWGui { WaitDialogProgressBar::WaitDialogProgressBar() : WindowBase("openmw_wait_dialog_progressbar.layout") { getWidget(mProgressBar, "ProgressBar"); getWidget(mProgressText, "ProgressText"); } void WaitDialogProgressBar::open() { center(); } void WaitDialogProgressBar::setProgress (int cur, int total) { mProgressBar->setProgressRange (total); mProgressBar->setProgressPosition (cur); mProgressText->setCaption(MyGUI::utility::toString(cur) + "/" + MyGUI::utility::toString(total)); } // --------------------------------------------------------------------------------------------------------- WaitDialog::WaitDialog() : WindowBase("openmw_wait_dialog.layout") , mTimeAdvancer(0.05f) , mSleeping(false) , mHours(1) , mManualHours(1) , mFadeTimeRemaining(0) , mInterruptAt(-1) , mProgressBar() { getWidget(mDateTimeText, "DateTimeText"); getWidget(mRestText, "RestText"); getWidget(mHourText, "HourText"); getWidget(mUntilHealedButton, "UntilHealedButton"); getWidget(mWaitButton, "WaitButton"); getWidget(mCancelButton, "CancelButton"); getWidget(mHourSlider, "HourSlider"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onCancelButtonClicked); mUntilHealedButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onUntilHealedButtonClicked); mWaitButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onWaitButtonClicked); mHourSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &WaitDialog::onHourSliderChangedPosition); mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &WaitDialog::onWaitingProgressChanged); mTimeAdvancer.eventInterrupted += MyGUI::newDelegate(this, &WaitDialog::onWaitingInterrupted); mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &WaitDialog::onWaitingFinished); mProgressBar.setVisible (false); } void WaitDialog::exit() { if(!mProgressBar.isVisible()) //Only exit if not currently waiting MWBase::Environment::get().getWindowManager()->popGuiMode(); } void WaitDialog::open() { if (!MWBase::Environment::get().getWindowManager ()->getRestEnabled ()) { MWBase::Environment::get().getWindowManager()->popGuiMode (); } int canRest = MWBase::Environment::get().getWorld ()->canRest (); if (canRest == 2) { // resting underwater or mid-air not allowed MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}"); MWBase::Environment::get().getWindowManager()->popGuiMode (); } setCanRest(canRest == 0); onHourSliderChangedPosition(mHourSlider, 0); mHourSlider->setScrollPosition (0); std::string month = MWBase::Environment::get().getWorld ()->getMonthName(); int hour = static_cast(MWBase::Environment::get().getWorld()->getTimeStamp().getHour()); bool pm = hour >= 12; if (hour >= 13) hour -= 12; if (hour == 0) hour = 12; std::string dateTimeText = MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " " + month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) + ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); mDateTimeText->setCaptionWithReplacing (dateTimeText); } void WaitDialog::onUntilHealedButtonClicked(MyGUI::Widget* sender) { int autoHours = MWBase::Environment::get().getMechanicsManager()->getHoursToRest(); startWaiting(autoHours); } void WaitDialog::onWaitButtonClicked(MyGUI::Widget* sender) { startWaiting(mManualHours); } void WaitDialog::startWaiting(int hoursToWait) { if(Settings::Manager::getBool("autosave","Saves") && mSleeping) //autosaves when enabled and sleeping MWBase::Environment::get().getStateManager()->quickSave("Autosave"); MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2f); mFadeTimeRemaining = 0.4f; setVisible(false); mHours = hoursToWait; // FIXME: move this somewhere else? mInterruptAt = -1; MWWorld::Ptr player = world->getPlayerPtr(); if (mSleeping && player.getCell()->isExterior()) { std::string regionstr = player.getCell()->getCell()->mRegion; if (!regionstr.empty()) { const ESM::Region *region = world->getStore().get().find (regionstr); if (!region->mSleepList.empty()) { // figure out if player will be woken while sleeping int x = Misc::Rng::rollDice(hoursToWait); float fSleepRandMod = world->getStore().get().find("fSleepRandMod")->getFloat(); if (x < fSleepRandMod * hoursToWait) { float fSleepRestMod = world->getStore().get().find("fSleepRestMod")->getFloat(); int interruptAtHoursRemaining = int(fSleepRestMod * hoursToWait); if (interruptAtHoursRemaining != 0) { mInterruptAt = hoursToWait - interruptAtHoursRemaining; mInterruptCreatureList = region->mSleepList; } } } } } mProgressBar.setProgress (0, hoursToWait); } void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender) { exit(); } void WaitDialog::onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position) { mHourText->setCaptionWithReplacing (MyGUI::utility::toString(position+1) + " #{sRestMenu2}"); mManualHours = position+1; } void WaitDialog::onWaitingProgressChanged(int cur, int total) { mProgressBar.setProgress(cur, total); MWBase::Environment::get().getWorld()->advanceTime(1); MWBase::Environment::get().getMechanicsManager()->rest(mSleeping); } void WaitDialog::onWaitingInterrupted() { MWBase::Environment::get().getWindowManager()->messageBox("#{sSleepInterrupt}"); MWBase::Environment::get().getWorld()->spawnRandomCreature(mInterruptCreatureList); stopWaiting(); } void WaitDialog::onWaitingFinished() { stopWaiting(); MWWorld::Ptr player = MWMechanics::getPlayer(); const MWMechanics::NpcStats &pcstats = player.getClass().getNpcStats(player); // trigger levelup if possible const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->getInt()) { MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); } } void WaitDialog::setCanRest (bool canRest) { MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); bool full = (stats.getHealth().getCurrent() >= stats.getHealth().getModified()) && (stats.getMagicka().getCurrent() >= stats.getMagicka().getModified()); MWMechanics::NpcStats& npcstats = player.getClass().getNpcStats(player); bool werewolf = npcstats.isWerewolf(); mUntilHealedButton->setVisible(canRest && !full); mWaitButton->setCaptionWithReplacing (canRest ? "#{sRest}" : "#{sWait}"); mRestText->setCaptionWithReplacing (canRest ? "#{sRestMenu3}" : (werewolf ? "#{sWerewolfRestMessage}" : "#{sRestIllegal}")); mSleeping = canRest; Gui::Box* box = dynamic_cast(mMainWidget); if (box == NULL) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); center(); } void WaitDialog::onFrame(float dt) { mTimeAdvancer.onFrame(dt); if (mFadeTimeRemaining <= 0) return; mFadeTimeRemaining -= dt; if (mFadeTimeRemaining <= 0) { mProgressBar.setVisible(true); mTimeAdvancer.run(mHours, mInterruptAt); } } void WaitDialog::stopWaiting () { MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.2f); mProgressBar.setVisible (false); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Rest); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed); mTimeAdvancer.stop(); } void WaitDialog::wakeUp () { mSleeping = false; mTimeAdvancer.stop(); stopWaiting(); } } openmw-openmw-0.38.0/apps/openmw/mwgui/waitdialog.hpp000066400000000000000000000035331264522266000226600ustar00rootroot00000000000000#ifndef MWGUI_WAIT_DIALOG_H #define MWGUI_WAIT_DIALOG_H #include "timeadvancer.hpp" #include "windowbase.hpp" namespace MWGui { class WaitDialogProgressBar : public WindowBase { public: WaitDialogProgressBar(); virtual void open(); void setProgress(int cur, int total); protected: MyGUI::ProgressBar* mProgressBar; MyGUI::TextBox* mProgressText; }; class WaitDialog : public WindowBase { public: WaitDialog(); virtual void open(); virtual void exit(); void onFrame(float dt); void bedActivated() { setCanRest(true); } bool getSleeping() { return mTimeAdvancer.isRunning() && mSleeping; } void wakeUp(); void autosave(); protected: MyGUI::TextBox* mDateTimeText; MyGUI::TextBox* mRestText; MyGUI::TextBox* mHourText; MyGUI::Button* mUntilHealedButton; MyGUI::Button* mWaitButton; MyGUI::Button* mCancelButton; MyGUI::ScrollBar* mHourSlider; TimeAdvancer mTimeAdvancer; bool mSleeping; int mHours; int mManualHours; // stores the hours to rest selected via slider float mFadeTimeRemaining; int mInterruptAt; std::string mInterruptCreatureList; WaitDialogProgressBar mProgressBar; void onUntilHealedButtonClicked(MyGUI::Widget* sender); void onWaitButtonClicked(MyGUI::Widget* sender); void onCancelButtonClicked(MyGUI::Widget* sender); void onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position); void onWaitingProgressChanged(int cur, int total); void onWaitingInterrupted(); void onWaitingFinished(); void setCanRest(bool canRest); void startWaiting(int hoursToWait); void stopWaiting(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/widgets.cpp000066400000000000000000000566621264522266000222100ustar00rootroot00000000000000#include "widgets.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/esmstore.hpp" #include "controllers.hpp" namespace MWGui { namespace Widgets { /* MWSkill */ MWSkill::MWSkill() : mSkillId(ESM::Skill::Length) , mSkillNameWidget(NULL) , mSkillValueWidget(NULL) { } void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) { mSkillId = skill; updateWidgets(); } void MWSkill::setSkillNumber(int skill) { if (skill < 0) setSkillId(ESM::Skill::Length); else if (skill < ESM::Skill::Length) setSkillId(static_cast(skill)); else throw new std::runtime_error("Skill number out of range"); } void MWSkill::setSkillValue(const SkillValue& value) { mValue = value; updateWidgets(); } void MWSkill::updateWidgets() { if (mSkillNameWidget) { if (mSkillId == ESM::Skill::Length) { mSkillNameWidget->setCaption(""); } else { const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); mSkillNameWidget->setCaption(name); } } if (mSkillValueWidget) { SkillValue::Type modified = mValue.getModified(), base = mValue.getBase(); mSkillValueWidget->setCaption(MyGUI::utility::toString(modified)); if (modified > base) mSkillValueWidget->_setWidgetState("increased"); else if (modified < base) mSkillValueWidget->_setWidgetState("decreased"); else mSkillValueWidget->_setWidgetState("normal"); } } void MWSkill::onClicked(MyGUI::Widget* _sender) { eventClicked(this); } MWSkill::~MWSkill() { } void MWSkill::initialiseOverride() { Base::initialiseOverride(); assignWidget(mSkillNameWidget, "StatName"); assignWidget(mSkillValueWidget, "StatValue"); MyGUI::Button* button; assignWidget(button, "StatNameButton"); if (button) { mSkillNameWidget = button; button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); } button = 0; assignWidget(button, "StatValueButton"); if (button) { mSkillNameWidget = button; button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); } } /* MWAttribute */ MWAttribute::MWAttribute() : mId(-1) , mAttributeNameWidget(NULL) , mAttributeValueWidget(NULL) { } void MWAttribute::setAttributeId(int attributeId) { mId = attributeId; updateWidgets(); } void MWAttribute::setAttributeValue(const AttributeValue& value) { mValue = value; updateWidgets(); } void MWAttribute::onClicked(MyGUI::Widget* _sender) { eventClicked(this); } void MWAttribute::updateWidgets() { if (mAttributeNameWidget) { if (mId < 0 || mId >= 8) { mAttributeNameWidget->setCaption(""); } else { static const char *attributes[8] = { "sAttributeStrength", "sAttributeIntelligence", "sAttributeWillpower", "sAttributeAgility", "sAttributeSpeed", "sAttributeEndurance", "sAttributePersonality", "sAttributeLuck" }; const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(attributes[mId], ""); mAttributeNameWidget->setCaption(name); } } if (mAttributeValueWidget) { int modified = mValue.getModified(), base = mValue.getBase(); mAttributeValueWidget->setCaption(MyGUI::utility::toString(modified)); if (modified > base) mAttributeValueWidget->_setWidgetState("increased"); else if (modified < base) mAttributeValueWidget->_setWidgetState("decreased"); else mAttributeValueWidget->_setWidgetState("normal"); } } MWAttribute::~MWAttribute() { } void MWAttribute::initialiseOverride() { Base::initialiseOverride(); assignWidget(mAttributeNameWidget, "StatName"); assignWidget(mAttributeValueWidget, "StatValue"); MyGUI::Button* button; assignWidget(button, "StatNameButton"); if (button) { mAttributeNameWidget = button; button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); } button = 0; assignWidget(button, "StatValueButton"); if (button) { mAttributeValueWidget = button; button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); } } /* MWSpell */ MWSpell::MWSpell() : mSpellNameWidget(NULL) { } void MWSpell::setSpellId(const std::string &spellId) { mId = spellId; updateWidgets(); } void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.get().search(mId); MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); std::vector::const_iterator end = spell->mEffects.mList.end(); for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) { MWSpellEffectPtr effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); SpellEffectParams params; params.mEffectID = it->mEffectID; params.mSkill = it->mSkill; params.mAttribute = it->mAttribute; params.mDuration = it->mDuration; params.mMagnMin = it->mMagnMin; params.mMagnMax = it->mMagnMax; params.mRange = it->mRange; params.mIsConstant = (flags & MWEffectList::EF_Constant) != 0; params.mNoTarget = (flags & MWEffectList::EF_NoTarget); effect->setSpellEffect(params); effects.push_back(effect); coord.top += effect->getHeight(); coord.width = std::max(coord.width, effect->getRequestedWidth()); } } void MWSpell::updateWidgets() { if (mSpellNameWidget && MWBase::Environment::get().getWindowManager()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.get().search(mId); if (spell) mSpellNameWidget->setCaption(spell->mName); else mSpellNameWidget->setCaption(""); } } void MWSpell::initialiseOverride() { Base::initialiseOverride(); assignWidget(mSpellNameWidget, "StatName"); } MWSpell::~MWSpell() { } /* MWEffectList */ MWEffectList::MWEffectList() : mEffectList(0) { } void MWEffectList::setEffectList(const SpellEffectList& list) { mEffectList = list; updateWidgets(); } void MWEffectList::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags) { // We don't know the width of all the elements beforehand, so we do it in // 2 steps: first, create all widgets and check their width.... MWSpellEffectPtr effect = NULL; int maxwidth = coord.width; for (SpellEffectList::iterator it=mEffectList.begin(); it != mEffectList.end(); ++it) { effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); it->mIsConstant = (flags & EF_Constant) || it->mIsConstant; it->mNoTarget = (flags & EF_NoTarget) || it->mNoTarget; effect->setSpellEffect(*it); effects.push_back(effect); if (effect->getRequestedWidth() > maxwidth) maxwidth = effect->getRequestedWidth(); coord.top += effect->getHeight(); } // ... then adjust the size for all widgets for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) { effect = (*it)->castType(); bool needcenter = center && (maxwidth > effect->getRequestedWidth()); int diff = maxwidth - effect->getRequestedWidth(); if (needcenter) { effect->setCoord(diff/2, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); } else { effect->setCoord(0, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); } } // inform the parent about width coord.width = maxwidth; } void MWEffectList::updateWidgets() { } void MWEffectList::initialiseOverride() { Base::initialiseOverride(); } MWEffectList::~MWEffectList() { } SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects) { SpellEffectList result; std::vector::const_iterator end = effects->mList.end(); for (std::vector::const_iterator it = effects->mList.begin(); it != end; ++it) { SpellEffectParams params; params.mEffectID = it->mEffectID; params.mSkill = it->mSkill; params.mAttribute = it->mAttribute; params.mDuration = it->mDuration; params.mMagnMin = it->mMagnMin; params.mMagnMax = it->mMagnMax; params.mRange = it->mRange; params.mArea = it->mArea; result.push_back(params); } return result; } /* MWSpellEffect */ MWSpellEffect::MWSpellEffect() : mImageWidget(NULL) , mTextWidget(NULL) , mRequestedWidth(0) { } void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) { mEffectParams = params; updateWidgets(); } void MWSpellEffect::updateWidgets() { if (!mEffectParams.mKnown) { mTextWidget->setCaption ("?"); mRequestedWidth = mTextWidget->getTextSize().width + 24; mImageWidget->setImageTexture (""); return; } const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::MagicEffect *magicEffect = store.get().search(mEffectParams.mEffectID); assert(magicEffect); std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", ""); std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", ""); std::string pct = MWBase::Environment::get().getWindowManager()->getGameSettingString("spercent", ""); std::string ft = MWBase::Environment::get().getWindowManager()->getGameSettingString("sfeet", ""); std::string lvl = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevel", ""); std::string lvls = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevels", ""); std::string to = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "") + " "; std::string sec = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("ssecond", ""); std::string secs = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sseconds", ""); std::string effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID); std::string spellLine = MWBase::Environment::get().getWindowManager()->getGameSettingString(effectIDStr, ""); if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill && mEffectParams.mSkill != -1) { spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); } if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute && mEffectParams.mAttribute != -1) { spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); } if (mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) { ESM::MagicEffect::MagnitudeDisplayType displayType = magicEffect->getMagnitudeDisplayType(); if ( displayType == ESM::MagicEffect::MDT_TimesInt ) { std::string timesInt = MWBase::Environment::get().getWindowManager()->getGameSettingString("sXTimesINT", ""); std::stringstream formatter; formatter << std::fixed << std::setprecision(1) << " " << (mEffectParams.mMagnMin / 10.0f); if (mEffectParams.mMagnMin != mEffectParams.mMagnMax) formatter << to << (mEffectParams.mMagnMax / 10.0f); formatter << timesInt; spellLine += formatter.str(); } else if ( displayType != ESM::MagicEffect::MDT_None ) { spellLine += " " + MyGUI::utility::toString(mEffectParams.mMagnMin); if (mEffectParams.mMagnMin != mEffectParams.mMagnMax) spellLine += to + MyGUI::utility::toString(mEffectParams.mMagnMax); if ( displayType == ESM::MagicEffect::MDT_Percentage ) spellLine += pct; else if ( displayType == ESM::MagicEffect::MDT_Feet ) spellLine += " " + ft; else if ( displayType == ESM::MagicEffect::MDT_Level ) spellLine += " " + ((mEffectParams.mMagnMin == 1 && mEffectParams.mMagnMax == 1) ? lvl : lvls ); else // ESM::MagicEffect::MDT_Points spellLine += " " + ((mEffectParams.mMagnMin == 1 && mEffectParams.mMagnMax == 1) ? pt : pts ); } } // constant effects have no duration and no target if (!mEffectParams.mIsConstant) { if (mEffectParams.mDuration > 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) { spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + MyGUI::utility::toString(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); } if (mEffectParams.mArea > 0) { spellLine += " #{sin} " + MyGUI::utility::toString(mEffectParams.mArea) + " #{sfootarea}"; } // potions have no target if (!mEffectParams.mNoTarget) { std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sonword", ""); if (mEffectParams.mRange == ESM::RT_Self) spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeSelf", ""); else if (mEffectParams.mRange == ESM::RT_Touch) spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTouch", ""); else if (mEffectParams.mRange == ESM::RT_Target) spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTarget", ""); } } mTextWidget->setCaptionWithReplacing(spellLine); mRequestedWidth = mTextWidget->getTextSize().width + 24; mImageWidget->setImageTexture(MWBase::Environment::get().getWindowManager()->correctIconPath(magicEffect->mIcon)); } MWSpellEffect::~MWSpellEffect() { } void MWSpellEffect::initialiseOverride() { Base::initialiseOverride(); assignWidget(mTextWidget, "Text"); assignWidget(mImageWidget, "Image"); } /* MWDynamicStat */ MWDynamicStat::MWDynamicStat() : mValue(0) , mMax(1) , mTextWidget(NULL) , mBarWidget(NULL) , mBarTextWidget(NULL) { } void MWDynamicStat::setValue(int cur, int max) { mValue = cur; mMax = max; if (mBarWidget) { mBarWidget->setProgressRange(mMax); mBarWidget->setProgressPosition(mValue); } if (mBarTextWidget) { std::stringstream out; out << mValue << "/" << mMax; mBarTextWidget->setCaption(out.str().c_str()); } } void MWDynamicStat::setTitle(const std::string& text) { if (mTextWidget) mTextWidget->setCaption(text); } MWDynamicStat::~MWDynamicStat() { } void MWDynamicStat::initialiseOverride() { Base::initialiseOverride(); assignWidget(mTextWidget, "Text"); assignWidget(mBarWidget, "Bar"); assignWidget(mBarTextWidget, "BarText"); } MWScrollBar::MWScrollBar() : mEnableRepeat(true) , mRepeatTriggerTime(0.5f) , mRepeatStepTime(0.1f) , mIsIncreasing(true) { #if MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3,2,2) ScrollBar::setRepeatEnabled(false); #endif } MWScrollBar::~MWScrollBar() { } void MWScrollBar::initialiseOverride() { ScrollBar::initialiseOverride(); if(mWidgetStart) { mWidgetStart->eventMouseButtonPressed += MyGUI::newDelegate(this, &MWScrollBar::onDecreaseButtonPressed); mWidgetStart->eventMouseButtonReleased += MyGUI::newDelegate(this, &MWScrollBar::onDecreaseButtonReleased); } if(mWidgetEnd) { mWidgetEnd->eventMouseButtonPressed += MyGUI::newDelegate(this, &MWScrollBar::onIncreaseButtonPressed); mWidgetEnd->eventMouseButtonReleased += MyGUI::newDelegate(this, &MWScrollBar::onIncreaseButtonReleased); } } void MWScrollBar::setRepeat(float trigger, float step) { mRepeatTriggerTime = trigger; mRepeatStepTime = step; } void MWScrollBar::repeatClick(MyGUI::Widget* _widget, MyGUI::ControllerItem* _controller) { int stepSize = mScrollPage; if(mIsIncreasing && mScrollPosition < mScrollRange-1) { if(mScrollPosition + stepSize > mScrollRange-1) mScrollPosition = mScrollRange-1; else mScrollPosition += stepSize; eventScrollChangePosition(this, mScrollPosition); updateTrack(); } else if(!mIsIncreasing && mScrollPosition > 0) { int newPos = mScrollPosition - stepSize; if(newPos < 0) mScrollPosition = 0; else mScrollPosition -= stepSize; eventScrollChangePosition(this, mScrollPosition); updateTrack(); } } void MWScrollBar::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { mIsIncreasing = false; MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(MWGui::Controllers::ControllerRepeatEvent::getClassTypeName()); MWGui::Controllers::ControllerRepeatEvent* controller = item->castType(); controller->eventRepeatClick += newDelegate(this, &MWScrollBar::repeatClick); controller->setEnabled(mEnableRepeat); controller->setRepeat(mRepeatTriggerTime, mRepeatStepTime); MyGUI::ControllerManager::getInstance().addItem(this, controller); } void MWScrollBar::onDecreaseButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { MyGUI::ControllerManager::getInstance().removeItem(this); } void MWScrollBar::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { mIsIncreasing = true; MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(MWGui::Controllers::ControllerRepeatEvent::getClassTypeName()); MWGui::Controllers::ControllerRepeatEvent* controller = item->castType(); controller->eventRepeatClick += newDelegate(this, &MWScrollBar::repeatClick); controller->setEnabled(mEnableRepeat); controller->setRepeat(mRepeatTriggerTime, mRepeatStepTime); MyGUI::ControllerManager::getInstance().addItem(this, controller); } void MWScrollBar::onIncreaseButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { MyGUI::ControllerManager::getInstance().removeItem(this); } } } openmw-openmw-0.38.0/apps/openmw/mwgui/widgets.hpp000066400000000000000000000243431264522266000222040ustar00rootroot00000000000000#ifndef MWGUI_WIDGETS_H #define MWGUI_WIDGETS_H #include "../mwmechanics/stat.hpp" #include #include #include #include #include namespace MyGUI { class ImageBox; class ControllerItem; } namespace MWBase { class WindowManager; } /* This file contains various custom widgets used in OpenMW. */ namespace MWGui { namespace Widgets { class MWEffectList; void fixTexturePath(std::string &path); struct SpellEffectParams { SpellEffectParams() : mNoTarget(false) , mIsConstant(false) , mKnown(true) , mEffectID(-1) , mSkill(-1) , mAttribute(-1) , mMagnMin(-1) , mMagnMax(-1) , mRange(-1) , mDuration(-1) , mArea(0) { } bool mNoTarget; // potion effects for example have no target (target is always the player) bool mIsConstant; // constant effect means that duration will not be displayed bool mKnown; // is this effect known to the player? (If not, will display as a question mark instead) // value of -1 here means the effect is unknown to the player short mEffectID; // value of -1 here means there is no skill/attribute signed char mSkill, mAttribute; // value of -1 here means the value is unavailable int mMagnMin, mMagnMax, mRange, mDuration; // value of 0 -> no area effect int mArea; bool operator==(const SpellEffectParams& other) const { if (mEffectID != other.mEffectID) return false; bool involvesAttribute = (mEffectID == 74 // restore attribute || mEffectID == 85 // absorb attribute || mEffectID == 17 // drain attribute || mEffectID == 79 // fortify attribute || mEffectID == 22); // damage attribute bool involvesSkill = (mEffectID == 78 // restore skill || mEffectID == 89 // absorb skill || mEffectID == 21 // drain skill || mEffectID == 83 // fortify skill || mEffectID == 26); // damage skill return ((other.mSkill == mSkill) || !involvesSkill) && ((other.mAttribute == mAttribute) && !involvesAttribute) && (other.mArea == mArea); } }; typedef std::vector SpellEffectList; class MWSkill : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWSkill ) public: MWSkill(); typedef MWMechanics::Stat SkillValue; void setSkillId(ESM::Skill::SkillEnum skillId); void setSkillNumber(int skillId); void setSkillValue(const SkillValue& value); ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } const SkillValue& getSkillValue() const { return mValue; } // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_SkillVoid; /** Event : Skill clicked.\n signature : void method(MWSkill* _sender)\n */ EventHandle_SkillVoid eventClicked; protected: virtual ~MWSkill(); virtual void initialiseOverride(); void onClicked(MyGUI::Widget* _sender); private: void updateWidgets(); ESM::Skill::SkillEnum mSkillId; SkillValue mValue; MyGUI::TextBox* mSkillNameWidget; MyGUI::TextBox* mSkillValueWidget; }; typedef MWSkill* MWSkillPtr; class MWAttribute : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWAttribute ) public: MWAttribute(); typedef MWMechanics::AttributeValue AttributeValue; void setAttributeId(int attributeId); void setAttributeValue(const AttributeValue& value); int getAttributeId() const { return mId; } const AttributeValue& getAttributeValue() const { return mValue; } // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_AttributeVoid; /** Event : Attribute clicked.\n signature : void method(MWAttribute* _sender)\n */ EventHandle_AttributeVoid eventClicked; protected: virtual ~MWAttribute(); virtual void initialiseOverride(); void onClicked(MyGUI::Widget* _sender); private: void updateWidgets(); int mId; AttributeValue mValue; MyGUI::TextBox* mAttributeNameWidget; MyGUI::TextBox* mAttributeValueWidget; }; typedef MWAttribute* MWAttributePtr; /** * @todo remove this class and use MWEffectList instead */ class MWSpellEffect; class MWSpell : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWSpell ) public: MWSpell(); typedef MWMechanics::Stat SpellValue; void setSpellId(const std::string &id); /** * @param vector to store the created effect widgets * @param parent widget * @param coordinates to use, will be expanded if more space is needed * @param spell category, if this is 0, this means the spell effects are permanent and won't display e.g. duration * @param various flags, see MWEffectList::EffectFlags */ void createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags); const std::string &getSpellId() const { return mId; } protected: virtual ~MWSpell(); virtual void initialiseOverride(); private: void updateWidgets(); std::string mId; MyGUI::TextBox* mSpellNameWidget; }; typedef MWSpell* MWSpellPtr; class MWEffectList : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWEffectList ) public: MWEffectList(); typedef MWMechanics::Stat EnchantmentValue; enum EffectFlags { EF_NoTarget = 0x01, // potions have no target (target is always the player) EF_Constant = 0x02 // constant effect means that duration will not be displayed }; void setEffectList(const SpellEffectList& list); static SpellEffectList effectListFromESM(const ESM::EffectList* effects); /** * @param vector to store the created effect widgets * @param parent widget * @param coordinates to use, will be expanded if more space is needed * @param center the effect widgets horizontally * @param various flags, see MWEffectList::EffectFlags */ void createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags); protected: virtual ~MWEffectList(); virtual void initialiseOverride(); private: void updateWidgets(); SpellEffectList mEffectList; }; typedef MWEffectList* MWEffectListPtr; class MWSpellEffect : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWSpellEffect ) public: MWSpellEffect(); typedef ESM::ENAMstruct SpellEffectValue; void setSpellEffect(const SpellEffectParams& params); int getRequestedWidth() const { return mRequestedWidth; } protected: virtual ~MWSpellEffect(); virtual void initialiseOverride(); private: void updateWidgets(); SpellEffectParams mEffectParams; MyGUI::ImageBox* mImageWidget; MyGUI::TextBox* mTextWidget; int mRequestedWidth; }; typedef MWSpellEffect* MWSpellEffectPtr; class MWDynamicStat : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWDynamicStat ) public: MWDynamicStat(); void setValue(int value, int max); void setTitle(const std::string& text); int getValue() const { return mValue; } int getMax() const { return mMax; } protected: virtual ~MWDynamicStat(); virtual void initialiseOverride(); private: int mValue, mMax; MyGUI::TextBox* mTextWidget; MyGUI::ProgressBar* mBarWidget; MyGUI::TextBox* mBarTextWidget; }; typedef MWDynamicStat* MWDynamicStatPtr; // Should be removed when upgrading to MyGUI 3.2.2 (current git), it has ScrollBar autorepeat support class MWScrollBar : public MyGUI::ScrollBar { MYGUI_RTTI_DERIVED(MWScrollBar) public: MWScrollBar(); virtual ~MWScrollBar(); void setRepeat(float trigger, float step); protected: virtual void initialiseOverride(); void repeatClick(MyGUI::Widget* _widget, MyGUI::ControllerItem* _controller); bool mEnableRepeat; float mRepeatTriggerTime; float mRepeatStepTime; bool mIsIncreasing; private: void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onDecreaseButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onIncreaseButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); }; } } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/windowbase.cpp000066400000000000000000000057301264522266000226720ustar00rootroot00000000000000#include "windowbase.hpp" #include #include #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "draganddrop.hpp" using namespace MWGui; WindowBase::WindowBase(const std::string& parLayout) : Layout(parLayout) { } void WindowBase::setVisible(bool visible) { bool wasVisible = mMainWidget->getVisible(); mMainWidget->setVisible(visible); if (visible) open(); else if (wasVisible && !visible) close(); // This is needed as invisible widgets can retain key focus. // Remove for MyGUI 3.2.2 if (!visible) { MyGUI::Widget* keyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); while (keyFocus != mMainWidget && keyFocus != NULL) keyFocus = keyFocus->getParent(); if (keyFocus == mMainWidget) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); } } bool WindowBase::isVisible() { return mMainWidget->getVisible(); } void WindowBase::center() { // Centre dialog MyGUI::IntSize layerSize = MyGUI::RenderManager::getInstance().getViewSize(); if (mMainWidget->getLayer()) layerSize = mMainWidget->getLayer()->getSize(); MyGUI::IntCoord coord = mMainWidget->getCoord(); coord.left = (layerSize.width - coord.width)/2; coord.top = (layerSize.height - coord.height)/2; mMainWidget->setCoord(coord); } WindowModal::WindowModal(const std::string& parLayout) : WindowBase(parLayout) { } void WindowModal::open() { MyGUI::InputManager::getInstance ().addWidgetModal (mMainWidget); MWBase::Environment::get().getWindowManager()->addCurrentModal(this); //Set so we can escape it if needed } void WindowModal::close() { MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget); MWBase::Environment::get().getWindowManager()->removeCurrentModal(this); } NoDrop::NoDrop(DragAndDrop *drag, MyGUI::Widget *widget) : mWidget(widget), mDrag(drag), mTransparent(false) { if (!mWidget) throw std::runtime_error("NoDrop needs a non-NULL widget!"); } void NoDrop::onFrame(float dt) { MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance().getMousePosition(); if (mDrag->mIsOnDragAndDrop) { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); while (focus && focus != mWidget) focus = focus->getParent(); if (focus == mWidget) mTransparent = true; } if (!mWidget->getAbsoluteCoord().inside(mousePos)) mTransparent = false; if (mTransparent) { mWidget->setNeedMouseFocus(false); // Allow click-through setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5)); } else { mWidget->setNeedMouseFocus(true); setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5)); } } void NoDrop::setAlpha(float alpha) { mWidget->setAlpha(alpha); } openmw-openmw-0.38.0/apps/openmw/mwgui/windowbase.hpp000066400000000000000000000031301264522266000226670ustar00rootroot00000000000000#ifndef MWGUI_WINDOW_BASE_H #define MWGUI_WINDOW_BASE_H #include "layout.hpp" namespace MWBase { class WindowManager; } namespace MWGui { class WindowManager; class DragAndDrop; class WindowBase: public Layout { public: WindowBase(const std::string& parLayout); // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; /// Notify that window has been made visible virtual void open() {} /// Notify that window has been hidden virtual void close () {} /// Gracefully exits the window virtual void exit() {} /// Sets the visibility of the window virtual void setVisible(bool visible); /// Returns the visibility state of the window bool isVisible(); void center(); }; /* * "Modal" windows cause the rest of the interface to be unaccessible while they are visible */ class WindowModal : public WindowBase { public: WindowModal(const std::string& parLayout); virtual void open(); virtual void close(); virtual void exit() {} }; /// A window that cannot be the target of a drag&drop action. /// When hovered with a drag item, the window will become transparent and allow click-through. class NoDrop { public: NoDrop(DragAndDrop* drag, MyGUI::Widget* widget); void onFrame(float dt); virtual void setAlpha(float alpha); private: MyGUI::Widget* mWidget; DragAndDrop* mDrag; bool mTransparent; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/windowmanagerimp.cpp000066400000000000000000002137421264522266000241040ustar00rootroot00000000000000#include "windowmanagerimp.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/inputmanager.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwrender/vismask.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/stat.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwrender/localmap.hpp" #include "console.hpp" #include "journalwindow.hpp" #include "journalviewmodel.hpp" #include "charactercreation.hpp" #include "dialogue.hpp" #include "statswindow.hpp" #include "messagebox.hpp" #include "tooltips.hpp" #include "scrollwindow.hpp" #include "bookwindow.hpp" #include "hud.hpp" #include "mainmenu.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" #include "spellbuyingwindow.hpp" #include "travelwindow.hpp" #include "settingswindow.hpp" #include "confirmationdialog.hpp" #include "alchemywindow.hpp" #include "spellwindow.hpp" #include "quickkeysmenu.hpp" #include "loadingscreen.hpp" #include "levelupdialog.hpp" #include "waitdialog.hpp" #include "enchantingdialog.hpp" #include "trainingwindow.hpp" #include "recharge.hpp" #include "exposedwindow.hpp" #include "cursor.hpp" #include "merchantrepair.hpp" #include "repair.hpp" #include "soulgemdialog.hpp" #include "companionwindow.hpp" #include "inventorywindow.hpp" #include "bookpage.hpp" #include "itemview.hpp" #include "videowidget.hpp" #include "backgroundimage.hpp" #include "itemwidget.hpp" #include "screenfader.hpp" #include "debugwindow.hpp" #include "spellview.hpp" #include "draganddrop.hpp" #include "container.hpp" #include "controllers.hpp" #include "jailscreen.hpp" namespace MWGui { WindowManager::WindowManager( osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem , const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription) : mStore(NULL) , mResourceSystem(resourceSystem) , mViewer(viewer) , mConsoleOnlyScripts(consoleOnlyScripts) , mCurrentModals() , mHud(NULL) , mMap(NULL) , mLocalMapRender(NULL) , mMenu(NULL) , mToolTips(NULL) , mStatsWindow(NULL) , mMessageBoxManager(NULL) , mConsole(NULL) , mJournal(NULL) , mDialogueWindow(NULL) , mContainerWindow(NULL) , mDragAndDrop(NULL) , mInventoryWindow(NULL) , mScrollWindow(NULL) , mBookWindow(NULL) , mCountDialog(NULL) , mTradeWindow(NULL) , mSpellBuyingWindow(NULL) , mTravelWindow(NULL) , mSettingsWindow(NULL) , mConfirmationDialog(NULL) , mAlchemyWindow(NULL) , mSpellWindow(NULL) , mQuickKeysMenu(NULL) , mLoadingScreen(NULL) , mLevelupDialog(NULL) , mWaitDialog(NULL) , mSpellCreationDialog(NULL) , mEnchantingDialog(NULL) , mTrainingWindow(NULL) , mMerchantRepair(NULL) , mSoulgemDialog(NULL) , mRepair(NULL) , mRecharge(NULL) , mCompanionWindow(NULL) , mVideoBackground(NULL) , mVideoWidget(NULL) , mWerewolfFader(NULL) , mBlindnessFader(NULL) , mHitFader(NULL) , mScreenFader(NULL) , mDebugWindow(NULL) , mJailScreen(NULL) , mTranslationDataStorage (translationDataStorage) , mCharGen(NULL) , mInputBlocker(NULL) , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mHitFaderEnabled(Settings::Manager::getBool ("hit fader", "GUI")) , mWerewolfOverlayEnabled(Settings::Manager::getBool ("werewolf overlay", "GUI")) , mHudEnabled(true) , mGuiEnabled(true) , mCursorVisible(true) , mPlayerName() , mPlayerRaceId() , mPlayerAttributes() , mPlayerMajorSkills() , mPlayerMinorSkills() , mPlayerSkillValues() , mGui(NULL) , mGuiModes() , mCursorManager(NULL) , mGarbageDialogs() , mShown(GW_ALL) , mForceHidden(GW_None) , mAllowed(GW_ALL) , mRestAllowed(true) , mFallbackMap(fallbackMap) , mShowOwned(0) , mVersionDescription(versionDescription) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getTextureManager(), uiScale); mGuiPlatform->initialise(resourcePath, logpath); mGui = new MyGUI::Gui; mGui->initialise(""); createTextures(); MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); // Load fonts mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS())); mFontLoader->loadAllFonts(exportFonts); //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Layer"); MyGUI::FactoryManager::getInstance().registerFactory("Layer"); BookPage::registerMyGUIComponents (); ItemView::registerComponents(); ItemWidget::registerComponents(); SpellView::registerComponents(); Gui::registerAllWidgets(); MyGUI::FactoryManager::getInstance().registerFactory("Controller"); MyGUI::FactoryManager::getInstance().registerFactory("Controller"); MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); MyGUI::ResourceManager::getInstance().load("core.xml"); mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer); //set up the hardware cursor manager mCursorManager = new SDLUtil::SDLCursorManager(); MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &WindowManager::onCursorChange); MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); // Create all cursors in advance createCursors(); onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); mCursorManager->setEnabled(true); // hide mygui's pointer MyGUI::PointerManager::getInstance().setVisible(false); mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Default, "InputBlocker"); mVideoBackground->setImageTexture("black"); mVideoBackground->setVisible(false); mVideoBackground->setNeedMouseFocus(true); mVideoBackground->setNeedKeyFocus(true); mVideoWidget = mVideoBackground->createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Default); mVideoWidget->setNeedMouseFocus(true); mVideoWidget->setNeedKeyFocus(true); mVideoWidget->setVFS(resourceSystem->getVFS()); // Removes default MyGUI system clipboard implementation, which supports windows only MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardChanged += MyGUI::newDelegate(this, &WindowManager::onClipboardChanged); MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested); mShowOwned = Settings::Manager::getInt("show owned", "Game"); } void WindowManager::initUI() { // Get size info from the Gui object int w = MyGUI::RenderManager::getInstance().getViewSize().width; int h = MyGUI::RenderManager::getInstance().getViewSize().height; mDragAndDrop = new DragAndDrop(); mRecharge = new Recharge(); mMenu = new MainMenu(w, h, mResourceSystem->getVFS(), mVersionDescription); mLocalMapRender = new MWRender::LocalMap(mViewer); mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender); trackWindow(mMap, "map"); mStatsWindow = new StatsWindow(mDragAndDrop); trackWindow(mStatsWindow, "stats"); mConsole = new Console(w,h, mConsoleOnlyScripts); trackWindow(mConsole, "console"); bool questList = mResourceSystem->getVFS()->exists("textures/tx_menubook_options_over.dds"); mJournal = JournalWindow::create(JournalViewModel::create (), questList); mMessageBoxManager = new MessageBoxManager(mStore->get().find("fMessageTimePerChar")->getFloat()); mInventoryWindow = new InventoryWindow(mDragAndDrop, mViewer, mResourceSystem); mTradeWindow = new TradeWindow(); trackWindow(mTradeWindow, "barter"); mSpellBuyingWindow = new SpellBuyingWindow(); mTravelWindow = new TravelWindow(); mDialogueWindow = new DialogueWindow(); trackWindow(mDialogueWindow, "dialogue"); mContainerWindow = new ContainerWindow(mDragAndDrop); trackWindow(mContainerWindow, "container"); mHud = new HUD(mCustomMarkers, mDragAndDrop, mLocalMapRender); mToolTips = new ToolTips(); mScrollWindow = new ScrollWindow(); mBookWindow = new BookWindow(); mCountDialog = new CountDialog(); mSettingsWindow = new SettingsWindow(); mConfirmationDialog = new ConfirmationDialog(); mAlchemyWindow = new AlchemyWindow(); trackWindow(mAlchemyWindow, "alchemy"); mSpellWindow = new SpellWindow(mDragAndDrop); trackWindow(mSpellWindow, "spells"); mQuickKeysMenu = new QuickKeysMenu(); mLevelupDialog = new LevelupDialog(); mWaitDialog = new WaitDialog(); mSpellCreationDialog = new SpellCreationDialog(); mEnchantingDialog = new EnchantingDialog(); mTrainingWindow = new TrainingWindow(); mMerchantRepair = new MerchantRepair(); mRepair = new Repair(); mSoulgemDialog = new SoulgemDialog(mMessageBoxManager); mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); trackWindow(mCompanionWindow, "companion"); mJailScreen = new JailScreen(); std::string werewolfFaderTex = "textures\\werewolfoverlay.dds"; if (mResourceSystem->getVFS()->exists(werewolfFaderTex)) mWerewolfFader = new ScreenFader(werewolfFaderTex); mBlindnessFader = new ScreenFader("black"); // fall back to player_hit_01.dds if bm_player_hit_01.dds is not available std::string hitFaderTexture = "textures\\bm_player_hit_01.dds"; const std::string hitFaderLayout = "openmw_screen_fader_hit.layout"; MyGUI::FloatCoord hitFaderCoord (0,0,1,1); if(!mResourceSystem->getVFS()->exists(hitFaderTexture)) { hitFaderTexture = "textures\\player_hit_01.dds"; hitFaderCoord = MyGUI::FloatCoord(0.2, 0.25, 0.6, 0.5); } mHitFader = new ScreenFader(hitFaderTexture, hitFaderLayout, hitFaderCoord); mScreenFader = new ScreenFader("black"); mDebugWindow = new DebugWindow(); mInputBlocker = MyGUI::Gui::getInstance().createWidget("",0,0,w,h,MyGUI::Align::Stretch,"InputBlocker"); mHud->setVisible(mHudEnabled); mCharGen = new CharacterCreation(mViewer, mResourceSystem); // Setup player stats for (int i = 0; i < ESM::Attribute::Length; ++i) { mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue())); } for (int i = 0; i < ESM::Skill::Length; ++i) { mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue())); } // Set up visibility updateVisible(); MWBase::Environment::get().getInputManager()->changeInputMode(false); } void WindowManager::renderWorldMap() { mMap->renderGlobalMap(mLoadingScreen); } void WindowManager::setNewGame(bool newgame) { // This method will always be called after loading a savegame or starting a new game // Reset enemy, it could be a dangling pointer from a previous game mHud->resetEnemy(); if (newgame) { disallowAll(); delete mCharGen; mCharGen = new CharacterCreation(mViewer, mResourceSystem); mGuiModes.clear(); MWBase::Environment::get().getInputManager()->changeInputMode(false); mHud->unsetSelectedWeapon(); mHud->unsetSelectedSpell(); unsetForceHide(GW_ALL); } else allow(GW_ALL); mRestAllowed = !newgame; } WindowManager::~WindowManager() { MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); delete mConsole; delete mMessageBoxManager; delete mHud; delete mMap; delete mLocalMapRender; delete mMenu; delete mStatsWindow; delete mJournal; delete mDialogueWindow; delete mContainerWindow; delete mInventoryWindow; delete mToolTips; delete mCharGen; delete mDragAndDrop; delete mBookWindow; delete mScrollWindow; delete mTradeWindow; delete mSpellBuyingWindow; delete mTravelWindow; delete mSettingsWindow; delete mConfirmationDialog; delete mAlchemyWindow; delete mSpellWindow; delete mLoadingScreen; delete mLevelupDialog; delete mWaitDialog; delete mSpellCreationDialog; delete mEnchantingDialog; delete mTrainingWindow; delete mCountDialog; delete mQuickKeysMenu; delete mMerchantRepair; delete mRepair; delete mSoulgemDialog; delete mRecharge; delete mCompanionWindow; delete mHitFader; delete mWerewolfFader; delete mScreenFader; delete mBlindnessFader; delete mDebugWindow; delete mJailScreen; delete mCursorManager; cleanupGarbage(); mFontLoader.reset(); mGui->shutdown(); delete mGui; mGuiPlatform->shutdown(); delete mGuiPlatform; } void WindowManager::setStore(const MWWorld::ESMStore &store) { mStore = &store; } void WindowManager::cleanupGarbage() { // Delete any dialogs which are no longer in use if (!mGarbageDialogs.empty()) { for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) { delete *it; } mGarbageDialogs.clear(); } } void WindowManager::update() { cleanupGarbage(); mHud->update(); } void WindowManager::updateVisible() { if (!mMap) return; // UI not created yet // Start out by hiding everything except the HUD mMap->setVisible(false); mMenu->setVisible(false); mStatsWindow->setVisible(false); mConsole->setVisible(false); mJournal->setVisible(false); mDialogueWindow->setVisible(false); mContainerWindow->setVisible(false); mInventoryWindow->setVisible(false); mScrollWindow->setVisible(false); mBookWindow->setVisible(false); mTradeWindow->setVisible(false); mSpellBuyingWindow->setVisible(false); mTravelWindow->setVisible(false); mSettingsWindow->setVisible(false); mAlchemyWindow->setVisible(false); mSpellWindow->setVisible(false); mQuickKeysMenu->setVisible(false); mLevelupDialog->setVisible(false); mWaitDialog->setVisible(false); mSpellCreationDialog->setVisible(false); mEnchantingDialog->setVisible(false); mTrainingWindow->setVisible(false); mMerchantRepair->setVisible(false); mRepair->setVisible(false); mCompanionWindow->setVisible(false); mInventoryWindow->setTrading(false); mRecharge->setVisible(false); mVideoBackground->setVisible(false); mJailScreen->setVisible(false); mHud->setVisible(mHudEnabled && mGuiEnabled); mToolTips->setVisible(mGuiEnabled); bool gameMode = !isGuiMode(); mInputBlocker->setVisible (gameMode); setCursorVisible(!gameMode); if (gameMode) setKeyFocusWidget (NULL); if (!mGuiEnabled) { if (containsMode(GM_Console)) mConsole->setVisible(true); return; } // Icons of forced hidden windows are displayed setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); setWeaponVisibility((mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory))); setSpellVisibility((mAllowed & GW_Magic) && (!mSpellWindow->pinned() || (mForceHidden & GW_Magic))); setHMSVisibility((mAllowed & GW_Stats) && (!mStatsWindow->pinned() || (mForceHidden & GW_Stats))); // If in game mode, show only the pinned windows if (gameMode) { mInventoryWindow->setGuiMode(GM_None); mMap->setVisible(mMap->pinned() && !(mForceHidden & GW_Map)); mStatsWindow->setVisible(mStatsWindow->pinned() && !(mForceHidden & GW_Stats)); mInventoryWindow->setVisible(mInventoryWindow->pinned() && !(mForceHidden & GW_Inventory)); mSpellWindow->setVisible(mSpellWindow->pinned() && !(mForceHidden & GW_Magic)); return; } if(mGuiModes.size() != 0) { GuiMode mode = mGuiModes.back(); switch(mode) { case GM_QuickKeysMenu: mQuickKeysMenu->setVisible (true); break; case GM_MainMenu: mMenu->setVisible(true); break; case GM_Settings: mSettingsWindow->setVisible(true); break; case GM_Console: mConsole->setVisible(true); break; case GM_Scroll: mScrollWindow->setVisible(true); break; case GM_Book: mBookWindow->setVisible(true); break; case GM_Alchemy: mAlchemyWindow->setVisible(true); break; case GM_Rest: mWaitDialog->setVisible(true); break; case GM_RestBed: mWaitDialog->setVisible(true); mWaitDialog->bedActivated(); break; case GM_Levelup: mLevelupDialog->setVisible(true); break; case GM_Name: case GM_Race: case GM_Class: case GM_ClassPick: case GM_ClassCreate: case GM_Birth: case GM_ClassGenerate: case GM_Review: mCharGen->spawnDialog(mode); break; case GM_Inventory: { // First, compute the effective set of windows to show. // This is controlled both by what windows the // user has opened/closed (the 'shown' variable) and by what // windows we are allowed to show (the 'allowed' var.) int eff = mShown & mAllowed & ~mForceHidden; // Show the windows we want mMap ->setVisible((eff & GW_Map) != 0); mStatsWindow ->setVisible((eff & GW_Stats) != 0); mInventoryWindow->setVisible((eff & GW_Inventory) != 0); mInventoryWindow->setGuiMode(mode); mSpellWindow ->setVisible((eff & GW_Magic) != 0); break; } case GM_Container: mContainerWindow->setVisible(true); mInventoryWindow->setVisible(true); mInventoryWindow->setGuiMode(mode); break; case GM_Companion: mCompanionWindow->setVisible(true); mInventoryWindow->setVisible(true); mInventoryWindow->setGuiMode(mode); break; case GM_Dialogue: mDialogueWindow->setVisible(true); break; case GM_Barter: mInventoryWindow->setVisible(true); mInventoryWindow->setTrading(true); mInventoryWindow->setGuiMode(mode); mTradeWindow->setVisible(true); break; case GM_SpellBuying: mSpellBuyingWindow->setVisible(true); break; case GM_Travel: mTravelWindow->setVisible(true); break; case GM_SpellCreation: mSpellCreationDialog->setVisible(true); break; case GM_Recharge: mRecharge->setVisible(true); break; case GM_Enchanting: mEnchantingDialog->setVisible(true); break; case GM_Training: mTrainingWindow->setVisible(true); break; case GM_MerchantRepair: mMerchantRepair->setVisible(true); break; case GM_Repair: mRepair->setVisible(true); break; case GM_Journal: mJournal->setVisible(true); break; case GM_Jail: mJailScreen->setVisible(true); break; case GM_LoadingWallpaper: case GM_Loading: // Don't need to show anything here - GM_LoadingWallpaper covers everything else anyway, // GM_Loading uses a texture of the last rendered frame so everything previously visible will be rendered. mHud->setVisible(false); mToolTips->setVisible(false); setCursorVisible(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox()); break; default: // Unsupported mode, switch back to game break; } } } void WindowManager::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { mStatsWindow->setValue (id, value); mCharGen->setValue(id, value); static const char *ids[] = { "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", "AttribVal6", "AttribVal7", "AttribVal8" }; static ESM::Attribute::AttributeID attributes[] = { ESM::Attribute::Strength, ESM::Attribute::Intelligence, ESM::Attribute::Willpower, ESM::Attribute::Agility, ESM::Attribute::Speed, ESM::Attribute::Endurance, ESM::Attribute::Personality, ESM::Attribute::Luck }; for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) { if (id != ids[i]) continue; mPlayerAttributes[attributes[i]] = value; break; } } void WindowManager::setValue (int parSkill, const MWMechanics::SkillValue& value) { /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we /// allow custom skills. mStatsWindow->setValue(static_cast (parSkill), value); mCharGen->setValue(static_cast (parSkill), value); mPlayerSkillValues[parSkill] = value; } void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { mStatsWindow->setValue (id, value); mHud->setValue (id, value); mCharGen->setValue(id, value); } void WindowManager::setValue (const std::string& id, const std::string& value) { mStatsWindow->setValue (id, value); if (id=="name") mPlayerName = value; else if (id=="race") mPlayerRaceId = value; } void WindowManager::setValue (const std::string& id, int value) { mStatsWindow->setValue (id, value); } void WindowManager::setDrowningTimeLeft (float time, float maxTime) { mHud->setDrowningTimeLeft(time, maxTime); } void WindowManager::setPlayerClass (const ESM::Class &class_) { mStatsWindow->setValue("class", class_.mName); } void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) { mStatsWindow->configureSkills (major, minor); mCharGen->configureSkills(major, minor); mPlayerMajorSkills = major; mPlayerMinorSkills = minor; } void WindowManager::updateSkillArea() { mStatsWindow->updateSkillArea(); } void WindowManager::removeDialog(Layout*dialog) { if (!dialog) return; dialog->setVisible(false); mGarbageDialogs.push_back(dialog); } void WindowManager::exitCurrentGuiMode() { switch(mGuiModes.back()) { case GM_QuickKeysMenu: mQuickKeysMenu->exit(); break; case GM_MainMenu: removeGuiMode(GM_MainMenu); //Simple way to remove it break; case GM_Settings: mSettingsWindow->exit(); break; case GM_Console: mConsole->exit(); break; case GM_Scroll: mScrollWindow->exit(); break; case GM_Book: mBookWindow->exit(); break; case GM_Alchemy: mAlchemyWindow->exit(); break; case GM_Rest: mWaitDialog->exit(); break; case GM_RestBed: mWaitDialog->exit(); break; case GM_Name: case GM_Race: case GM_Class: case GM_ClassPick: case GM_ClassCreate: case GM_Birth: case GM_ClassGenerate: case GM_Review: break; case GM_Inventory: removeGuiMode(GM_Inventory); //Simple way to remove it break; case GM_Container: mContainerWindow->exit(); break; case GM_Companion: mCompanionWindow->exit(); break; case GM_Dialogue: mDialogueWindow->exit(); break; case GM_Barter: mTradeWindow->exit(); break; case GM_SpellBuying: mSpellBuyingWindow->exit(); break; case GM_Travel: mTravelWindow->exit(); break; case GM_SpellCreation: mSpellCreationDialog->exit(); break; case GM_Recharge: mRecharge->exit(); break; case GM_Enchanting: mEnchantingDialog->exit(); break; case GM_Training: mTrainingWindow->exit(); break; case GM_MerchantRepair: mMerchantRepair->exit(); break; case GM_Repair: mRepair->exit(); break; case GM_Journal: MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); removeGuiMode(GM_Journal); //Simple way to remove it break; default: // Unsupported mode, switch back to game break; } } void WindowManager::interactiveMessageBox(const std::string &message, const std::vector &buttons, bool block) { mMessageBoxManager->createInteractiveMessageBox(message, buttons); MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); updateVisible(); if (block) { while (mMessageBoxManager->readPressedButton(false) == -1 && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) { mMessageBoxManager->onFrame(0.f); MWBase::Environment::get().getInputManager()->update(0, true, false); // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); } } } void WindowManager::messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode) { if (getMode() == GM_Dialogue && showInDialogueMode != MWGui::ShowInDialogueMode_Never) { mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); } else if (showInDialogueMode != MWGui::ShowInDialogueMode_Only) { mMessageBoxManager->createMessageBox(message); } } void WindowManager::staticMessageBox(const std::string& message) { mMessageBoxManager->createMessageBox(message, true); } void WindowManager::removeStaticMessageBox() { mMessageBoxManager->removeStaticMessageBox(); } int WindowManager::readPressedButton () { return mMessageBoxManager->readPressedButton(); } std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_) { const ESM::GameSetting *setting = mStore->get().search(id); if (setting && setting->mValue.getType()==ESM::VT_String) return setting->mValue.getString(); return default_; } void WindowManager::updateMap() { if (!mLocalMapRender) return; MWWorld::Ptr player = MWMechanics::getPlayer(); osg::Vec3f playerPosition = player.getRefData().getPosition().asVec3(); osg::Quat playerOrientation (-player.getRefData().getPosition().rot[2], osg::Vec3(0,0,1)); osg::Vec3f playerdirection; int x,y; float u,v; mLocalMapRender->updatePlayer(playerPosition, playerOrientation, u, v, x, y, playerdirection); if (!player.getCell()->isExterior()) { mMap->setActiveCell(x, y, true); mHud->setActiveCell(x, y, true); } // else: need to know the current grid center, call setActiveCell from MWWorld::Scene mMap->setPlayerDir(playerdirection.x(), playerdirection.y()); mMap->setPlayerPos(x, y, u, v); mHud->setPlayerDir(playerdirection.x(), playerdirection.y()); mHud->setPlayerPos(x, y, u, v); } void WindowManager::onFrame (float frameDuration) { mMessageBoxManager->onFrame(frameDuration); mToolTips->onFrame(frameDuration); mMenu->update(frameDuration); if (mLocalMapRender) mLocalMapRender->cleanupCameras(); if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_NoGame) return; if (mDragAndDrop->mIsOnDragAndDrop) { assert(mDragAndDrop->mDraggedWidget); mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); } mDialogueWindow->onFrame(); mInventoryWindow->onFrame(); updateMap(); mStatsWindow->onFrame(frameDuration); mMap->onFrame(frameDuration); mSpellWindow->onFrame(frameDuration); mWaitDialog->onFrame(frameDuration); mHud->onFrame(frameDuration); mTrainingWindow->onFrame (frameDuration); mTrainingWindow->checkReferenceAvailable(); mDialogueWindow->checkReferenceAvailable(); mTradeWindow->checkReferenceAvailable(); mSpellBuyingWindow->checkReferenceAvailable(); mSpellCreationDialog->checkReferenceAvailable(); mEnchantingDialog->checkReferenceAvailable(); mContainerWindow->checkReferenceAvailable(); mCompanionWindow->checkReferenceAvailable(); mConsole->checkReferenceAvailable(); mCompanionWindow->onFrame(); mJailScreen->onFrame(frameDuration); if (mWerewolfFader) mWerewolfFader->update(frameDuration); mBlindnessFader->update(frameDuration); mHitFader->update(frameDuration); mScreenFader->update(frameDuration); mDebugWindow->onFrame(frameDuration); } void WindowManager::changeCell(MWWorld::CellStore* cell) { std::string name = MWBase::Environment::get().getWorld()->getCellName (cell); mMap->setCellName( name ); mHud->setCellName( name ); if (cell->getCell()->isExterior()) { if (!cell->getCell()->mName.empty()) mMap->addVisitedLocation (name, cell->getCell()->getGridX (), cell->getCell()->getGridY ()); mMap->cellExplored (cell->getCell()->getGridX(), cell->getCell()->getGridY()); } else { mMap->setCellPrefix (cell->getCell()->mName ); mHud->setCellPrefix (cell->getCell()->mName ); osg::Vec3f worldPos; if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); else MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos); mMap->setGlobalMapPlayerPosition(worldPos.x(), worldPos.y()); } } void WindowManager::setActiveMap(int x, int y, bool interior) { mMap->setActiveCell(x,y, interior); mHud->setActiveCell(x,y, interior); } void WindowManager::setDrowningBarVisibility(bool visible) { mHud->setDrowningBarVisible(visible); } void WindowManager::setHMSVisibility(bool visible) { mHud->setHmsVisible (visible); } void WindowManager::setMinimapVisibility(bool visible) { mHud->setMinimapVisible (visible); } bool WindowManager::toggleFogOfWar() { mMap->toggleFogOfWar(); return mHud->toggleFogOfWar(); } void WindowManager::setFocusObject(const MWWorld::Ptr& focus) { mToolTips->setFocusObject(focus); if(mHud && (mShowOwned == 2 || mShowOwned == 3)) { bool owned = mToolTips->checkOwned(); mHud->setCrosshairOwned(owned); } } void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) { mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); } bool WindowManager::toggleFullHelp() { return mToolTips->toggleFullHelp(); } bool WindowManager::getFullHelp() const { return mToolTips->getFullHelp(); } void WindowManager::setWeaponVisibility(bool visible) { mHud->setWeapVisible (visible); } void WindowManager::setSpellVisibility(bool visible) { mHud->setSpellVisible (visible); mHud->setEffectVisible (visible); } void WindowManager::setSneakVisibility(bool visible) { mHud->setSneakVisible(visible); } void WindowManager::setDragDrop(bool dragDrop) { mToolTips->setEnabled(!dragDrop); MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); } void WindowManager::setCursorVisible(bool visible) { mCursorVisible = visible; } void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) { std::string tag(_tag); std::string MyGuiPrefix = "setting="; size_t MyGuiPrefixLength = MyGuiPrefix.length(); std::string tokenToFind = "sCell="; size_t tokenLength = tokenToFind.length(); if(tag.compare(0, MyGuiPrefixLength, MyGuiPrefix) == 0) { tag = tag.substr(MyGuiPrefixLength, tag.length()); std::string settingSection = tag.substr(0, tag.find(",")); std::string settingTag = tag.substr(tag.find(",")+1, tag.length()); _result = Settings::Manager::getString(settingTag, settingSection); } else if (tag.compare(0, tokenLength, tokenToFind) == 0) { _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); } else if (Gui::replaceTag(tag, _result, mFallbackMap)) { return; } else { if (!mStore) { std::cerr << "WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'" << std::endl; return; } const ESM::GameSetting *setting = mStore->get().find(tag); if (setting && setting->mValue.getType()==ESM::VT_String) _result = setting->mValue.getString(); else _result = tag; } } void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) { mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) { if (it->first == "HUD" && it->second == "crosshair") mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); else if (it->first == "GUI" && it->second == "subtitles") mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); else if (it->first == "GUI" && it->second == "menu transparency") setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); } } void WindowManager::windowResized(int x, int y) { mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y); // scaled size const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); x = viewSize.width; y = viewSize.height; sizeVideo(x, y); if (!mHud) return; // UI not initialized yet for (std::map::iterator it = mTrackedWindows.begin(); it != mTrackedWindows.end(); ++it) { MyGUI::IntPoint pos(static_cast(Settings::Manager::getFloat(it->second + " x", "Windows") * x), static_cast( Settings::Manager::getFloat(it->second+ " y", "Windows") * y)); MyGUI::IntSize size(static_cast(Settings::Manager::getFloat(it->second + " w", "Windows") * x), static_cast(Settings::Manager::getFloat(it->second + " h", "Windows") * y)); it->first->setPosition(pos); it->first->setSize(size); } mConsole->onResChange(x, y); mMenu->onResChange(x, y); mSettingsWindow->center(); mAlchemyWindow->center(); mScrollWindow->center(); mBookWindow->center(); mQuickKeysMenu->center(); mSpellBuyingWindow->center(); } void WindowManager::pushGuiMode(GuiMode mode) { if (mode==GM_Inventory && mAllowed==GW_None) return; // If this mode already exists somewhere in the stack, just bring it to the front. if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) { mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); } mGuiModes.push_back(mode); bool gameMode = !isGuiMode(); MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); updateVisible(); } void WindowManager::onCursorChange(const std::string &name) { mCursorManager->cursorChanged(name); } void WindowManager::popGuiMode() { if (!mGuiModes.empty()) mGuiModes.pop_back(); bool gameMode = !isGuiMode(); MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); updateVisible(); } void WindowManager::removeGuiMode(GuiMode mode) { std::vector::iterator it = mGuiModes.begin(); while (it != mGuiModes.end()) { if (*it == mode) it = mGuiModes.erase(it); else ++it; } bool gameMode = !isGuiMode(); MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); updateVisible(); } void WindowManager::goToJail(int days) { pushGuiMode(MWGui::GM_Jail); mJailScreen->goToJail(days); } void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) { mSelectedSpell = spellId; mHud->setSelectedSpell(spellId, successChancePercent); const ESM::Spell* spell = mStore->get().find(spellId); mSpellWindow->setTitle(spell->mName); } void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) { mSelectedSpell = ""; const ESM::Enchantment* ench = mStore->get() .find(item.getClass().getEnchantment(item)); int chargePercent = (item.getCellRef().getEnchantmentCharge() == -1) ? 100 : static_cast(item.getCellRef().getEnchantmentCharge() / static_cast(ench->mData.mCharge) * 100); mHud->setSelectedEnchantItem(item, chargePercent); mSpellWindow->setTitle(item.getClass().getName(item)); } void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) { int durabilityPercent = static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); } void WindowManager::unsetSelectedSpell() { mSelectedSpell = ""; mHud->unsetSelectedSpell(); MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer(); if (player->getDrawState() == MWMechanics::DrawState_Spell) player->setDrawState(MWMechanics::DrawState_Nothing); mSpellWindow->setTitle("#{sNone}"); } void WindowManager::unsetSelectedWeapon() { mHud->unsetSelectedWeapon(); mInventoryWindow->setTitle("#{sSkillHandtohand}"); } void WindowManager::getMousePosition(int &x, int &y) { const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); x = pos.left; y = pos.top; } void WindowManager::getMousePosition(float &x, float &y) { const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); x = static_cast(pos.left); y = static_cast(pos.top); const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); x /= viewSize.width; y /= viewSize.height; } bool WindowManager::getWorldMouseOver() { return mHud->getWorldMouseOver(); } void WindowManager::executeInConsole (const std::string& path) { mConsole->executeFile (path); } MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } void WindowManager::useItem(const MWWorld::Ptr &item) { if (mInventoryWindow) mInventoryWindow->useItem(item); } bool WindowManager::isAllowed (GuiWindow wnd) const { return (mAllowed & wnd) != 0; } void WindowManager::allow (GuiWindow wnd) { mAllowed = (GuiWindow)(mAllowed | wnd); if (wnd & GW_Inventory) { mBookWindow->setInventoryAllowed (true); mScrollWindow->setInventoryAllowed (true); } updateVisible(); } void WindowManager::disallowAll() { mAllowed = GW_None; mBookWindow->setInventoryAllowed (false); mScrollWindow->setInventoryAllowed (false); updateVisible(); } void WindowManager::toggleVisible (GuiWindow wnd) { if (getMode() != GM_Inventory) return; mShown = (GuiWindow)(mShown ^ wnd); updateVisible(); } void WindowManager::forceHide(GuiWindow wnd) { mForceHidden = (GuiWindow)(mForceHidden | wnd); updateVisible(); } void WindowManager::unsetForceHide(GuiWindow wnd) { mForceHidden = (GuiWindow)(mForceHidden & ~wnd); updateVisible(); } bool WindowManager::isGuiMode() const { return !mGuiModes.empty() || (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox()); } bool WindowManager::isConsoleMode() const { if (!mGuiModes.empty() && mGuiModes.back()==GM_Console) return true; return false; } MWGui::GuiMode WindowManager::getMode() const { if (mGuiModes.empty()) return GM_None; return mGuiModes.back(); } std::map WindowManager::getPlayerSkillValues() { return mPlayerSkillValues; } std::map WindowManager::getPlayerAttributeValues() { return mPlayerAttributes; } WindowManager::SkillList WindowManager::getPlayerMinorSkills() { return mPlayerMinorSkills; } WindowManager::SkillList WindowManager::getPlayerMajorSkills() { return mPlayerMajorSkills; } void WindowManager::disallowMouse() { mInputBlocker->setVisible (true); } void WindowManager::allowMouse() { mInputBlocker->setVisible (!isGuiMode ()); } void WindowManager::notifyInputActionBound () { mSettingsWindow->updateControlsBox (); allowMouse(); } bool WindowManager::containsMode(GuiMode mode) const { if(mGuiModes.empty()) return false; return std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end(); } void WindowManager::showCrosshair (bool show) { if (mHud) mHud->setCrosshairVisible (show && mCrosshairEnabled); } void WindowManager::activateQuickKey (int index) { mQuickKeysMenu->activateQuickKey(index); } bool WindowManager::getSubtitlesEnabled () { return mSubtitlesEnabled; } bool WindowManager::toggleGui() { mGuiEnabled = !mGuiEnabled; updateVisible(); return mGuiEnabled; } bool WindowManager::getRestEnabled() { //Enable rest dialogue if character creation finished if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) mRestAllowed=true; return mRestAllowed; } bool WindowManager::getPlayerSleeping () { return mWaitDialog->getSleeping(); } void WindowManager::wakeUpPlayer() { mWaitDialog->wakeUp(); } void WindowManager::addVisitedLocation(const std::string& name, int x, int y) { mMap->addVisitedLocation (name, x, y); } void WindowManager::startSpellMaking(MWWorld::Ptr actor) { pushGuiMode(GM_SpellCreation); mSpellCreationDialog->startSpellMaking (actor); } void WindowManager::startEnchanting (MWWorld::Ptr actor) { pushGuiMode(GM_Enchanting); mEnchantingDialog->startEnchanting (actor); } void WindowManager::startSelfEnchanting(MWWorld::Ptr soulgem) { mEnchantingDialog->startSelfEnchanting(soulgem); } void WindowManager::startTraining(MWWorld::Ptr actor) { pushGuiMode(GM_Training); mTrainingWindow->startTraining(actor); } void WindowManager::startRepair(MWWorld::Ptr actor) { pushGuiMode(GM_MerchantRepair); mMerchantRepair->startRepair(actor); } void WindowManager::startRepairItem(MWWorld::Ptr item) { pushGuiMode(MWGui::GM_Repair); mRepair->startRepairItem(item); } const Translation::Storage& WindowManager::getTranslationDataStorage() const { return mTranslationDataStorage; } void WindowManager::showCompanionWindow(MWWorld::Ptr actor) { pushGuiMode(MWGui::GM_Companion); mCompanionWindow->openCompanion(actor); } void WindowManager::changePointer(const std::string &name) { MyGUI::PointerManager::getInstance().setPointer(name); onCursorChange(name); } void WindowManager::showSoulgemDialog(MWWorld::Ptr item) { mSoulgemDialog->show(item); MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); updateVisible(); } void WindowManager::updatePlayer() { mInventoryWindow->updatePlayer(); const MWWorld::Ptr player = MWMechanics::getPlayer(); if (player.getClass().getNpcStats(player).isWerewolf()) { setWerewolfOverlay(true); forceHide((GuiWindow)(MWGui::GW_Inventory | MWGui::GW_Magic)); } } // Remove this method for MyGUI 3.2.2 void WindowManager::setKeyFocusWidget(MyGUI::Widget *widget) { if (widget == NULL) MyGUI::InputManager::getInstance().resetKeyFocusWidget(); else MyGUI::InputManager::getInstance().setKeyFocusWidget(widget); onKeyFocusChanged(widget); } void WindowManager::onKeyFocusChanged(MyGUI::Widget *widget) { if (widget && widget->castType(false)) SDL_StartTextInput(); else SDL_StopTextInput(); } void WindowManager::setEnemy(const MWWorld::Ptr &enemy) { mHud->setEnemy(enemy); } Loading::Listener* WindowManager::getLoadingScreen() { return mLoadingScreen; } void WindowManager::startRecharge(MWWorld::Ptr soulgem) { mRecharge->start(soulgem); } bool WindowManager::getCursorVisible() { return mCursorVisible; } void WindowManager::trackWindow(Layout *layout, const std::string &name) { MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntPoint pos(static_cast(Settings::Manager::getFloat(name + " x", "Windows") * viewSize.width), static_cast(Settings::Manager::getFloat(name + " y", "Windows") * viewSize.height)); MyGUI::IntSize size (static_cast(Settings::Manager::getFloat(name + " w", "Windows") * viewSize.width), static_cast(Settings::Manager::getFloat(name + " h", "Windows") * viewSize.height)); layout->mMainWidget->setPosition(pos); layout->mMainWidget->setSize(size); MyGUI::Window* window = dynamic_cast(layout->mMainWidget); if (!window) throw std::runtime_error("Attempting to track size of a non-resizable window"); window->eventWindowChangeCoord += MyGUI::newDelegate(this, &WindowManager::onWindowChangeCoord); mTrackedWindows[window] = name; } void WindowManager::onWindowChangeCoord(MyGUI::Window *_sender) { std::string setting = mTrackedWindows[_sender]; MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); float x = _sender->getPosition().left / float(viewSize.width); float y = _sender->getPosition().top / float(viewSize.height); float w = _sender->getSize().width / float(viewSize.width); float h = _sender->getSize().height / float(viewSize.height); Settings::Manager::setFloat(setting + " x", "Windows", x); Settings::Manager::setFloat(setting + " y", "Windows", y); Settings::Manager::setFloat(setting + " w", "Windows", w); Settings::Manager::setFloat(setting + " h", "Windows", h); } void WindowManager::clear() { if (mLocalMapRender) mLocalMapRender->clear(); mMap->clear(); mQuickKeysMenu->clear(); mMessageBoxManager->clear(); mTrainingWindow->resetReference(); mDialogueWindow->resetReference(); mTradeWindow->resetReference(); mSpellBuyingWindow->resetReference(); mSpellCreationDialog->resetReference(); mEnchantingDialog->resetReference(); mContainerWindow->resetReference(); mCompanionWindow->resetReference(); mConsole->resetReference(); mSelectedSpell.clear(); mCustomMarkers.clear(); mForceHidden = GW_None; setWerewolfOverlay(false); mGuiModes.clear(); MWBase::Environment::get().getInputManager()->changeInputMode(false); updateVisible(); } void WindowManager::write(ESM::ESMWriter &writer, Loading::Listener& progress) { mMap->write(writer, progress); mQuickKeysMenu->write(writer); if (!mSelectedSpell.empty()) { writer.startRecord(ESM::REC_ASPL); writer.writeHNString("ID__", mSelectedSpell); writer.endRecord(ESM::REC_ASPL); } for (CustomMarkerCollection::ContainerType::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) { writer.startRecord(ESM::REC_MARK); it->second.save(writer); writer.endRecord(ESM::REC_MARK); } } void WindowManager::readRecord(ESM::ESMReader &reader, uint32_t type) { if (type == ESM::REC_GMAP) mMap->readRecord(reader, type); else if (type == ESM::REC_KEYS) mQuickKeysMenu->readRecord(reader, type); else if (type == ESM::REC_ASPL) { reader.getSubNameIs("ID__"); std::string spell = reader.getHString(); if (mStore->get().search(spell)) mSelectedSpell = spell; } else if (type == ESM::REC_MARK) { ESM::CustomMarker marker; marker.load(reader); mCustomMarkers.addMarker(marker, false); } } int WindowManager::countSavedGameRecords() const { return 1 // Global map + 1 // QuickKeysMenu + mCustomMarkers.size() + (!mSelectedSpell.empty() ? 1 : 0); } bool WindowManager::isSavingAllowed() const { return !MyGUI::InputManager::getInstance().isModalAny() // TODO: remove this, once we have properly serialized the state of open windows && (!isGuiMode() || (mGuiModes.size() == 1 && (getMode() == GM_MainMenu || getMode() == GM_Rest || getMode() == GM_RestBed))); } void WindowManager::playVideo(const std::string &name, bool allowSkipping) { mVideoWidget->playVideo("video\\" + name); mVideoWidget->eventKeyButtonPressed.clear(); mVideoBackground->eventKeyButtonPressed.clear(); if (allowSkipping) { mVideoWidget->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed); mVideoBackground->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed); } // Turn off all rendering except for the GUI int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); int oldCullMask = mViewer->getCamera()->getCullMask(); mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI); mViewer->getCamera()->setCullMask(MWRender::Mask_GUI); MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); sizeVideo(screenSize.width, screenSize.height); MyGUI::Widget* oldKeyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); setKeyFocusWidget(mVideoWidget); mVideoBackground->setVisible(true); bool cursorWasVisible = mCursorVisible; setCursorVisible(false); if (mVideoWidget->hasAudioStream()) MWBase::Environment::get().getSoundManager()->pauseSounds( MWBase::SoundManager::Play_TypeMask&(~MWBase::SoundManager::Play_TypeMovie)); while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) { MWBase::Environment::get().getInputManager()->update(0, true, false); // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); } mVideoWidget->stop(); MWBase::Environment::get().getSoundManager()->resumeSounds(); setKeyFocusWidget(oldKeyFocus); setCursorVisible(cursorWasVisible); // Restore normal rendering mViewer->getUpdateVisitor()->setTraversalMask(oldUpdateMask); mViewer->getCamera()->setCullMask(oldCullMask); mVideoBackground->setVisible(false); } void WindowManager::sizeVideo(int screenWidth, int screenHeight) { // Use black bars to correct aspect ratio bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); mVideoBackground->setSize(screenWidth, screenHeight); mVideoWidget->autoResize(stretch); } void WindowManager::exitCurrentModal() { if (!mCurrentModals.empty()) mCurrentModals.top()->exit(); } void WindowManager::removeCurrentModal(WindowModal* input) { // Only remove the top if it matches the current pointer. A lot of things hide their visibility before showing it, //so just popping the top would cause massive issues. if(!mCurrentModals.empty()) if(input == mCurrentModals.top()) mCurrentModals.pop(); } void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) { if (_key == MyGUI::KeyCode::Escape) mVideoWidget->stop(); } void WindowManager::pinWindow(GuiWindow window) { switch (window) { case GW_Inventory: mInventoryWindow->setPinned(true); break; case GW_Map: mMap->setPinned(true); break; case GW_Magic: mSpellWindow->setPinned(true); break; case GW_Stats: mStatsWindow->setPinned(true); break; default: break; } updateVisible(); } void WindowManager::fadeScreenIn(const float time, bool clearQueue) { if (clearQueue) mScreenFader->clearQueue(); mScreenFader->fadeOut(time); } void WindowManager::fadeScreenOut(const float time, bool clearQueue) { if (clearQueue) mScreenFader->clearQueue(); mScreenFader->fadeIn(time); } void WindowManager::fadeScreenTo(const int percent, const float time, bool clearQueue) { if (clearQueue) mScreenFader->clearQueue(); mScreenFader->fadeTo(percent, time); } void WindowManager::setBlindness(const int percent) { mBlindnessFader->notifyAlphaChanged(percent / 100.f); } void WindowManager::activateHitOverlay(bool interrupt) { if (!mHitFaderEnabled) return; if (!interrupt && !mHitFader->isEmpty()) return; mHitFader->clearQueue(); mHitFader->fadeTo(100, 0.0f); mHitFader->fadeTo(0, 0.5f); } void WindowManager::setWerewolfOverlay(bool set) { if (!mWerewolfOverlayEnabled) return; if (mWerewolfFader) mWerewolfFader->notifyAlphaChanged(set ? 1.0f : 0.0f); } void WindowManager::onClipboardChanged(const std::string &_type, const std::string &_data) { if (_type == "Text") SDL_SetClipboardText(MyGUI::TextIterator::getOnlyText(MyGUI::UString(_data)).asUTF8().c_str()); } void WindowManager::onClipboardRequested(const std::string &_type, std::string &_data) { if (_type != "Text") return; char* text=0; text = SDL_GetClipboardText(); if (text) { // MyGUI's clipboard might still have color information, to retain that information, only set the new text // if it actually changed (clipboard inserted by an external application) if (MyGUI::TextIterator::getOnlyText(_data) != text) _data = text; } SDL_free(text); } void WindowManager::toggleDebugWindow() { mDebugWindow->setVisible(!mDebugWindow->isVisible()); } void WindowManager::cycleSpell(bool next) { mSpellWindow->cycle(next); } void WindowManager::cycleWeapon(bool next) { mInventoryWindow->cycle(next); } void WindowManager::setConsoleSelectedObject(const MWWorld::Ptr &object) { mConsole->setSelectedObject(object); } void WindowManager::updateSpellWindow() { if (mSpellWindow) mSpellWindow->updateSpells(); } void WindowManager::startTravel(const MWWorld::Ptr &actor) { pushGuiMode(GM_Travel); mTravelWindow->startTravel(actor); } void WindowManager::startSpellBuying(const MWWorld::Ptr &actor) { pushGuiMode(GM_SpellBuying); mSpellBuyingWindow->startSpellBuying(actor); } void WindowManager::startTrade(const MWWorld::Ptr &actor) { pushGuiMode(GM_Barter); mTradeWindow->startTrade(actor); } void WindowManager::openContainer(const MWWorld::Ptr &container, bool loot) { pushGuiMode(GM_Container); mContainerWindow->openContainer(container, loot); } void WindowManager::showBook(const MWWorld::Ptr &item, bool showTakeButton) { pushGuiMode(GM_Book); mBookWindow->openBook(item, showTakeButton); } void WindowManager::showScroll(const MWWorld::Ptr &item, bool showTakeButton) { pushGuiMode(GM_Scroll); mScrollWindow->openScroll(item, showTakeButton); } std::string WindowManager::correctIconPath(const std::string& path) { return Misc::ResourceHelpers::correctIconPath(path, mResourceSystem->getVFS()); } std::string WindowManager::correctBookartPath(const std::string& path, int width, int height) { return Misc::ResourceHelpers::correctBookartPath(path, width, height, mResourceSystem->getVFS()); } std::string WindowManager::correctTexturePath(const std::string& path) { return Misc::ResourceHelpers::correctTexturePath(path, mResourceSystem->getVFS()); } void WindowManager::createCursors() { MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator(); while (enumerator.next()) { MyGUI::IResource* resource = enumerator.current().second; ResourceImageSetPointerFix* imgSetPointer = dynamic_cast(resource); if (!imgSetPointer) continue; std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0,0).texture; osg::ref_ptr tex = mResourceSystem->getTextureManager()->getTexture2D(tex_name, osg::Texture::CLAMP, osg::Texture::CLAMP); if(tex.valid()) { //everything looks good, send it to the cursor manager Uint8 size_x = imgSetPointer->getSize().width; Uint8 size_y = imgSetPointer->getSize().height; Uint8 hotspot_x = imgSetPointer->getHotSpot().left; Uint8 hotspot_y = imgSetPointer->getHotSpot().top; int rotation = imgSetPointer->getRotation(); mCursorManager->createCursor(imgSetPointer->getResourceName(), rotation, tex->getImage(), size_x, size_y, hotspot_x, hotspot_y); } } } void WindowManager::createTextures() { { MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture("white"); tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8); unsigned char* data = reinterpret_cast(tex->lock(MyGUI::TextureUsage::Write)); for (int x=0; x<8; ++x) for (int y=0; y<8; ++y) { *(data++) = 255; *(data++) = 255; *(data++) = 255; } tex->unlock(); } { MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture("black"); tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8); unsigned char* data = reinterpret_cast(tex->lock(MyGUI::TextureUsage::Write)); for (int x=0; x<8; ++x) for (int y=0; y<8; ++y) { *(data++) = 0; *(data++) = 0; *(data++) = 0; } tex->unlock(); } { MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture("transparent"); tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8A8); setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); } } void WindowManager::setMenuTransparency(float value) { MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().getTexture("transparent"); unsigned char* data = reinterpret_cast(tex->lock(MyGUI::TextureUsage::Write)); for (int x=0; x<8; ++x) for (int y=0; y<8; ++y) { *(data++) = 255; *(data++) = 255; *(data++) = 255; *(data++) = static_cast(value*255); } tex->unlock(); } void WindowManager::requestMap(std::set cells) { mLocalMapRender->requestMap(cells); } void WindowManager::removeCell(MWWorld::CellStore *cell) { mLocalMapRender->removeCell(cell); } void WindowManager::writeFog(MWWorld::CellStore *cell) { mLocalMapRender->saveFogOfWar(cell); } } openmw-openmw-0.38.0/apps/openmw/mwgui/windowmanagerimp.hpp000066400000000000000000000443531264522266000241110ustar00rootroot00000000000000#ifndef MWGUI_WINDOWMANAGERIMP_H #define MWGUI_WINDOWMANAGERIMP_H /** This class owns and controls all the MW specific windows in the GUI. It can enable/disable Gui mode, and is responsible for sending and retrieving information from the Gui. **/ #include #include "../mwbase/windowmanager.hpp" #include #include #include "mapwindow.hpp" #include #include namespace MyGUI { class Gui; class Widget; class Window; class UString; class ImageBox; } namespace MWWorld { class ESMStore; } namespace Compiler { class Extensions; } namespace Translation { class Storage; } namespace osg { class Group; } namespace osgViewer { class Viewer; } namespace Resource { class ResourceSystem; } namespace SDLUtil { class SDLCursorManager; } namespace osgMyGUI { class Platform; } namespace Gui { class FontLoader; } namespace MWRender { class LocalMap; } namespace MWGui { class WindowBase; class HUD; class MapWindow; class MainMenu; class StatsWindow; class InventoryWindow; struct JournalWindow; class CharacterCreation; class DragAndDrop; class ToolTips; class TextInputDialog; class InfoBoxDialog; class MessageBoxManager; class SettingsWindow; class AlchemyWindow; class QuickKeysMenu; class LoadingScreen; class LevelupDialog; class WaitDialog; class SpellCreationDialog; class EnchantingDialog; class TrainingWindow; class SpellIcons; class MerchantRepair; class Repair; class SoulgemDialog; class Recharge; class CompanionWindow; class VideoWidget; class WindowModal; class ScreenFader; class DebugWindow; class JailScreen; class WindowManager : public MWBase::WindowManager { public: typedef std::pair Faction; typedef std::vector FactionList; WindowManager(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription); virtual ~WindowManager(); /// Set the ESMStore to use for retrieving of GUI-related strings. void setStore (const MWWorld::ESMStore& store); void initUI(); void renderWorldMap(); virtual Loading::Listener* getLoadingScreen(); /// @note This method will block until the video finishes playing /// (and will continually update the window while doing so) virtual void playVideo(const std::string& name, bool allowSkipping); /** * Should be called each frame to update windows/gui elements. * This could mean updating sizes of gui elements or opening * new dialogs. */ virtual void update(); /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget); virtual void setNewGame(bool newgame); virtual void pushGuiMode(GuiMode mode); virtual void popGuiMode(); virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack virtual void goToJail(int days); virtual GuiMode getMode() const; virtual bool containsMode(GuiMode mode) const; virtual bool isGuiMode() const; virtual bool isConsoleMode() const; virtual void toggleVisible(GuiWindow wnd); virtual void forceHide(MWGui::GuiWindow wnd); virtual void unsetForceHide(MWGui::GuiWindow wnd); /// Disallow all inventory mode windows virtual void disallowAll(); /// Allow one or more windows virtual void allow(GuiWindow wnd); virtual bool isAllowed(GuiWindow wnd) const; /// \todo investigate, if we really need to expose every single lousy UI element to the outside world virtual MWGui::DialogueWindow* getDialogueWindow(); virtual MWGui::InventoryWindow* getInventoryWindow(); virtual MWGui::CountDialog* getCountDialog(); virtual MWGui::ConfirmationDialog* getConfirmationDialog(); virtual MWGui::TradeWindow* getTradeWindow(); /// Make the player use an item, while updating GUI state accordingly virtual void useItem(const MWWorld::Ptr& item); virtual void updateSpellWindow(); virtual void setConsoleSelectedObject(const MWWorld::Ptr& object); ///< Set value for the given ID. virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value); virtual void setValue (int parSkill, const MWMechanics::SkillValue& value); virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); virtual void setValue (const std::string& id, const std::string& value); virtual void setValue (const std::string& id, int value); /// Set time left for the player to start drowning (update the drowning bar) /// @param time time left to start drowning /// @param maxTime how long we can be underwater (in total) until drowning starts virtual void setDrowningTimeLeft (float time, float maxTime); virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. virtual void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty virtual void changeCell(MWWorld::CellStore* cell); ///< change the active cell virtual void setFocusObject(const MWWorld::Ptr& focus); virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); virtual void getMousePosition(int &x, int &y); virtual void getMousePosition(float &x, float &y); virtual void setDragDrop(bool dragDrop); virtual bool getWorldMouseOver(); virtual bool toggleFogOfWar(); virtual bool toggleFullHelp(); ///< show extra info in item tooltips (owner, script) virtual bool getFullHelp() const; virtual void setActiveMap(int x, int y, bool interior); ///< set the indices of the map texture that should be used /// sets the visibility of the drowning bar virtual void setDrowningBarVisibility(bool visible); // sets the visibility of the hud health/magicka/stamina bars virtual void setHMSVisibility(bool visible); // sets the visibility of the hud minimap virtual void setMinimapVisibility(bool visible); virtual void setWeaponVisibility(bool visible); virtual void setSpellVisibility(bool visible); virtual void setSneakVisibility(bool visible); virtual void activateQuickKey (int index); virtual std::string getSelectedSpell() { return mSelectedSpell; } virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedEnchantItem(const MWWorld::Ptr& item); virtual void setSelectedWeapon(const MWWorld::Ptr& item); virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); virtual void showCrosshair(bool show); virtual bool getSubtitlesEnabled(); /// Turn visibility of *all* GUI elements on or off (HUD and all windows, except the console) virtual bool toggleGui(); virtual void disallowMouse(); virtual void allowMouse(); virtual void notifyInputActionBound(); virtual void addVisitedLocation(const std::string& name, int x, int y); ///Hides dialog and schedules dialog to be deleted. virtual void removeDialog(Layout* dialog); ///Gracefully attempts to exit the topmost GUI mode virtual void exitCurrentGuiMode(); virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible); virtual void staticMessageBox(const std::string& message); virtual void removeStaticMessageBox(); virtual void interactiveMessageBox (const std::string& message, const std::vector& buttons = std::vector(), bool block=false); virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual void onFrame (float frameDuration); /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. virtual std::map getPlayerSkillValues(); virtual std::map getPlayerAttributeValues(); virtual SkillList getPlayerMinorSkills(); virtual SkillList getPlayerMajorSkills(); /** * Fetches a GMST string from the store, if there is no setting with the given * ID or it is not a string the default string is returned. * * @param id Identifier for the GMST setting, e.g. "aName" * @param default Default value if the GMST setting cannot be used. */ virtual std::string getGameSettingString(const std::string &id, const std::string &default_); virtual void processChangedSettings(const Settings::CategorySettingVector& changed); virtual void windowResized(int x, int y); virtual void executeInConsole (const std::string& path); virtual void enableRest() { mRestAllowed = true; } virtual bool getRestEnabled(); virtual bool getJournalAllowed() { return (mAllowed & GW_Magic) != 0; } virtual bool getPlayerSleeping(); virtual void wakeUpPlayer(); virtual void updatePlayer(); virtual void showCompanionWindow(MWWorld::Ptr actor); virtual void startSpellMaking(MWWorld::Ptr actor); virtual void startEnchanting(MWWorld::Ptr actor); virtual void startSelfEnchanting(MWWorld::Ptr soulgem); virtual void startTraining(MWWorld::Ptr actor); virtual void startRepair(MWWorld::Ptr actor); virtual void startRepairItem(MWWorld::Ptr item); virtual void startRecharge(MWWorld::Ptr soulgem); virtual void startTravel(const MWWorld::Ptr& actor); virtual void startSpellBuying(const MWWorld::Ptr &actor); virtual void startTrade(const MWWorld::Ptr &actor); virtual void openContainer(const MWWorld::Ptr &container, bool loot); virtual void showBook(const MWWorld::Ptr& item, bool showTakeButton); virtual void showScroll(const MWWorld::Ptr& item, bool showTakeButton); virtual void showSoulgemDialog (MWWorld::Ptr item); virtual void changePointer (const std::string& name); virtual void setEnemy (const MWWorld::Ptr& enemy); virtual const Translation::Storage& getTranslationDataStorage() const; void onSoulgemDialogButtonPressed (int button); virtual bool getCursorVisible(); /// Clear all savegame-specific data virtual void clear(); virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress); virtual void readRecord (ESM::ESMReader& reader, uint32_t type); virtual int countSavedGameRecords() const; /// Does the current stack of GUI-windows permit saving? virtual bool isSavingAllowed() const; /// Send exit command to active Modal window **/ virtual void exitCurrentModal(); /// Sets the current Modal /** Used to send exit command to active Modal when Esc is pressed **/ virtual void addCurrentModal(WindowModal* input) {mCurrentModals.push(input);} /// Removes the top Modal /** Used when one Modal adds another Modal \param input Pointer to the current modal, to ensure proper modal is removed **/ virtual void removeCurrentModal(WindowModal* input); virtual void pinWindow (MWGui::GuiWindow window); /// Fade the screen in, over \a time seconds virtual void fadeScreenIn(const float time, bool clearQueue); /// Fade the screen out to black, over \a time seconds virtual void fadeScreenOut(const float time, bool clearQueue); /// Fade the screen to a specified percentage of black, over \a time seconds virtual void fadeScreenTo(const int percent, const float time, bool clearQueue); /// Darken the screen to a specified percentage virtual void setBlindness(const int percent); virtual void activateHitOverlay(bool interrupt); virtual void setWerewolfOverlay(bool set); virtual void toggleDebugWindow(); /// Cycle to next or previous spell virtual void cycleSpell(bool next); /// Cycle to next or previous weapon virtual void cycleWeapon(bool next); // In WindowManager for now since there isn't a VFS singleton virtual std::string correctIconPath(const std::string& path); virtual std::string correctBookartPath(const std::string& path, int width, int height); virtual std::string correctTexturePath(const std::string& path); void requestMap(std::set cells); void removeCell(MWWorld::CellStore* cell); void writeFog(MWWorld::CellStore* cell); private: const MWWorld::ESMStore* mStore; Resource::ResourceSystem* mResourceSystem; osgMyGUI::Platform* mGuiPlatform; osgViewer::Viewer* mViewer; std::auto_ptr mFontLoader; bool mConsoleOnlyScripts; std::map mTrackedWindows; void trackWindow(Layout* layout, const std::string& name); void onWindowChangeCoord(MyGUI::Window* _sender); std::string mSelectedSpell; std::stack mCurrentModals; // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). CustomMarkerCollection mCustomMarkers; HUD *mHud; MapWindow *mMap; MWRender::LocalMap* mLocalMapRender; MainMenu *mMenu; ToolTips *mToolTips; StatsWindow *mStatsWindow; MessageBoxManager *mMessageBoxManager; Console *mConsole; JournalWindow* mJournal; DialogueWindow *mDialogueWindow; ContainerWindow *mContainerWindow; DragAndDrop* mDragAndDrop; InventoryWindow *mInventoryWindow; ScrollWindow* mScrollWindow; BookWindow* mBookWindow; CountDialog* mCountDialog; TradeWindow* mTradeWindow; SpellBuyingWindow* mSpellBuyingWindow; TravelWindow* mTravelWindow; SettingsWindow* mSettingsWindow; ConfirmationDialog* mConfirmationDialog; AlchemyWindow* mAlchemyWindow; SpellWindow* mSpellWindow; QuickKeysMenu* mQuickKeysMenu; LoadingScreen* mLoadingScreen; LevelupDialog* mLevelupDialog; WaitDialog* mWaitDialog; SpellCreationDialog* mSpellCreationDialog; EnchantingDialog* mEnchantingDialog; TrainingWindow* mTrainingWindow; MerchantRepair* mMerchantRepair; SoulgemDialog* mSoulgemDialog; Repair* mRepair; Recharge* mRecharge; CompanionWindow* mCompanionWindow; MyGUI::ImageBox* mVideoBackground; VideoWidget* mVideoWidget; ScreenFader* mWerewolfFader; ScreenFader* mBlindnessFader; ScreenFader* mHitFader; ScreenFader* mScreenFader; DebugWindow* mDebugWindow; JailScreen* mJailScreen; Translation::Storage& mTranslationDataStorage; CharacterCreation* mCharGen; MyGUI::Widget* mInputBlocker; bool mCrosshairEnabled; bool mSubtitlesEnabled; bool mHitFaderEnabled; bool mWerewolfOverlayEnabled; bool mHudEnabled; bool mGuiEnabled; bool mCursorVisible; void setCursorVisible(bool visible); /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager std::string mPlayerName; std::string mPlayerRaceId; std::map mPlayerAttributes; SkillList mPlayerMajorSkills, mPlayerMinorSkills; std::map mPlayerSkillValues; MyGUI::Gui *mGui; // Gui std::vector mGuiModes; SDLUtil::SDLCursorManager* mCursorManager; std::vector mGarbageDialogs; void cleanupGarbage(); GuiWindow mShown; // Currently shown windows in inventory mode GuiWindow mForceHidden; // Hidden windows (overrides mShown) /* Currently ALLOWED windows in inventory mode. This is used at the start of the game, when windows are enabled one by one through script commands. You can manipulate this through using allow() and disableAll(). */ GuiWindow mAllowed; // is the rest window allowed? bool mRestAllowed; void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings void updateMap(); std::map mFallbackMap; int mShowOwned; std::string mVersionDescription; /** * Called when MyGUI tries to retrieve a tag's value. Tags must be denoted in #{tag} notation and will be replaced upon setting a user visible text/property. * Supported syntax: * #{GMSTName}: retrieves String value of the GMST called GMSTName * #{setting=CATEGORY_NAME,SETTING_NAME}: retrieves String value of SETTING_NAME under category CATEGORY_NAME from settings.cfg * #{sCell=CellID}: retrieves translated name of the given CellID (used only by some Morrowind localisations, in others cell ID is == cell name) * #{fontcolour=FontColourName}: retrieves the value of the fallback setting "FontColor_color_" from openmw.cfg, * in the format "r g b a", float values in range 0-1. Useful for "Colour" and "TextColour" properties in skins. * #{fontcolourhtml=FontColourName}: retrieves the value of the fallback setting "FontColor_color_" from openmw.cfg, * in the format "#xxxxxx" where x are hexadecimal numbers. Useful in an EditBox's caption to change the color of following text. */ void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); void onCursorChange(const std::string& name); void onKeyFocusChanged(MyGUI::Widget* widget); // Key pressed while playing a video void onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char); void sizeVideo(int screenWidth, int screenHeight); void onClipboardChanged(const std::string& _type, const std::string& _data); void onClipboardRequested(const std::string& _type, std::string& _data); void createTextures(); void createCursors(); void setMenuTransparency(float value); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwgui/windowpinnablebase.cpp000066400000000000000000000033441264522266000244020ustar00rootroot00000000000000#include "windowpinnablebase.hpp" #include #include "exposedwindow.hpp" namespace MWGui { WindowPinnableBase::WindowPinnableBase(const std::string& parLayout) : WindowBase(parLayout), mPinned(false) { ExposedWindow* window = mMainWidget->castType(); mPinButton = window->getSkinWidget ("Button"); mPinButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonPressed); MyGUI::Button* button = NULL; MyGUI::VectorWidgetPtr widgets = window->getSkinWidgetsByName("Action"); for (MyGUI::VectorWidgetPtr::iterator it = widgets.begin(); it != widgets.end(); ++it) { if ((*it)->isUserString("HideWindowOnDoubleClick")) button = (*it)->castType(); } if (button) button->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &WindowPinnableBase::onDoubleClick); } void WindowPinnableBase::onPinButtonPressed(MyGUI::Widget* _sender, int left, int top, MyGUI::MouseButton id) { if (id != MyGUI::MouseButton::Left) return; mPinned = !mPinned; if (mPinned) mPinButton->changeWidgetSkin ("PinDown"); else mPinButton->changeWidgetSkin ("PinUp"); onPinToggled(); } void WindowPinnableBase::onDoubleClick(MyGUI::Widget *_sender) { onTitleDoubleClicked(); } void WindowPinnableBase::setPinned(bool pinned) { if (pinned != mPinned) onPinButtonPressed(mPinButton, 0, 0, MyGUI::MouseButton::Left); } void WindowPinnableBase::setPinButtonVisible(bool visible) { mPinButton->setVisible(visible); } } openmw-openmw-0.38.0/apps/openmw/mwgui/windowpinnablebase.hpp000066400000000000000000000013501264522266000244020ustar00rootroot00000000000000#ifndef MWGUI_WINDOW_PINNABLE_BASE_H #define MWGUI_WINDOW_PINNABLE_BASE_H #include "windowbase.hpp" namespace MWGui { class WindowManager; class WindowPinnableBase: public WindowBase { public: WindowPinnableBase(const std::string& parLayout); bool pinned() { return mPinned; } void setPinned (bool pinned); void setPinButtonVisible(bool visible); private: void onPinButtonPressed(MyGUI::Widget* _sender, int left, int top, MyGUI::MouseButton id); void onDoubleClick(MyGUI::Widget* _sender); protected: virtual void onPinToggled() = 0; virtual void onTitleDoubleClicked() = 0; MyGUI::Widget* mPinButton; bool mPinned; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwinput/000077500000000000000000000000001264522266000203725ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwinput/inputmanagerimp.cpp000066400000000000000000001770011264522266000243040ustar00rootroot00000000000000#include "inputmanagerimp.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWInput { InputManager::InputManager( SDL_Window* window, osg::ref_ptr viewer, osg::ref_ptr screenCaptureHandler, const std::string& userFile, bool userFileExists, const std::string& controllerBindingsFile, bool grab) : mWindow(window) , mWindowVisible(true) , mViewer(viewer) , mScreenCaptureHandler(screenCaptureHandler) , mJoystickLastUsed(false) , mPlayer(NULL) , mInputManager(NULL) , mVideoWrapper(NULL) , mUserFile(userFile) , mDragDrop(false) , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) , mControlsDisabled(false) , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) , mMouseLookEnabled(false) , mGuiCursorEnabled(true) , mDetectingKeyboard(false) , mOverencumberedMessageDelay(0.f) , mGuiCursorX(0) , mGuiCursorY(0) , mMouseWheel(0) , mUserFileExists(userFileExists) , mAlwaysRunActive(Settings::Manager::getBool("always run", "Input")) , mSneakToggles(Settings::Manager::getBool("toggle sneak", "Input")) , mSneaking(false) , mAttemptJump(false) , mInvUiScalingFactor(1.f) , mFakeDeviceID(1) { mInputManager = new SDLUtil::InputWrapper(window, viewer, grab); mInputManager->setMouseEventCallback (this); mInputManager->setKeyboardEventCallback (this); mInputManager->setWindowEventCallback(this); mInputManager->setControllerEventCallback(this); mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer); mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"), Settings::Manager::getFloat("contrast", "Video")); std::string file = userFileExists ? userFile : ""; mInputBinder = new ICS::InputControlSystem(file, true, this, NULL, A_Last); loadKeyDefaults(); loadControllerDefaults(); for (int i = 0; i < A_Last; ++i) { mInputBinder->getChannel (i)->addListener (this); } mControlSwitch["playercontrols"] = true; mControlSwitch["playerfighting"] = true; mControlSwitch["playerjumping"] = true; mControlSwitch["playerlooking"] = true; mControlSwitch["playermagic"] = true; mControlSwitch["playerviewswitch"] = true; mControlSwitch["vanitymode"] = true; /* Joystick Init */ // Load controller mappings #if SDL_VERSION_ATLEAST(2,0,2) if(controllerBindingsFile!="") { SDL_GameControllerAddMappingsFromFile(controllerBindingsFile.c_str()); } #endif // Open all presently connected sticks int numSticks = SDL_NumJoysticks(); for(int i = 0; i < numSticks; i++) { if(SDL_IsGameController(i)) { SDL_ControllerDeviceEvent evt; evt.which = i; controllerAdded(mFakeDeviceID, evt); std::cout << "Detected game controller: " << SDL_GameControllerNameForIndex(i) << std::endl; } else { std::cout << "Detected unusable controller: " << SDL_JoystickNameForIndex(i) << std::endl; } } float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); if (uiScale != 0.f) mInvUiScalingFactor = 1.f / uiScale; int w,h; SDL_GetWindowSize(window, &w, &h); mGuiCursorX = mInvUiScalingFactor * w / 2.f; mGuiCursorY = mInvUiScalingFactor * h / 2.f; } void InputManager::clear() { // Enable all controls for (std::map::iterator it = mControlSwitch.begin(); it != mControlSwitch.end(); ++it) it->second = true; } InputManager::~InputManager() { mInputBinder->save (mUserFile); delete mInputBinder; delete mInputManager; delete mVideoWrapper; } bool InputManager::isWindowVisible() { return mWindowVisible; } void InputManager::setPlayerControlsEnabled(bool enabled) { int nPlayerChannels = 17; int playerChannels[] = {A_Activate, A_AutoMove, A_AlwaysRun, A_ToggleWeapon, A_ToggleSpell, A_Rest, A_QuickKey1, A_QuickKey2, A_QuickKey3, A_QuickKey4, A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10, A_Use}; for(int i = 0; i < nPlayerChannels; i++) { int pc = playerChannels[i]; mInputBinder->getChannel(pc)->setEnabled(enabled); } } void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) { if (mDragDrop) return; resetIdleTime (); int action = channel->getNumber(); if((previousValue == 1 || previousValue == 0) && (currentValue==1 || currentValue==0)) { //Is a normal button press, so don't change it at all } //Otherwise only trigger button presses as they go through specific points else if(previousValue >= .8 && currentValue < .8) { currentValue = 0.0; previousValue = 1.0; } else if(previousValue <= .6 && currentValue > .6) { currentValue = 1.0; previousValue = 0.0; } else { //If it's not switching between those values, ignore the channel change. return; } if (mControlSwitch["playercontrols"]) { if (action == A_Use) mPlayer->setAttackingOrSpell(currentValue != 0); else if (action == A_Jump) mAttemptJump = (currentValue == 1.0 && previousValue == 0.0); } if (currentValue == 1) { // trigger action activated switch (action) { case A_GameMenu: if(!(MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu)) toggleMainMenu (); break; case A_Screenshot: screenshot(); break; case A_Inventory: toggleInventory (); break; case A_Console: toggleConsole (); break; case A_Activate: resetIdleTime(); if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) activate(); break; case A_Journal: toggleJournal (); break; case A_AutoMove: toggleAutoMove (); break; case A_AlwaysRun: toggleWalking (); break; case A_ToggleWeapon: toggleWeapon (); break; case A_Rest: rest(); break; case A_ToggleSpell: toggleSpell (); break; case A_QuickKey1: quickKey(1); break; case A_QuickKey2: quickKey(2); break; case A_QuickKey3: quickKey(3); break; case A_QuickKey4: quickKey(4); break; case A_QuickKey5: quickKey(5); break; case A_QuickKey6: quickKey(6); break; case A_QuickKey7: quickKey(7); break; case A_QuickKey8: quickKey(8); break; case A_QuickKey9: quickKey(9); break; case A_QuickKey10: quickKey(10); break; case A_QuickKeysMenu: showQuickKeysMenu(); break; case A_ToggleHUD: MWBase::Environment::get().getWindowManager()->toggleGui(); break; case A_ToggleDebug: MWBase::Environment::get().getWindowManager()->toggleDebugWindow(); break; case A_QuickSave: quickSave(); break; case A_QuickLoad: quickLoad(); break; case A_CycleSpellLeft: if (checkAllowedToUseItems()) MWBase::Environment::get().getWindowManager()->cycleSpell(false); break; case A_CycleSpellRight: if (checkAllowedToUseItems()) MWBase::Environment::get().getWindowManager()->cycleSpell(true); break; case A_CycleWeaponLeft: if (checkAllowedToUseItems()) MWBase::Environment::get().getWindowManager()->cycleWeapon(false); break; case A_CycleWeaponRight: if (checkAllowedToUseItems()) MWBase::Environment::get().getWindowManager()->cycleWeapon(true); break; case A_Sneak: if (mSneakToggles) { toggleSneaking(); } break; } } } void InputManager::updateCursorMode() { bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Console; bool was_relative = mInputManager->getMouseRelative(); bool is_relative = !MWBase::Environment::get().getWindowManager()->isGuiMode(); // don't keep the pointer away from the window edge in gui mode // stop using raw mouse motions and switch to system cursor movements mInputManager->setMouseRelative(is_relative); //we let the mouse escape in the main menu mInputManager->setGrabPointer(grab && (mGrabCursor || is_relative)); //we switched to non-relative mode, move our cursor to where the in-game //cursor is if( !is_relative && was_relative != is_relative ) { mInputManager->warpMouse(static_cast(mGuiCursorX/mInvUiScalingFactor), static_cast(mGuiCursorY/mInvUiScalingFactor)); } } bool InputManager::checkAllowedToUseItems() const { MWWorld::Ptr player = MWMechanics::getPlayer(); if (player.getClass().getNpcStats(player).isWerewolf()) { // Cannot use items or spells while in werewolf form MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); return false; } return true; } void InputManager::update(float dt, bool disableControls, bool disableEvents) { mControlsDisabled = disableControls; mInputManager->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); mInputManager->capture(disableEvents); // inject some fake mouse movement to force updating MyGUI's widget states MyGUI::InputManager::getInstance().injectMouseMove( int(mGuiCursorX), int(mGuiCursorY), mMouseWheel); if (mControlsDisabled) { updateCursorMode(); return; } // update values of channels (as a result of pressed keys) mInputBinder->update(dt); updateCursorMode(); if(mJoystickLastUsed) { if (mGuiCursorEnabled) { float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue()*2.0f-1.0f; float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue()*2.0f-1.0f; float zAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); xAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); yAxis *= (1.5f - mInputBinder->getChannel(A_Use)->getValue()); // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor mGuiCursorX += xAxis * dt * 1500.0f * mInvUiScalingFactor; mGuiCursorY += yAxis * dt * 1500.0f * mInvUiScalingFactor; mMouseWheel -= static_cast(zAxis * dt * 1500.0f); mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width))); mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height))); MyGUI::InputManager::getInstance().injectMouseMove(static_cast(mGuiCursorX), static_cast(mGuiCursorY), mMouseWheel); mInputManager->warpMouse(static_cast(mGuiCursorX/mInvUiScalingFactor), static_cast(mGuiCursorY/mInvUiScalingFactor)); } if (mMouseLookEnabled) { float xAxis = mInputBinder->getChannel(A_LookLeftRight)->getValue()*2.0f-1.0f; float yAxis = mInputBinder->getChannel(A_LookUpDown)->getValue()*2.0f-1.0f; resetIdleTime(); float rot[3]; rot[0] = yAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; rot[1] = 0.0f; rot[2] = xAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f); // Only actually turn player when we're not in vanity mode if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) { mPlayer->yaw(rot[2]); mPlayer->pitch(rot[0]); } } } // Disable movement in Gui mode if (!(MWBase::Environment::get().getWindowManager()->isGuiMode() || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)) { // Configure player movement according to keyboard input. Actual movement will // be done in the physics system. if (mControlSwitch["playercontrols"]) { bool triedToMove = false; bool isRunning = false; if(mJoystickLastUsed) { float xAxis = mInputBinder->getChannel(A_MoveLeftRight)->getValue(); float yAxis = mInputBinder->getChannel(A_MoveForwardBackward)->getValue(); if (xAxis < .5) { triedToMove = true; mPlayer->setLeftRight (-1); } else if (xAxis > .5) { triedToMove = true; mPlayer->setLeftRight (1); } if (yAxis < .5) { triedToMove = true; mPlayer->setAutoMove (false); mPlayer->setForwardBackward (1); } else if (yAxis > .5) { triedToMove = true; mPlayer->setAutoMove (false); mPlayer->setForwardBackward (-1); } else if(mPlayer->getAutoMove()) { triedToMove = true; mPlayer->setForwardBackward (1); } isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; if(triedToMove) resetIdleTime(); } else { if (actionIsActive(A_MoveLeft)) { triedToMove = true; mPlayer->setLeftRight (-1); } else if (actionIsActive(A_MoveRight)) { triedToMove = true; mPlayer->setLeftRight (1); } if (actionIsActive(A_MoveForward)) { triedToMove = true; mPlayer->setAutoMove (false); mPlayer->setForwardBackward (1); } else if (actionIsActive(A_MoveBackward)) { triedToMove = true; mPlayer->setAutoMove (false); mPlayer->setForwardBackward (-1); } else if(mPlayer->getAutoMove()) { triedToMove = true; mPlayer->setForwardBackward (1); } } if (!mSneakToggles) { mPlayer->setSneak(actionIsActive(A_Sneak)); } if (mAttemptJump && mControlSwitch["playerjumping"]) { mPlayer->setUpDown (1); triedToMove = true; mOverencumberedMessageDelay = 0.f; } if (mAlwaysRunActive || isRunning) mPlayer->setRunState(!actionIsActive(A_Run)); else mPlayer->setRunState(actionIsActive(A_Run)); // if player tried to start moving, but can't (due to being overencumbered), display a notification. if (triedToMove) { MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mOverencumberedMessageDelay -= dt; if (player.getClass().getEncumbrance(player) > player.getClass().getCapacity(player)) { mPlayer->setAutoMove (false); if (mOverencumberedMessageDelay <= 0) { MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage59}"); mOverencumberedMessageDelay = 1.0; } } } if (mControlSwitch["playerviewswitch"]) { if (actionIsActive(A_TogglePOV)) { if (mPreviewPOVDelay <= 0.5 && (mPreviewPOVDelay += dt) > 0.5) { mPreviewPOVDelay = 1.f; MWBase::Environment::get().getWorld()->togglePreviewMode(true); } } else { //disable preview mode MWBase::Environment::get().getWorld()->togglePreviewMode(false); if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5) { MWBase::Environment::get().getWorld()->togglePOV(); } mPreviewPOVDelay = 0.f; } } } if (actionIsActive(A_MoveForward) || actionIsActive(A_MoveBackward) || actionIsActive(A_MoveLeft) || actionIsActive(A_MoveRight) || actionIsActive(A_Jump) || actionIsActive(A_Sneak) || actionIsActive(A_TogglePOV)) { resetIdleTime(); } else { updateIdleTime(dt); } } mAttemptJump = false; // Can only jump on first frame input is on } void InputManager::setDragDrop(bool dragDrop) { mDragDrop = dragDrop; } void InputManager::changeInputMode(bool guiMode) { mGuiCursorEnabled = guiMode; mMouseLookEnabled = !guiMode; if (guiMode) MWBase::Environment::get().getWindowManager()->showCrosshair(false); MWBase::Environment::get().getWindowManager()->setCursorVisible(guiMode); // if not in gui mode, the camera decides whether to show crosshair or not. } void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) { bool changeRes = false; for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) { if (it->first == "Input" && it->second == "invert y axis") mInvertY = Settings::Manager::getBool("invert y axis", "Input"); if (it->first == "Input" && it->second == "camera sensitivity") mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); if (it->first == "Input" && it->second == "grab cursor") mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); if (it->first == "Video" && ( it->second == "resolution x" || it->second == "resolution y" || it->second == "fullscreen" || it->second == "window border")) changeRes = true; if (it->first == "Video" && it->second == "vsync") mVideoWrapper->setSyncToVBlank(Settings::Manager::getBool("vsync", "Video")); if (it->first == "Video" && (it->second == "gamma" || it->second == "contrast")) mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"), Settings::Manager::getFloat("contrast", "Video")); } if (changeRes) { mVideoWrapper->setVideoMode(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video"), Settings::Manager::getBool("fullscreen", "Video"), Settings::Manager::getBool("window border", "Video")); } } bool InputManager::getControlSwitch (const std::string& sw) { return mControlSwitch[sw]; } void InputManager::toggleControlSwitch (const std::string& sw, bool value) { if (mControlSwitch[sw] == value) { return; } /// \note 7 switches at all, if-else is relevant if (sw == "playercontrols" && !value) { mPlayer->setLeftRight(0); mPlayer->setForwardBackward(0); mPlayer->setAutoMove(false); mPlayer->setUpDown(0); } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time mPlayer->setUpDown(0); } else if (sw == "vanitymode") { MWBase::Environment::get().getWorld()->allowVanityMode(value); } else if (sw == "playerlooking") { MWBase::Environment::get().getWorld()->togglePlayerLooking(value); } mControlSwitch[sw] = value; } void InputManager::keyPressed( const SDL_KeyboardEvent &arg ) { // HACK: to make Morrowind's default keybinding for the console work without printing an extra "^" upon closing // This assumes that SDL_TextInput events always come *after* the key event // (which is somewhat reasonable, and hopefully true for all SDL platforms) OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); if (mInputBinder->getKeyBinding(mInputBinder->getControl(A_Console), ICS::Control::INCREASE) == arg.keysym.scancode && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Console) SDL_StopTextInput(); bool consumed = false; if (kc != OIS::KC_UNASSIGNED) { consumed = SDL_IsTextInputActive() && ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym)); // Little trick to check if key is printable bool guiFocus = MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); setPlayerControlsEnabled(!guiFocus); } if (!mControlsDisabled && !consumed) mInputBinder->keyPressed (arg); mJoystickLastUsed = false; } void InputManager::textInput(const SDL_TextInputEvent &arg) { MyGUI::UString ustring(&arg.text[0]); MyGUI::UString::utf32string utf32string = ustring.asUTF32(); for (MyGUI::UString::utf32string::const_iterator it = utf32string.begin(); it != utf32string.end(); ++it) MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it); } void InputManager::keyReleased(const SDL_KeyboardEvent &arg ) { mJoystickLastUsed = false; OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); mInputBinder->keyReleased (arg); } void InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) { mJoystickLastUsed = false; bool guiMode = false; if (id == SDL_BUTTON_LEFT || id == SDL_BUTTON_RIGHT) // MyGUI only uses these mouse events { guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); guiMode = MyGUI::InputManager::getInstance().injectMousePress(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode; if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) { MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); if (b && b->getEnabled()) { MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); } } } setPlayerControlsEnabled(!guiMode); // Don't trigger any mouse bindings while in settings menu, otherwise rebinding controls becomes impossible if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings) mInputBinder->mousePressed (arg, id); } void InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) { mJoystickLastUsed = false; if(mInputBinder->detectingBindingState()) { mInputBinder->mouseReleased (arg, id); } else { bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode; if(mInputBinder->detectingBindingState()) return; // don't allow same mouseup to bind as initiated bind setPlayerControlsEnabled(!guiMode); mInputBinder->mouseReleased (arg, id); } } void InputManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg ) { mInputBinder->mouseMoved (arg); mJoystickLastUsed = false; resetIdleTime (); if (mGuiCursorEnabled) { // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor mGuiCursorX = static_cast(arg.x) * mInvUiScalingFactor; mGuiCursorY = static_cast(arg.y) * mInvUiScalingFactor; mMouseWheel = int(arg.z); MyGUI::InputManager::getInstance().injectMouseMove( int(mGuiCursorX), int(mGuiCursorY), mMouseWheel); } if (mMouseLookEnabled && !mControlsDisabled) { resetIdleTime(); float x = arg.xrel * mCameraSensitivity * (1.0f/256.f); float y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; float rot[3]; rot[0] = -y; rot[1] = 0.0f; rot[2] = -x; // Only actually turn player when we're not in vanity mode if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) { mPlayer->yaw(x); mPlayer->pitch(y); } if (arg.zrel && mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"]) //Check to make sure you are allowed to zoomout and there is a change { MWBase::Environment::get().getWorld()->changeVanityModeScale(static_cast(arg.zrel)); if (Settings::Manager::getBool("allow third person zoom", "Input")) MWBase::Environment::get().getWorld()->setCameraDistance(static_cast(arg.zrel), true, true); } } } void InputManager::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg ) { mJoystickLastUsed = true; bool guiMode = false; if (arg.button == SDL_CONTROLLER_BUTTON_A || arg.button == SDL_CONTROLLER_BUTTON_B) // We'll pretend that A is left click and B is right click { guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); if(!mInputBinder->detectingBindingState()) { guiMode = MyGUI::InputManager::getInstance().injectMousePress(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI((arg.button == SDL_CONTROLLER_BUTTON_B) ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT)) && guiMode; if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) { MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); if (b && b->getEnabled()) { MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); } } } } setPlayerControlsEnabled(!guiMode); //esc, to leave initial movie screen OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(SDLK_ESCAPE); bool guiFocus = MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); setPlayerControlsEnabled(!guiFocus); if (!mControlsDisabled) mInputBinder->buttonPressed(deviceID, arg); } void InputManager::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg ) { mJoystickLastUsed = true; if(mInputBinder->detectingBindingState()) mInputBinder->buttonReleased(deviceID, arg); else if(arg.button == SDL_CONTROLLER_BUTTON_A || arg.button == SDL_CONTROLLER_BUTTON_B) { bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode(); guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI((arg.button == SDL_CONTROLLER_BUTTON_B) ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT)) && guiMode; if(mInputBinder->detectingBindingState()) return; // don't allow same mouseup to bind as initiated bind setPlayerControlsEnabled(!guiMode); mInputBinder->buttonReleased(deviceID, arg); } else mInputBinder->buttonReleased(deviceID, arg); ///to escape initial movie OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(SDLK_ESCAPE); setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc))); } void InputManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg ) { mJoystickLastUsed = true; if (!mControlsDisabled) mInputBinder->axisMoved(deviceID, arg); } void InputManager::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg) { mInputBinder->controllerAdded(deviceID, arg); } void InputManager::controllerRemoved(const SDL_ControllerDeviceEvent &arg) { mInputBinder->controllerRemoved(arg); } void InputManager::windowFocusChange(bool have_focus) { } void InputManager::windowVisibilityChange(bool visible) { mWindowVisible = visible; } void InputManager::windowResized(int x, int y) { Settings::Manager::setInt("resolution x", "Video", x); Settings::Manager::setInt("resolution y", "Video", y); MWBase::Environment::get().getWindowManager()->windowResized(x, y); } void InputManager::windowClosed() { MWBase::Environment::get().getStateManager()->requestQuit(); } void InputManager::toggleMainMenu() { if (MyGUI::InputManager::getInstance().isModalAny()) { MWBase::Environment::get().getWindowManager()->exitCurrentModal(); return; } if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu { MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } else //Close current GUI { MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } } void InputManager::quickLoad() { if (!MyGUI::InputManager::getInstance().isModalAny()) MWBase::Environment::get().getStateManager()->quickLoad(); } void InputManager::quickSave() { if (!MyGUI::InputManager::getInstance().isModalAny()) MWBase::Environment::get().getStateManager()->quickSave(); } void InputManager::toggleSpell() { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; // Not allowed before the magic window is accessible if (!mControlSwitch["playermagic"] || !mControlSwitch["playercontrols"]) return; if (!checkAllowedToUseItems()) return; // Not allowed if no spell selected MWWorld::InventoryStore& inventory = mPlayer->getPlayer().getClass().getInventoryStore(mPlayer->getPlayer()); if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty() && inventory.getSelectedEnchantItem() == inventory.end()) return; MWMechanics::DrawState_ state = mPlayer->getDrawState(); if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) mPlayer->setDrawState(MWMechanics::DrawState_Spell); else mPlayer->setDrawState(MWMechanics::DrawState_Nothing); } void InputManager::toggleWeapon() { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; // Not allowed before the inventory window is accessible if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"]) return; MWMechanics::DrawState_ state = mPlayer->getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) mPlayer->setDrawState(MWMechanics::DrawState_Weapon); else mPlayer->setDrawState(MWMechanics::DrawState_Nothing); } void InputManager::rest() { if (!mControlSwitch["playercontrols"]) return; if (!MWBase::Environment::get().getWindowManager()->getRestEnabled () || MWBase::Environment::get().getWindowManager()->isGuiMode ()) return; if(mPlayer->isInCombat()) {//Check if in combat MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); //Nope, return; } MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); //Open rest GUI } void InputManager::screenshot() { mScreenCaptureHandler->setFramesToCapture(1); mScreenCaptureHandler->captureNextFrame(*mViewer); MWBase::Environment::get().getWindowManager()->messageBox ("Screenshot saved"); } void InputManager::toggleInventory() { if (!mControlSwitch["playercontrols"]) return; if (MyGUI::InputManager::getInstance ().isModalAny()) return; // Toggle between game mode and inventory mode if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Inventory); else { MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); if(mode == MWGui::GM_Inventory || mode == MWGui::GM_Container) MWBase::Environment::get().getWindowManager()->popGuiMode(); } // .. but don't touch any other mode, except container. } void InputManager::toggleConsole() { if (MyGUI::InputManager::getInstance ().isModalAny()) return; // Switch to console mode no matter what mode we are currently // in, except of course if we are already in console mode if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Console) MWBase::Environment::get().getWindowManager()->popGuiMode(); else MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Console); } else MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Console); } void InputManager::toggleJournal() { if (!mControlSwitch["playercontrols"]) return; if (MyGUI::InputManager::getInstance ().isModalAny()) return; if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) { MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); } else if(MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal)) { MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Journal); } } void InputManager::quickKey (int index) { if (!mControlSwitch["playercontrols"]) return; if (!checkAllowedToUseItems()) return; if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) MWBase::Environment::get().getWindowManager()->activateQuickKey (index); } void InputManager::showQuickKeysMenu() { if (!MWBase::Environment::get().getWindowManager()->isGuiMode () && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) { if (!checkAllowedToUseItems()) return; MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); } else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) { while(MyGUI::InputManager::getInstance().isModalAny()) { //Handle any open Modal windows MWBase::Environment::get().getWindowManager()->exitCurrentModal(); } MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); //And handle the actual main window } } void InputManager::activate() { if (mControlSwitch["playercontrols"]) mPlayer->activate(); } void InputManager::toggleAutoMove() { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; if (mControlSwitch["playercontrols"]) mPlayer->setAutoMove (!mPlayer->getAutoMove()); } void InputManager::toggleWalking() { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; mAlwaysRunActive = !mAlwaysRunActive; Settings::Manager::setBool("always run", "Input", mAlwaysRunActive); } void InputManager::toggleSneaking() { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; mSneaking = !mSneaking; mPlayer->setSneak(mSneaking); } void InputManager::resetIdleTime() { if (mTimeIdle < 0) MWBase::Environment::get().getWorld()->toggleVanityMode(false); mTimeIdle = 0.f; } void InputManager::updateIdleTime(float dt) { static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get() .find("fVanityDelay")->getFloat(); if (mTimeIdle >= 0.f) mTimeIdle += dt; if (mTimeIdle > vanityDelay) { MWBase::Environment::get().getWorld()->toggleVanityMode(true); mTimeIdle = -1.f; } } bool InputManager::actionIsActive (int id) { return (mInputBinder->getChannel (id)->getValue ()==1.0); } void InputManager::loadKeyDefaults (bool force) { // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid // across different versions of OpenMW (in the case where another input action is added) std::map defaultKeyBindings; //Gets the Keyvalue from the Scancode; gives the button in the same place reguardless of keyboard format defaultKeyBindings[A_Activate] = SDL_SCANCODE_SPACE; defaultKeyBindings[A_MoveBackward] = SDL_SCANCODE_S; defaultKeyBindings[A_MoveForward] = SDL_SCANCODE_W; defaultKeyBindings[A_MoveLeft] = SDL_SCANCODE_A; defaultKeyBindings[A_MoveRight] = SDL_SCANCODE_D; defaultKeyBindings[A_ToggleWeapon] = SDL_SCANCODE_F; defaultKeyBindings[A_ToggleSpell] = SDL_SCANCODE_R; defaultKeyBindings[A_CycleSpellLeft] = SDL_SCANCODE_MINUS; defaultKeyBindings[A_CycleSpellRight] = SDL_SCANCODE_EQUALS; defaultKeyBindings[A_CycleWeaponLeft] = SDL_SCANCODE_LEFTBRACKET; defaultKeyBindings[A_CycleWeaponRight] = SDL_SCANCODE_RIGHTBRACKET; defaultKeyBindings[A_QuickKeysMenu] = SDL_SCANCODE_F1; defaultKeyBindings[A_Console] = SDL_SCANCODE_GRAVE; defaultKeyBindings[A_Run] = SDL_SCANCODE_LSHIFT; defaultKeyBindings[A_Sneak] = SDL_SCANCODE_LCTRL; defaultKeyBindings[A_AutoMove] = SDL_SCANCODE_Q; defaultKeyBindings[A_Jump] = SDL_SCANCODE_E; defaultKeyBindings[A_Journal] = SDL_SCANCODE_J; defaultKeyBindings[A_Rest] = SDL_SCANCODE_T; defaultKeyBindings[A_GameMenu] = SDL_SCANCODE_ESCAPE; defaultKeyBindings[A_TogglePOV] = SDL_SCANCODE_TAB; defaultKeyBindings[A_QuickKey1] = SDL_SCANCODE_1; defaultKeyBindings[A_QuickKey2] = SDL_SCANCODE_2; defaultKeyBindings[A_QuickKey3] = SDL_SCANCODE_3; defaultKeyBindings[A_QuickKey4] = SDL_SCANCODE_4; defaultKeyBindings[A_QuickKey5] = SDL_SCANCODE_5; defaultKeyBindings[A_QuickKey6] = SDL_SCANCODE_6; defaultKeyBindings[A_QuickKey7] = SDL_SCANCODE_7; defaultKeyBindings[A_QuickKey8] = SDL_SCANCODE_8; defaultKeyBindings[A_QuickKey9] = SDL_SCANCODE_9; defaultKeyBindings[A_QuickKey10] = SDL_SCANCODE_0; defaultKeyBindings[A_Screenshot] = SDL_SCANCODE_F12; defaultKeyBindings[A_ToggleHUD] = SDL_SCANCODE_F11; defaultKeyBindings[A_ToggleDebug] = SDL_SCANCODE_F10; defaultKeyBindings[A_AlwaysRun] = SDL_SCANCODE_CAPSLOCK; defaultKeyBindings[A_QuickSave] = SDL_SCANCODE_F5; defaultKeyBindings[A_QuickLoad] = SDL_SCANCODE_F9; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT; defaultMouseButtonBindings[A_Use] = SDL_BUTTON_LEFT; for (int i = 0; i < A_Last; ++i) { ICS::Control* control; bool controlExists = mInputBinder->getChannel(i)->getControlsCount () != 0; if (!controlExists) { control = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); mInputBinder->addControl(control); control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT); } else { control = mInputBinder->getChannel(i)->getAttachedControls ().front().control; } if (!controlExists || force || ( mInputBinder->getKeyBinding (control, ICS::Control::INCREASE) == SDL_SCANCODE_UNKNOWN && mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS )) { clearAllKeyBindings(control); if (defaultKeyBindings.find(i) != defaultKeyBindings.end() && !mInputBinder->isKeyBound(defaultKeyBindings[i])) { control->setInitialValue(0.0f); mInputBinder->addKeyBinding(control, defaultKeyBindings[i], ICS::Control::INCREASE); } else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end() && !mInputBinder->isMouseButtonBound(defaultMouseButtonBindings[i])) { control->setInitialValue(0.0f); mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); } } } } void InputManager::loadControllerDefaults(bool force) { // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid // across different versions of OpenMW (in the case where another input action is added) std::map defaultButtonBindings; defaultButtonBindings[A_Activate] = SDL_CONTROLLER_BUTTON_A; defaultButtonBindings[A_ToggleWeapon] = SDL_CONTROLLER_BUTTON_X; defaultButtonBindings[A_ToggleSpell] = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; //defaultButtonBindings[A_QuickButtonsMenu] = SDL_GetButtonFromScancode(SDL_SCANCODE_F1); // Need to implement, should be ToggleSpell(5) AND Wait(9) defaultButtonBindings[A_Sneak] = SDL_CONTROLLER_BUTTON_RIGHTSTICK; defaultButtonBindings[A_Jump] = SDL_CONTROLLER_BUTTON_Y; defaultButtonBindings[A_Journal] = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; defaultButtonBindings[A_Rest] = SDL_CONTROLLER_BUTTON_BACK; defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_LEFTSTICK; defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B; defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START; defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE; defaultButtonBindings[A_QuickKey1] = SDL_CONTROLLER_BUTTON_DPAD_UP; defaultButtonBindings[A_QuickKey2] = SDL_CONTROLLER_BUTTON_DPAD_LEFT; defaultButtonBindings[A_QuickKey3] = SDL_CONTROLLER_BUTTON_DPAD_DOWN; defaultButtonBindings[A_QuickKey4] = SDL_CONTROLLER_BUTTON_DPAD_RIGHT; std::map defaultAxisBindings; defaultAxisBindings[A_MoveForwardBackward] = SDL_CONTROLLER_AXIS_LEFTY; defaultAxisBindings[A_MoveLeftRight] = SDL_CONTROLLER_AXIS_LEFTX; defaultAxisBindings[A_LookUpDown] = SDL_CONTROLLER_AXIS_RIGHTY; defaultAxisBindings[A_LookLeftRight] = SDL_CONTROLLER_AXIS_RIGHTX; defaultAxisBindings[A_Use] = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; for (int i = 0; i < A_Last; i++) { ICS::Control* control; bool controlExists = mInputBinder->getChannel(i)->getControlsCount () != 0; if (!controlExists) { float initial; if (defaultButtonBindings.find(i) != defaultButtonBindings.end()) initial = 0.0f; else initial = 0.5f; control = new ICS::Control(boost::lexical_cast(i), false, true, initial, ICS::ICS_MAX, ICS::ICS_MAX); mInputBinder->addControl(control); control->attachChannel(mInputBinder->getChannel(i), ICS::Channel::DIRECT); } else { control = mInputBinder->getChannel(i)->getAttachedControls ().front().control; } if (!controlExists || force || ( mInputBinder->getJoystickAxisBinding (control, mFakeDeviceID, ICS::Control::INCREASE) == ICS::InputControlSystem::UNASSIGNED && mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS )) { clearAllControllerBindings(control); if (defaultButtonBindings.find(i) != defaultButtonBindings.end()) { control->setInitialValue(0.0f); mInputBinder->addJoystickButtonBinding(control, mFakeDeviceID, defaultButtonBindings[i], ICS::Control::INCREASE); } else if (defaultAxisBindings.find(i) != defaultAxisBindings.end()) { control->setValue(0.5f); control->setInitialValue(0.5f); mInputBinder->addJoystickAxisBinding(control, mFakeDeviceID, defaultAxisBindings[i], ICS::Control::INCREASE); } } } } std::string InputManager::getActionDescription (int action) { std::map descriptions; if (action == A_Screenshot) return "Screenshot"; descriptions[A_Use] = "sUse"; descriptions[A_Activate] = "sActivate"; descriptions[A_MoveBackward] = "sBack"; descriptions[A_MoveForward] = "sForward"; descriptions[A_MoveLeft] = "sLeft"; descriptions[A_MoveRight] = "sRight"; descriptions[A_ToggleWeapon] = "sReady_Weapon"; descriptions[A_ToggleSpell] = "sReady_Magic"; descriptions[A_CycleSpellLeft] = "sPrevSpell"; descriptions[A_CycleSpellRight] = "sNextSpell"; descriptions[A_CycleWeaponLeft] = "sPrevWeapon"; descriptions[A_CycleWeaponRight] = "sNextWeapon"; descriptions[A_Console] = "sConsoleTitle"; descriptions[A_Run] = "sRun"; descriptions[A_Sneak] = "sCrouch_Sneak"; descriptions[A_AutoMove] = "sAuto_Run"; descriptions[A_Jump] = "sJump"; descriptions[A_Journal] = "sJournal"; descriptions[A_Rest] = "sRestKey"; descriptions[A_Inventory] = "sInventory"; descriptions[A_TogglePOV] = "sTogglePOVCmd"; descriptions[A_QuickKeysMenu] = "sQuickMenu"; descriptions[A_QuickKey1] = "sQuick1Cmd"; descriptions[A_QuickKey2] = "sQuick2Cmd"; descriptions[A_QuickKey3] = "sQuick3Cmd"; descriptions[A_QuickKey4] = "sQuick4Cmd"; descriptions[A_QuickKey5] = "sQuick5Cmd"; descriptions[A_QuickKey6] = "sQuick6Cmd"; descriptions[A_QuickKey7] = "sQuick7Cmd"; descriptions[A_QuickKey8] = "sQuick8Cmd"; descriptions[A_QuickKey9] = "sQuick9Cmd"; descriptions[A_QuickKey10] = "sQuick10Cmd"; descriptions[A_AlwaysRun] = "sAlways_Run"; descriptions[A_QuickSave] = "sQuickSaveCmd"; descriptions[A_QuickLoad] = "sQuickLoadCmd"; if (descriptions[action] == "") return ""; // not configurable return "#{" + descriptions[action] + "}"; } std::string InputManager::getActionKeyBindingName (int action) { if (mInputBinder->getChannel (action)->getControlsCount () == 0) return "#{sNone}"; ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; if (mInputBinder->getKeyBinding (c, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN) return mInputBinder->scancodeToString (mInputBinder->getKeyBinding (c, ICS::Control::INCREASE)); else if (mInputBinder->getMouseButtonBinding (c, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) return "#{sMouse} " + boost::lexical_cast(mInputBinder->getMouseButtonBinding (c, ICS::Control::INCREASE)); else return "#{sNone}"; } std::string InputManager::getActionControllerBindingName (int action) { if (mInputBinder->getChannel (action)->getControlsCount () == 0) return "#{sNone}"; ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; if (mInputBinder->getJoystickAxisBinding (c, mFakeDeviceID, ICS::Control::INCREASE) != ICS::InputControlSystem::UNASSIGNED) return sdlControllerAxisToString(mInputBinder->getJoystickAxisBinding (c, mFakeDeviceID, ICS::Control::INCREASE)); else if (mInputBinder->getJoystickButtonBinding (c, mFakeDeviceID, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS ) return sdlControllerButtonToString(mInputBinder->getJoystickButtonBinding (c, mFakeDeviceID, ICS::Control::INCREASE)); else return "#{sNone}"; } std::string InputManager::sdlControllerButtonToString(int button) { switch(button) { case SDL_CONTROLLER_BUTTON_A: return "A Button"; case SDL_CONTROLLER_BUTTON_B: return "B Button"; case SDL_CONTROLLER_BUTTON_BACK: return "Back Button"; case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return "DPad Down"; case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return "DPad Left"; case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return "DPad Right"; case SDL_CONTROLLER_BUTTON_DPAD_UP: return "DPad Up"; case SDL_CONTROLLER_BUTTON_GUIDE: return "Guide Button"; case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return "Left Shoulder"; case SDL_CONTROLLER_BUTTON_LEFTSTICK: return "Left Stick Button"; case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return "Right Shoulder"; case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return "Right Stick Button"; case SDL_CONTROLLER_BUTTON_START: return "Start Button"; case SDL_CONTROLLER_BUTTON_X: return "X Button"; case SDL_CONTROLLER_BUTTON_Y: return "Y Button"; default: return "Button " + boost::lexical_cast(button); } } std::string InputManager::sdlControllerAxisToString(int axis) { switch(axis) { case SDL_CONTROLLER_AXIS_LEFTX: return "Left Stick X"; case SDL_CONTROLLER_AXIS_LEFTY: return "Left Stick Y"; case SDL_CONTROLLER_AXIS_RIGHTX: return "Right Stick X"; case SDL_CONTROLLER_AXIS_RIGHTY: return "Right Stick Y"; case SDL_CONTROLLER_AXIS_TRIGGERLEFT: return "Left Trigger"; case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: return "Right Trigger"; default: return "Axis " + boost::lexical_cast(axis); } } std::vector InputManager::getActionKeySorting() { std::vector ret; ret.push_back(A_MoveForward); ret.push_back(A_MoveBackward); ret.push_back(A_MoveLeft); ret.push_back(A_MoveRight); ret.push_back(A_TogglePOV); ret.push_back(A_Run); ret.push_back(A_AlwaysRun); ret.push_back(A_Sneak); ret.push_back(A_Activate); ret.push_back(A_Use); ret.push_back(A_ToggleWeapon); ret.push_back(A_ToggleSpell); ret.push_back(A_CycleSpellLeft); ret.push_back(A_CycleSpellRight); ret.push_back(A_CycleWeaponLeft); ret.push_back(A_CycleWeaponRight); ret.push_back(A_AutoMove); ret.push_back(A_Jump); ret.push_back(A_Inventory); ret.push_back(A_Journal); ret.push_back(A_Rest); ret.push_back(A_Console); ret.push_back(A_QuickSave); ret.push_back(A_QuickLoad); ret.push_back(A_Screenshot); ret.push_back(A_QuickKeysMenu); ret.push_back(A_QuickKey1); ret.push_back(A_QuickKey2); ret.push_back(A_QuickKey3); ret.push_back(A_QuickKey4); ret.push_back(A_QuickKey5); ret.push_back(A_QuickKey6); ret.push_back(A_QuickKey7); ret.push_back(A_QuickKey8); ret.push_back(A_QuickKey9); ret.push_back(A_QuickKey10); return ret; } std::vector InputManager::getActionControllerSorting() { std::vector ret; ret.push_back(A_TogglePOV); ret.push_back(A_Sneak); ret.push_back(A_Activate); ret.push_back(A_Use); ret.push_back(A_ToggleWeapon); ret.push_back(A_ToggleSpell); ret.push_back(A_AutoMove); ret.push_back(A_Jump); ret.push_back(A_Inventory); ret.push_back(A_Journal); ret.push_back(A_Rest); ret.push_back(A_QuickSave); ret.push_back(A_QuickLoad); ret.push_back(A_Screenshot); ret.push_back(A_QuickKeysMenu); ret.push_back(A_QuickKey1); ret.push_back(A_QuickKey2); ret.push_back(A_QuickKey3); ret.push_back(A_QuickKey4); ret.push_back(A_QuickKey5); ret.push_back(A_QuickKey6); ret.push_back(A_QuickKey7); ret.push_back(A_QuickKey8); ret.push_back(A_QuickKey9); ret.push_back(A_QuickKey10); return ret; } void InputManager::enableDetectingBindingMode (int action, bool keyboard) { mDetectingKeyboard = keyboard; ICS::Control* c = mInputBinder->getChannel (action)->getAttachedControls ().front().control; mInputBinder->enableDetectingBindingState (c, ICS::Control::INCREASE); } void InputManager::mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction) { // we don't want mouse movement bindings return; } void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , SDL_Scancode key, ICS::Control::ControlChangingDirection direction) { //Disallow binding escape key if(key==SDL_SCANCODE_ESCAPE) { //Stop binding if esc pressed mInputBinder->cancelDetectingBindingState(); MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); return; } if(!mDetectingKeyboard) return; clearAllKeyBindings(control); control->setInitialValue(0.0f); ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction); MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); } void InputManager::mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , unsigned int button, ICS::Control::ControlChangingDirection direction) { if(!mDetectingKeyboard) return; clearAllKeyBindings(control); control->setInitialValue(0.0f); ICS::DetectingBindingListener::mouseButtonBindingDetected (ICS, control, button, direction); MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); } void InputManager::joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control , int axis, ICS::Control::ControlChangingDirection direction) { //only allow binding to the trigers if(axis != SDL_CONTROLLER_AXIS_TRIGGERLEFT && axis != SDL_CONTROLLER_AXIS_TRIGGERRIGHT) return; if(mDetectingKeyboard) return; clearAllControllerBindings(control); control->setValue(0.5f); //axis bindings must start at 0.5 control->setInitialValue(0.5f); ICS::DetectingBindingListener::joystickAxisBindingDetected (ICS, deviceID, control, axis, direction); MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); } void InputManager::joystickButtonBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control , unsigned int button, ICS::Control::ControlChangingDirection direction) { if(mDetectingKeyboard) return; clearAllControllerBindings(control); control->setInitialValue(0.0f); ICS::DetectingBindingListener::joystickButtonBindingDetected (ICS, deviceID, control, button, direction); MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); } void InputManager::clearAllKeyBindings (ICS::Control* control) { // right now we don't really need multiple bindings for the same action, so remove all others first if (mInputBinder->getKeyBinding (control, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN) mInputBinder->removeKeyBinding (mInputBinder->getKeyBinding (control, ICS::Control::INCREASE)); if (mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) mInputBinder->removeMouseButtonBinding (mInputBinder->getMouseButtonBinding (control, ICS::Control::INCREASE)); } void InputManager::clearAllControllerBindings (ICS::Control* control) { // right now we don't really need multiple bindings for the same action, so remove all others first if (mInputBinder->getJoystickAxisBinding (control, mFakeDeviceID, ICS::Control::INCREASE) != SDL_SCANCODE_UNKNOWN) mInputBinder->removeJoystickAxisBinding (mFakeDeviceID, mInputBinder->getJoystickAxisBinding (control, mFakeDeviceID, ICS::Control::INCREASE)); if (mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) mInputBinder->removeJoystickButtonBinding (mFakeDeviceID, mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE)); } void InputManager::resetToDefaultKeyBindings() { loadKeyDefaults(true); } void InputManager::resetToDefaultControllerBindings() { loadControllerDefaults(true); } MyGUI::MouseButton InputManager::sdlButtonToMyGUI(Uint8 button) { //The right button is the second button, according to MyGUI if(button == SDL_BUTTON_RIGHT) button = SDL_BUTTON_MIDDLE; else if(button == SDL_BUTTON_MIDDLE) button = SDL_BUTTON_RIGHT; //MyGUI's buttons are 0 indexed return MyGUI::MouseButton::Enum(button - 1); } } openmw-openmw-0.38.0/apps/openmw/mwinput/inputmanagerimp.hpp000066400000000000000000000223041264522266000243040ustar00rootroot00000000000000#ifndef MWINPUT_MWINPUTMANAGERIMP_H #define MWINPUT_MWINPUTMANAGERIMP_H #include "../mwgui/mode.hpp" #include #include #include #include #include #include #include "../mwbase/inputmanager.hpp" namespace MWWorld { class Player; } namespace MWBase { class WindowManager; } namespace ICS { class InputControlSystem; } namespace MyGUI { struct MouseButton; } namespace Files { struct ConfigurationManager; } namespace SDLUtil { class InputWrapper; class VideoWrapper; } namespace osgViewer { class Viewer; class ScreenCaptureHandler; } struct SDL_Window; namespace MWInput { /** * @brief Class that handles all input and key bindings for OpenMW. */ class InputManager : public MWBase::InputManager, public SDLUtil::KeyListener, public SDLUtil::MouseListener, public SDLUtil::WindowListener, public SDLUtil::ControllerListener, public ICS::ChannelListener, public ICS::DetectingBindingListener { public: InputManager( SDL_Window* window, osg::ref_ptr viewer, osg::ref_ptr screenCaptureHandler, const std::string& userFile, bool userFileExists, const std::string& controllerBindingsFile, bool grab); virtual ~InputManager(); virtual bool isWindowVisible(); /// Clear all savegame-specific data virtual void clear(); virtual void update(float dt, bool disableControls=false, bool disableEvents=false); void setPlayer (MWWorld::Player* player) { mPlayer = player; } virtual void changeInputMode(bool guiMode); virtual void processChangedSettings(const Settings::CategorySettingVector& changed); virtual void setDragDrop(bool dragDrop); virtual void toggleControlSwitch (const std::string& sw, bool value); virtual bool getControlSwitch (const std::string& sw); virtual std::string getActionDescription (int action); virtual std::string getActionKeyBindingName (int action); virtual std::string getActionControllerBindingName (int action); virtual int getNumActions() { return A_Last; } virtual std::vector getActionKeySorting(); virtual std::vector getActionControllerSorting(); virtual void enableDetectingBindingMode (int action, bool keyboard); virtual void resetToDefaultKeyBindings(); virtual void resetToDefaultControllerBindings(); virtual bool joystickLastUsed() {return mJoystickLastUsed;} public: virtual void keyPressed(const SDL_KeyboardEvent &arg ); virtual void keyReleased( const SDL_KeyboardEvent &arg ); virtual void textInput (const SDL_TextInputEvent &arg); virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); virtual void mouseMoved( const SDLUtil::MouseMotionEvent &arg ); virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &arg); virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg); virtual void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg); virtual void controllerRemoved(const SDL_ControllerDeviceEvent &arg); virtual void windowVisibilityChange( bool visible ); virtual void windowFocusChange( bool have_focus ); virtual void windowResized (int x, int y); virtual void windowClosed (); virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction); virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , SDL_Scancode key, ICS::Control::ControlChangingDirection direction); virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , unsigned int button, ICS::Control::ControlChangingDirection direction); virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control , int axis, ICS::Control::ControlChangingDirection direction); virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, int deviceID, ICS::Control* control , unsigned int button, ICS::Control::ControlChangingDirection direction); void clearAllKeyBindings (ICS::Control* control); void clearAllControllerBindings (ICS::Control* control); private: SDL_Window* mWindow; bool mWindowVisible; osg::ref_ptr mViewer; osg::ref_ptr mScreenCaptureHandler; bool mJoystickLastUsed; MWWorld::Player* mPlayer; ICS::InputControlSystem* mInputBinder; SDLUtil::InputWrapper* mInputManager; SDLUtil::VideoWrapper* mVideoWrapper; std::string mUserFile; bool mDragDrop; bool mGrabCursor; bool mInvertY; bool mControlsDisabled; float mCameraSensitivity; float mCameraYMultiplier; float mPreviewPOVDelay; float mTimeIdle; bool mMouseLookEnabled; bool mGuiCursorEnabled; bool mDetectingKeyboard; float mOverencumberedMessageDelay; float mGuiCursorX; float mGuiCursorY; int mMouseWheel; bool mUserFileExists; bool mAlwaysRunActive; bool mSneakToggles; bool mSneaking; bool mAttemptJump; std::map mControlSwitch; float mInvUiScalingFactor; private: void convertMousePosForMyGUI(int& x, int& y); MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button); virtual std::string sdlControllerAxisToString(int axis); virtual std::string sdlControllerButtonToString(int button); void resetIdleTime(); void updateIdleTime(float dt); void setPlayerControlsEnabled(bool enabled); void updateCursorMode(); bool checkAllowedToUseItems() const; private: void toggleMainMenu(); void toggleSpell(); void toggleWeapon(); void toggleInventory(); void toggleConsole(); void screenshot(); void toggleJournal(); void activate(); void toggleWalking(); void toggleSneaking(); void toggleAutoMove(); void rest(); void quickLoad(); void quickSave(); void quickKey (int index); void showQuickKeysMenu(); bool actionIsActive (int id); void loadKeyDefaults(bool force = false); void loadControllerDefaults(bool force = false); int mFakeDeviceID; //As we only support one controller at a time, use a fake deviceID so we don't lose bindings when switching controllers private: enum Actions { // please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files A_GameMenu, A_Unused, A_Screenshot, // Take a screenshot A_Inventory, // Toggle inventory screen A_Console, // Toggle console screen A_MoveLeft, // Move player left / right A_MoveRight, A_MoveForward, // Forward / Backward A_MoveBackward, A_Activate, A_Use, //Use weapon, spell, etc. A_Jump, A_AutoMove, //Toggle Auto-move forward A_Rest, //Rest A_Journal, //Journal A_Weapon, //Draw/Sheath weapon A_Spell, //Ready/Unready Casting A_Run, //Run when held A_CycleSpellLeft, //cycling through spells A_CycleSpellRight, A_CycleWeaponLeft,//Cycling through weapons A_CycleWeaponRight, A_ToggleSneak, //Toggles Sneak A_AlwaysRun, //Toggle Walking/Running A_Sneak, A_QuickSave, A_QuickLoad, A_QuickMenu, A_ToggleWeapon, A_ToggleSpell, A_TogglePOV, A_QuickKey1, A_QuickKey2, A_QuickKey3, A_QuickKey4, A_QuickKey5, A_QuickKey6, A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10, A_QuickKeysMenu, A_ToggleHUD, A_ToggleDebug, A_LookUpDown, //Joystick look A_LookLeftRight, A_MoveForwardBackward, A_MoveLeftRight, A_Last // Marker for the last item }; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/000077500000000000000000000000001264522266000211655ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwmechanics/activespells.cpp000066400000000000000000000256351264522266000244020ustar00rootroot00000000000000#include "activespells.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" namespace MWMechanics { void ActiveSpells::update() const { bool rebuild = false; MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); // Erase no longer active spells and effects if (mLastUpdate!=now) { TContainer::iterator iter (mSpells.begin()); while (iter!=mSpells.end()) { if (!timeToExpire (iter)) { mSpells.erase (iter++); rebuild = true; } else { std::vector& effects = iter->second.mEffects; for (std::vector::iterator effectIt = effects.begin(); effectIt != effects.end();) { MWWorld::TimeStamp start = iter->second.mTimeStamp; MWWorld::TimeStamp end = start + static_cast(effectIt->mDuration)*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); if (end <= now) { effectIt = effects.erase(effectIt); rebuild = true; } else ++effectIt; } ++iter; } } mLastUpdate = now; } if (mSpellsChanged) { mSpellsChanged = false; rebuild = true; } if (rebuild) rebuildEffects(); } void ActiveSpells::rebuildEffects() const { MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); mEffects = MagicEffects(); for (TIterator iter (begin()); iter!=end(); ++iter) { const MWWorld::TimeStamp& start = iter->second.mTimeStamp; const std::vector& effects = iter->second.mEffects; for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) { double duration = effectIt->mDuration; MWWorld::TimeStamp end = start; end += duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); if (end>now) mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude)); } } } ActiveSpells::ActiveSpells() : mSpellsChanged (false) , mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp()) {} const MagicEffects& ActiveSpells::getMagicEffects() const { update(); return mEffects; } ActiveSpells::TIterator ActiveSpells::begin() const { return mSpells.begin(); } ActiveSpells::TIterator ActiveSpells::end() const { return mSpells.end(); } double ActiveSpells::timeToExpire (const TIterator& iterator) const { const std::vector& effects = iterator->second.mEffects; float duration = 0; for (std::vector::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) { if (iter->mDuration > duration) duration = iter->mDuration; } double scaledDuration = duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp() - iterator->second.mTimeStamp; if (usedUp>=scaledDuration) return 0; return scaledDuration-usedUp; } bool ActiveSpells::isSpellActive(const std::string& id) const { for (TContainer::iterator iter = mSpells.begin(); iter != mSpells.end(); ++iter) { if (Misc::StringUtils::ciEqual(iter->first, id)) return true; } return false; } const ActiveSpells::TContainer& ActiveSpells::getActiveSpells() const { return mSpells; } void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, const std::string &displayName, int casterActorId) { TContainer::iterator it(mSpells.find(id)); ActiveSpellParams params; params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mEffects = effects; params.mDisplayName = displayName; params.mCasterActorId = casterActorId; if (it == end() || stack) { mSpells.insert(std::make_pair(id, params)); } else { // addSpell() is called with effects for a range. // but a spell may have effects with different ranges (e.g. Touch & Target) // so, if we see new effects for same spell assume additional // spell effects and add to existing effects of spell mergeEffects(params.mEffects, it->second.mEffects); it->second = params; } mSpellsChanged = true; } void ActiveSpells::mergeEffects(std::vector& addTo, const std::vector& from) { for (std::vector::const_iterator effect(from.begin()); effect != from.end(); ++effect) { // if effect is not in addTo, add it bool missing = true; for (std::vector::const_iterator iter(addTo.begin()); iter != addTo.end(); ++iter) { if ((effect->mEffectId == iter->mEffectId) && (effect->mArg == iter->mArg)) { missing = false; break; } } if (missing) { addTo.push_back(*effect); } } } void ActiveSpells::removeEffects(const std::string &id) { mSpells.erase(Misc::StringUtils::lowerCase(id)); mSpellsChanged = true; } void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const { for (TContainer::const_iterator it = begin(); it != end(); ++it) { float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); for (std::vector::const_iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end(); ++effectIt) { std::string name = it->second.mDisplayName; float remainingTime = effectIt->mDuration + static_cast(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; float magnitude = effectIt->mMagnitude; if (magnitude) visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration); } } } void ActiveSpells::purgeAll(float chance) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ) { if (Misc::Rng::roll0to99() < chance) mSpells.erase(it++); else ++it; } mSpellsChanged = true; } void ActiveSpells::purgeEffect(short effectId) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { if (effectIt->mEffectId == effectId) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; } } mSpellsChanged = true; } void ActiveSpells::purgeEffect(short effectId, const std::string& sourceId) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { if (effectIt->mEffectId == effectId && it->first == sourceId) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; } } mSpellsChanged = true; } void ActiveSpells::purge(int casterActorId) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectId); if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked && it->second.mCasterActorId == casterActorId) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; } } mSpellsChanged = true; } void ActiveSpells::clear() { mSpells.clear(); mSpellsChanged = true; } void ActiveSpells::writeState(ESM::ActiveSpells &state) const { for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it) { // Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp ESM::ActiveSpells::ActiveSpellParams params; params.mEffects = it->second.mEffects; params.mCasterActorId = it->second.mCasterActorId; params.mDisplayName = it->second.mDisplayName; params.mTimeStamp = it->second.mTimeStamp.toEsm(); state.mSpells.insert (std::make_pair(it->first, params)); } } void ActiveSpells::readState(const ESM::ActiveSpells &state) { for (ESM::ActiveSpells::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) { // Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp ActiveSpellParams params; params.mEffects = it->second.mEffects; params.mCasterActorId = it->second.mCasterActorId; params.mDisplayName = it->second.mDisplayName; params.mTimeStamp = MWWorld::TimeStamp(it->second.mTimeStamp); mSpells.insert (std::make_pair(it->first, params)); mSpellsChanged = true; } } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/activespells.hpp000066400000000000000000000066151264522266000244040ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_ACTIVESPELLS_H #define GAME_MWMECHANICS_ACTIVESPELLS_H #include #include #include #include "../mwworld/timestamp.hpp" #include "magiceffects.hpp" #include #include namespace MWMechanics { /// \brief Lasting spell effects /// /// \note The name of this class is slightly misleading, since it also handels lasting potion /// effects. class ActiveSpells { public: typedef ESM::ActiveEffect ActiveEffect; struct ActiveSpellParams { std::vector mEffects; MWWorld::TimeStamp mTimeStamp; std::string mDisplayName; // The caster that inflicted this spell on us int mCasterActorId; }; typedef std::multimap TContainer; typedef TContainer::const_iterator TIterator; void readState (const ESM::ActiveSpells& state); void writeState (ESM::ActiveSpells& state) const; TIterator begin() const; TIterator end() const; private: mutable TContainer mSpells; mutable MagicEffects mEffects; mutable bool mSpellsChanged; mutable MWWorld::TimeStamp mLastUpdate; void update() const; void rebuildEffects() const; /// Add any effects that are in "from" and not in "addTo" to "addTo" void mergeEffects(std::vector& addTo, const std::vector& from); double timeToExpire (const TIterator& iterator) const; ///< Returns time (in in-game hours) until the spell pointed to by \a iterator /// expires. const TContainer& getActiveSpells() const; public: ActiveSpells(); /// Add lasting effects /// /// \brief addSpell /// \param id ID for stacking purposes. /// \param stack If false, the spell is not added if one with the same ID exists already. /// \param effects /// \param displayName Name for display in magic menu. /// void addSpell (const std::string& id, bool stack, std::vector effects, const std::string& displayName, int casterActorId); /// Removes the active effects from this spell/potion/.. with \a id void removeEffects (const std::string& id); /// Remove all active effects with this effect id void purgeEffect (short effectId); /// Remove all active effects with this effect id and source id void purgeEffect (short effectId, const std::string& sourceId); /// Remove all active effects, if roll succeeds (for each effect) void purgeAll (float chance); /// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId void purge (int casterActorId); /// Remove all spells void clear(); bool isSpellActive (const std::string& id) const; ///< case insensitive const MagicEffects& getMagicEffects() const; void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/actor.cpp000066400000000000000000000010331264522266000227760ustar00rootroot00000000000000#include "actor.hpp" #include "character.hpp" namespace MWMechanics { Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation) { mCharacterController.reset(new CharacterController(ptr, animation)); } void Actor::updatePtr(const MWWorld::Ptr &newPtr) { mCharacterController->updatePtr(newPtr); } CharacterController* Actor::getCharacterController() { return mCharacterController.get(); } AiState& Actor::getAiState() { return mAiState; } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/actor.hpp000066400000000000000000000014611264522266000230100ustar00rootroot00000000000000#ifndef OPENMW_MECHANICS_ACTOR_H #define OPENMW_MECHANICS_ACTOR_H #include #include "aistate.hpp" namespace MWRender { class Animation; } namespace MWWorld { class Ptr; } namespace MWMechanics { class CharacterController; /// @brief Holds temporary state for an actor that will be discarded when the actor leaves the scene. class Actor { public: Actor(const MWWorld::Ptr& ptr, MWRender::Animation* animation); /// Notify this actor of its new base object Ptr, use when the object changed cells void updatePtr(const MWWorld::Ptr& newPtr); CharacterController* getCharacterController(); AiState& getAiState(); private: std::auto_ptr mCharacterController; AiState mAiState; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/actors.cpp000066400000000000000000001761531264522266000232010ustar00rootroot00000000000000#include "actors.hpp" #include #include #include #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/spellcasting.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" #include "movement.hpp" #include "character.hpp" #include "aicombat.hpp" #include "aifollow.hpp" #include "aipursue.hpp" #include "actor.hpp" #include "summoning.hpp" #include "combat.hpp" #include "actorutil.hpp" namespace { bool isConscious(const MWWorld::Ptr& ptr) { const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); return !stats.isDead() && !stats.getKnockedDown(); } void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& actor) { if (bound) { if (actor.getClass().getContainerStore(actor).count(item) == 0) { MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); MWWorld::Ptr newPtr = *store.MWWorld::ContainerStore::add(item, 1, actor); MWWorld::ActionEquip action(newPtr); action.execute(actor); MWWorld::ContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); // change draw state only if the item is in player's right hand if (actor == MWMechanics::getPlayer() && rightHand != store.end() && newPtr == *rightHand) { MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon); } } } else { actor.getClass().getContainerStore(actor).remove(item, 1, actor); } } class CheckActorCommanded : public MWMechanics::EffectSourceVisitor { MWWorld::Ptr mActor; public: bool mCommanded; CheckActorCommanded(MWWorld::Ptr actor) : mActor(actor) , mCommanded(false){} virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { MWWorld::Ptr player = MWMechanics::getPlayer(); if ( ((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc()) || (key.mId == ESM::MagicEffect::CommandCreature && mActor.getTypeName() == typeid(ESM::Creature).name())) && casterActorId == player.getClass().getCreatureStats(player).getActorId() && magnitude >= mActor.getClass().getCreatureStats(mActor).getLevel()) mCommanded = true; } }; void adjustCommandedActor (const MWWorld::Ptr& actor) { CheckActorCommanded check(actor); MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); stats.getActiveSpells().visitEffectSources(check); bool hasCommandPackage = false; std::list::const_iterator it; for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && static_cast(*it)->isCommanded()) { hasCommandPackage = true; break; } } if (check.mCommanded && !hasCommandPackage) { // FIXME: don't use refid string MWMechanics::AiFollow package("player", true); stats.getAiSequence().stack(package, actor); } else if (!check.mCommanded && hasCommandPackage) { stats.getAiSequence().erase(it); } } void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float& magicka) { MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).getMagnitude() > 0; int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); health = 0.1f * endurance; magicka = 0; if (!stunted) { float fRestMagicMult = settings.find("fRestMagicMult")->getFloat (); magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); } } } namespace MWMechanics { class SoulTrap : public MWMechanics::EffectSourceVisitor { MWWorld::Ptr mCreature; MWWorld::Ptr mActor; bool mTrapped; public: SoulTrap(MWWorld::Ptr trappedCreature) : mCreature(trappedCreature) , mTrapped(false) { } virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { if (mTrapped) return; if (key.mId != ESM::MagicEffect::Soultrap) return; if (magnitude <= 0) return; MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr caster = world->searchPtrViaActorId(casterActorId); if (caster.isEmpty() || !caster.getClass().isActor()) return; static const float fSoulgemMult = world->getStore().get().find("fSoulgemMult")->getFloat(); int creatureSoulValue = mCreature.get()->mBase->mData.mSoul; if (creatureSoulValue == 0) return; // Use the smallest soulgem that is large enough to hold the soul MWWorld::ContainerStore& container = caster.getClass().getContainerStore(caster); MWWorld::ContainerStoreIterator gem = container.end(); float gemCapacity = std::numeric_limits::max(); std::string soulgemFilter = "misc_soulgem"; // no other way to check for soulgems? :/ for (MWWorld::ContainerStoreIterator it = container.begin(MWWorld::ContainerStore::Type_Miscellaneous); it != container.end(); ++it) { const std::string& id = it->getCellRef().getRefId(); if (id.size() >= soulgemFilter.size() && id.substr(0,soulgemFilter.size()) == soulgemFilter) { float thisGemCapacity = it->get()->mBase->mData.mValue * fSoulgemMult; if (thisGemCapacity >= creatureSoulValue && thisGemCapacity < gemCapacity && it->getCellRef().getSoul().empty()) { gem = it; gemCapacity = thisGemCapacity; } } } if (gem == container.end()) return; // Set the soul on just one of the gems, not the whole stack gem->getContainerStore()->unstack(*gem, caster); gem->getCellRef().setSoul(mCreature.getCellRef().getRefId()); mTrapped = true; if (caster == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sSoultrapSuccess}"); const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() .search("VFX_Soul_Trap"); if (fx) MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, "", mCreature.getRefData().getPosition().asVec3()); MWBase::Environment::get().getSoundManager()->playSound3D( mCreature.getRefData().getPosition().asVec3(), "conjuration hit", 1.f, 1.f ); } }; void Actors::updateActor (const MWWorld::Ptr& ptr, float duration) { // magic effects adjustMagicEffects (ptr); if (ptr.getClass().getCreatureStats(ptr).needToRecalcDynamicStats()) calculateDynamicStats (ptr); calculateCreatureStatModifiers (ptr, duration); // fatigue restoration calculateRestoration(ptr, duration); } void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance) { static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get() .find("fMaxHeadTrackDistance")->getFloat(); static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fInteriorHeadTrackMult")->getFloat(); float maxDistance = fMaxHeadTrackDistance; const ESM::Cell* currentCell = actor.getCell()->getCell(); if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx)) maxDistance *= fInteriorHeadTrackMult; const ESM::Position& actor1Pos = actor.getRefData().getPosition(); const ESM::Position& actor2Pos = targetActor.getRefData().getPosition(); float sqrDist = (actor1Pos.asVec3() - actor2Pos.asVec3()).length2(); if (sqrDist > maxDistance*maxDistance) return; if (targetActor.getClass().getCreatureStats(targetActor).isDead()) return; if (!actor.getRefData().getBaseNode()) return; // stop tracking when target is behind the actor osg::Vec3f actorDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0); osg::Vec3f targetDirection (actor2Pos.asVec3() - actor1Pos.asVec3()); actorDirection.z() = 0; targetDirection.z() = 0; actorDirection.normalize(); targetDirection.normalize(); if (std::acos(actorDirection * targetDirection) < osg::DegreesToRadians(90.f) && sqrDist <= sqrHeadTrackDistance && MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor)) { sqrHeadTrackDistance = sqrDist; headTrackTarget = targetActor; } } void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) { CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1); if (actor2.getClass().getCreatureStats(actor2).isDead() || actor1.getClass().getCreatureStats(actor1).isDead()) return; const ESM::Position& actor1Pos = actor1.getRefData().getPosition(); const ESM::Position& actor2Pos = actor2.getRefData().getPosition(); float sqrDist = (actor1Pos.asVec3() - actor2Pos.asVec3()).length2(); if (sqrDist > 7168*7168) return; // pure water creatures won't try to fight with the target on the ground // except that creature is already hostile if ((againstPlayer || !creatureStats.getAiSequence().isInCombat()) && !MWMechanics::isEnvironmentCompatible(actor1, actor2)) // creature can't swim to target { return; } // no combat for totally static creatures (they have no movement or attack animations anyway) if (!actor1.getClass().isMobile(actor1)) return; bool aggressive; if (againstPlayer) { // followers with high fight should not engage in combat with the player (e.g. bm_bear_black_summon) const std::list& followers = getActorsSidingWith(actor2); if (std::find(followers.begin(), followers.end(), actor1) != followers.end()) return; aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); } else { aggressive = false; // Make guards fight aggressive creatures if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) { if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) aggressive = true; } } // start combat if target actor is in combat with one of our followers const std::list& followers = getActorsSidingWith(actor1); const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2); for (std::list::const_iterator it = followers.begin(); it != followers.end(); ++it) { // need to check both ways since player doesn't use AI packages if ((creatureStats2.getAiSequence().isInCombat(*it) || it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2)) && !creatureStats.getAiSequence().isInCombat(*it)) aggressive = true; } // start combat if target actor is in combat with someone we are following for (std::list::const_iterator it = creatureStats.getAiSequence().begin(); it != creatureStats.getAiSequence().end(); ++it) { if (!(*it)->sideWithTarget()) continue; MWWorld::Ptr followTarget = (*it)->getTarget(); if (followTarget.isEmpty()) continue; if (creatureStats.getAiSequence().isInCombat(followTarget)) continue; // need to check both ways since player doesn't use AI packages if (creatureStats2.getAiSequence().isInCombat(followTarget) || followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2)) aggressive = true; } if(aggressive) { bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); if (LOS) { MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); if (!againstPlayer) // start combat between each other { MWBase::Environment::get().getMechanicsManager()->startCombat(actor2, actor1); } } } } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration) { updateDrowning(ptr, duration); calculateNpcStatModifiers(ptr, duration); updateEquippedLight(ptr, duration); } void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) { CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature); if (creatureStats.isDead()) return; MagicEffects now = creatureStats.getSpells().getMagicEffects(); if (creature.getTypeName()==typeid (ESM::NPC).name()) { MWWorld::InventoryStore& store = creature.getClass().getInventoryStore (creature); now += store.getMagicEffects(); } now += creatureStats.getActiveSpells().getMagicEffects(); creatureStats.modifyMagicEffects(now); } void Actors::calculateDynamicStats (const MWWorld::Ptr& ptr) { CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); float base = 1.f; if (ptr == getPlayer()) base = MWBase::Environment::get().getWorld()->getStore().get().find("fPCbaseMagickaMult")->getFloat(); else base = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCbaseMagickaMult")->getFloat(); double magickaFactor = base + creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).getMagnitude() * 0.1; DynamicStat magicka = creatureStats.getMagicka(); float diff = (static_cast(magickaFactor*intelligence)) - magicka.getBase(); magicka.modify(diff); creatureStats.setMagicka(magicka); } void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, bool sleep) { if (ptr.getClass().getCreatureStats(ptr).isDead()) return; MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); if (sleep) { float health, magicka; getRestorationPerHourOfSleep(ptr, health, magicka); DynamicStat stat = stats.getHealth(); stat.setCurrent(stat.getCurrent() + health); stats.setHealth(stat); stat = stats.getMagicka(); stat.setCurrent(stat.getCurrent() + magicka); stats.setMagicka(stat); } int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); if (normalizedEncumbrance > 1) normalizedEncumbrance = 1; // restore fatigue float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); stats.setFatigue (fatigue); } void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) { if (ptr.getClass().getCreatureStats(ptr).isDead()) return; MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); // restore fatigue const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float x = fFatigueReturnBase + fFatigueReturnMult * endurance; DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + duration * x); stats.setFatigue (fatigue); } void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration) { CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr); const MagicEffects &effects = creatureStats.getMagicEffects(); bool wasDead = creatureStats.isDead(); if (duration > 0) { for (MagicEffects::Collection::const_iterator it = effects.begin(); it != effects.end(); ++it) { // tickable effects (i.e. effects having a lasting impact after expiry) effectTick(creatureStats, ptr, it->first, it->second.getMagnitude() * duration); // instant effects are already applied on spell impact in spellcasting.cpp, but may also come from permanent abilities if (it->second.getMagnitude() > 0) { CastSpell cast(ptr, ptr); if (cast.applyInstantEffect(ptr, ptr, it->first, it->second.getMagnitude())) { creatureStats.getActiveSpells().purgeEffect(it->first.mId); if (ptr.getClass().hasInventoryStore(ptr)) ptr.getClass().getInventoryStore(ptr).purgeEffect(it->first.mId); } } } } // attributes for(int i = 0;i < ESM::Attribute::Length;++i) { AttributeValue stat = creatureStats.getAttribute(i); stat.setModifier(static_cast(effects.get(EffectKey(ESM::MagicEffect::FortifyAttribute, i)).getMagnitude() - effects.get(EffectKey(ESM::MagicEffect::DrainAttribute, i)).getMagnitude() - effects.get(EffectKey(ESM::MagicEffect::AbsorbAttribute, i)).getMagnitude())); creatureStats.setAttribute(i, stat); } { Spells & spells = creatureStats.getSpells(); for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { if (spells.getCorprusSpells().find(it->first) != spells.getCorprusSpells().end()) { if (MWBase::Environment::get().getWorld()->getTimeStamp() >= spells.getCorprusSpells().at(it->first).mNextWorsening) { spells.worsenCorprus(it->first); if (ptr == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); } } } } // dynamic stats for(int i = 0;i < 3;++i) { DynamicStat stat = creatureStats.getDynamic(i); stat.setModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).getMagnitude() - effects.get(ESM::MagicEffect::DrainHealth+i).getMagnitude(), // Fatigue can be decreased below zero meaning the actor will be knocked out i == 2); creatureStats.setDynamic(i, stat); } // AI setting modifiers int creature = !ptr.getClass().isNpc(); if (creature && ptr.get()->mBase->mData.mType == ESM::Creature::Humanoid) creature = false; // Note: the Creature variants only work on normal creatures, not on daedra or undead creatures. if (!creature || ptr.get()->mBase->mData.mType == ESM::Creature::Creatures) { Stat stat = creatureStats.getAiSetting(CreatureStats::AI_Fight); stat.setModifier(static_cast(effects.get(ESM::MagicEffect::FrenzyHumanoid + creature).getMagnitude() - effects.get(ESM::MagicEffect::CalmHumanoid+creature).getMagnitude())); creatureStats.setAiSetting(CreatureStats::AI_Fight, stat); stat = creatureStats.getAiSetting(CreatureStats::AI_Flee); stat.setModifier(static_cast(effects.get(ESM::MagicEffect::DemoralizeHumanoid + creature).getMagnitude() - effects.get(ESM::MagicEffect::RallyHumanoid+creature).getMagnitude())); creatureStats.setAiSetting(CreatureStats::AI_Flee, stat); } if (creature && ptr.get()->mBase->mData.mType == ESM::Creature::Undead) { Stat stat = creatureStats.getAiSetting(CreatureStats::AI_Flee); stat.setModifier(static_cast(effects.get(ESM::MagicEffect::TurnUndead).getMagnitude())); creatureStats.setAiSetting(CreatureStats::AI_Flee, stat); } if (!wasDead && creatureStats.isDead()) { // The actor was killed by a magic effect. Figure out if the player was responsible for it. const ActiveSpells& spells = creatureStats.getActiveSpells(); bool killedByPlayer = false; MWWorld::Ptr player = getPlayer(); for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ActiveSpells::ActiveSpellParams& spell = it->second; for (std::vector::const_iterator effectIt = spell.mEffects.begin(); effectIt != spell.mEffects.end(); ++effectIt) { int effectId = effectIt->mEffectId; bool isDamageEffect = false; int damageEffects[] = { ESM::MagicEffect::FireDamage, ESM::MagicEffect::ShockDamage, ESM::MagicEffect::FrostDamage, ESM::MagicEffect::Poison, ESM::MagicEffect::SunDamage, ESM::MagicEffect::DamageHealth, ESM::MagicEffect::AbsorbHealth }; for (unsigned int i=0; isearchPtrViaActorId(spell.mCasterActorId); if (isDamageEffect && caster == player) killedByPlayer = true; } } if (killedByPlayer) { MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player); if (player.getClass().getNpcStats(player).isWerewolf()) player.getClass().getNpcStats(player).addWerewolfKill(); } } // TODO: dirty flag for magic effects to avoid some unnecessary work below? // any value of calm > 0 will stop the actor from fighting if ((effects.get(ESM::MagicEffect::CalmHumanoid).getMagnitude() > 0 && ptr.getClass().isNpc()) || (effects.get(ESM::MagicEffect::CalmCreature).getMagnitude() > 0 && !ptr.getClass().isNpc())) { for (std::list::const_iterator it = creatureStats.getAiSequence().begin(); it != creatureStats.getAiSequence().end(); ) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) it = creatureStats.getAiSequence().erase(it); else ++it; } } // Update bound effects // Note: in vanilla MW multiple bound items of the same type can be created by different spells. // As these extra copies are kinda useless this may or may not be important. static std::map boundItemsMap; if (boundItemsMap.empty()) { boundItemsMap[ESM::MagicEffect::BoundBattleAxe] = "sMagicBoundBattleAxeID"; boundItemsMap[ESM::MagicEffect::BoundBoots] = "sMagicBoundBootsID"; boundItemsMap[ESM::MagicEffect::BoundCuirass] = "sMagicBoundCuirassID"; boundItemsMap[ESM::MagicEffect::BoundDagger] = "sMagicBoundDaggerID"; boundItemsMap[ESM::MagicEffect::BoundGloves] = "sMagicBoundLeftGauntletID"; // Note: needs RightGauntlet variant too (see below) boundItemsMap[ESM::MagicEffect::BoundHelm] = "sMagicBoundHelmID"; boundItemsMap[ESM::MagicEffect::BoundLongbow] = "sMagicBoundLongbowID"; boundItemsMap[ESM::MagicEffect::BoundLongsword] = "sMagicBoundLongswordID"; boundItemsMap[ESM::MagicEffect::BoundMace] = "sMagicBoundMaceID"; boundItemsMap[ESM::MagicEffect::BoundShield] = "sMagicBoundShieldID"; boundItemsMap[ESM::MagicEffect::BoundSpear] = "sMagicBoundSpearID"; } for (std::map::iterator it = boundItemsMap.begin(); it != boundItemsMap.end(); ++it) { bool found = creatureStats.mBoundItems.find(it->first) != creatureStats.mBoundItems.end(); float magnitude = effects.get(it->first).getMagnitude(); if (found != (magnitude > 0)) { std::string itemGmst = it->second; std::string item = MWBase::Environment::get().getWorld()->getStore().get().find( itemGmst)->getString(); if (it->first == ESM::MagicEffect::BoundGloves) { item = MWBase::Environment::get().getWorld()->getStore().get().find( "sMagicBoundLeftGauntletID")->getString(); adjustBoundItem(item, magnitude > 0, ptr); item = MWBase::Environment::get().getWorld()->getStore().get().find( "sMagicBoundRightGauntletID")->getString(); adjustBoundItem(item, magnitude > 0, ptr); } else adjustBoundItem(item, magnitude > 0, ptr); if (magnitude > 0) creatureStats.mBoundItems.insert(it->first); else creatureStats.mBoundItems.erase(it->first); } } UpdateSummonedCreatures updateSummonedCreatures(ptr); creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures); if (ptr.getClass().hasInventoryStore(ptr)) ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures); updateSummonedCreatures.finish(); } void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration) { NpcStats &npcStats = ptr.getClass().getNpcStats(ptr); const MagicEffects &effects = npcStats.getMagicEffects(); // skills for(int i = 0;i < ESM::Skill::Length;++i) { SkillValue& skill = npcStats.getSkill(i); skill.setModifier(static_cast(effects.get(EffectKey(ESM::MagicEffect::FortifySkill, i)).getMagnitude() - effects.get(EffectKey(ESM::MagicEffect::DrainSkill, i)).getMagnitude() - effects.get(EffectKey(ESM::MagicEffect::AbsorbSkill, i)).getMagnitude())); } } void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) { PtrActorMap::iterator it = mActors.find(ptr); if (it == mActors.end()) return; CharacterController* ctrl = it->second->getCharacterController(); NpcStats &stats = ptr.getClass().getNpcStats(ptr); MWBase::World *world = MWBase::Environment::get().getWorld(); bool knockedOutUnderwater = (ctrl->isKnockedOut() && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); if((world->isSubmerged(ptr) || knockedOutUnderwater) && stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0) { float timeLeft = 0.0f; if(knockedOutUnderwater) stats.setTimeToStartDrowning(0); else { timeLeft = stats.getTimeToStartDrowning() - duration; if(timeLeft < 0.0f) timeLeft = 0.0f; stats.setTimeToStartDrowning(timeLeft); } if(timeLeft == 0.0f) { // If drowning, apply 3 points of damage per second static const float fSuffocationDamage = world->getStore().get().find("fSuffocationDamage")->getFloat(); DynamicStat health = stats.getHealth(); health.setCurrent(health.getCurrent() - fSuffocationDamage*duration); stats.setHealth(health); // Play a drowning sound MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager(); if(!sndmgr->getSoundPlaying(ptr, "drown")) sndmgr->playSound3D(ptr, "drown", 1.0f, 1.0f); if(ptr == world->getPlayerPtr()) MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); } } else { static const float fHoldBreathTime = world->getStore().get().find("fHoldBreathTime")->getFloat(); stats.setTimeToStartDrowning(fHoldBreathTime); } } void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration) { bool isPlayer = (ptr == getPlayer()); MWWorld::InventoryStore &inventoryStore = ptr.getClass().getInventoryStore(ptr); MWWorld::ContainerStoreIterator heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); /** * Automatically equip NPCs torches at night and unequip them at day */ if (!isPlayer) { MWWorld::ContainerStoreIterator torch = inventoryStore.end(); for (MWWorld::ContainerStoreIterator it = inventoryStore.begin(); it != inventoryStore.end(); ++it) { if (it->getTypeName() == typeid(ESM::Light).name()) { torch = it; break; } } if (MWBase::Environment::get().getWorld()->isDark()) { if (torch != inventoryStore.end()) { if (!ptr.getClass().getCreatureStats (ptr).getAiSequence().isInCombat()) { // For non-hostile NPCs, unequip whatever is in the left slot in favor of a light. if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) inventoryStore.unequipItem(*heldIter, ptr); // Also unequip twohanded weapons which conflict with anything in CarriedLeft if (torch->getClass().canBeEquipped(*torch, ptr).first == 3) inventoryStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, ptr); } heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); // If we have a torch and can equip it (left slot free, no // twohanded weapon in right slot), then equip it now. if (heldIter == inventoryStore.end() && torch->getClass().canBeEquipped(*torch, ptr).first == 1) { inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr); } } } else { if (heldIter != inventoryStore.end() && heldIter->getTypeName() == typeid(ESM::Light).name()) { // At day, unequip lights and auto equip shields or other suitable items // (Note: autoEquip will ignore lights) inventoryStore.autoEquip(ptr); } } } heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); //If holding a light... if(heldIter.getType() == MWWorld::ContainerStore::Type_Light) { // Use time from the player's light if(isPlayer) { float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter); // -1 is infinite light source. Other negative values are treated as 0. if(timeRemaining != -1.0f) { timeRemaining -= duration; if(timeRemaining > 0.0f) heldIter->getClass().setRemainingUsageTime(*heldIter, timeRemaining); else { inventoryStore.remove(*heldIter, 1, ptr); // remove it return; } } } // Both NPC and player lights extinguish in water. if(MWBase::Environment::get().getWorld()->isSwimming(ptr)) { inventoryStore.remove(*heldIter, 1, ptr); // remove it // ...But, only the player makes a sound. if(isPlayer) MWBase::Environment::get().getSoundManager()->playSound("torch out", 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoEnv); } } } void Actors::updateCrimePersuit(const MWWorld::Ptr& ptr, float duration) { MWWorld::Ptr player = getPlayer(); if (ptr != player && ptr.getClass().isNpc()) { // get stats of witness CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr); NpcStats& npcStats = ptr.getClass().getNpcStats(ptr); if (player.getClass().getNpcStats(player).isWerewolf()) return; if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat()) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); static const int cutoff = esmStore.get().find("iCrimeThreshold")->getInt(); // Force dialogue on sight if bounty is greater than the cutoff // In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty) if ( player.getClass().getNpcStats(player).getBounty() >= cutoff // TODO: do not run these two every frame. keep an Aware state for each actor and update it every 0.2 s or so? && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { static const int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->getInt(); if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); else creatureStats.getAiSequence().stack(AiPursue(player), ptr); creatureStats.setAlarmed(true); npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId()); } } // if I was a witness to a crime if (npcStats.getCrimeId() != -1) { // if you've paid for your crimes and I havent noticed if( npcStats.getCrimeId() <= MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() ) { // Calm witness down if (ptr.getClass().isClass(ptr, "Guard")) creatureStats.getAiSequence().stopPursuit(); creatureStats.getAiSequence().stopCombat(); // Reset factors to attack creatureStats.setAttacked(false); creatureStats.setAlarmed(false); creatureStats.setAiSetting(CreatureStats::AI_Fight, ptr.getClass().getBaseFightRating(ptr)); // Update witness crime id npcStats.setCrimeId(-1); } } } } Actors::Actors() {} Actors::~Actors() { clear(); } void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately) { removeActor(ptr); MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); if (!anim) return; mActors.insert(std::make_pair(ptr, new Actor(ptr, anim))); if (updateImmediately) mActors[ptr]->getCharacterController()->update(0); } void Actors::removeActor (const MWWorld::Ptr& ptr) { PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) { delete iter->second; mActors.erase(iter); } } void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) { PtrActorMap::iterator iter = mActors.find(old); if(iter != mActors.end()) { Actor *actor = iter->second; mActors.erase(iter); actor->updatePtr(ptr); mActors.insert(std::make_pair(ptr, actor)); } } void Actors::dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore) { PtrActorMap::iterator iter = mActors.begin(); while(iter != mActors.end()) { if((iter->first.isInCell() && iter->first.getCell()==cellStore) && iter->first != ignore) { delete iter->second; mActors.erase(iter++); } else ++iter; } } void Actors::update (float duration, bool paused) { if(!paused) { static float timerUpdateAITargets = 0; static float timerUpdateHeadTrack = 0; // target lists get updated once every 1.0 sec if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0; if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0; MWWorld::Ptr player = getPlayer(); int hostilesCount = 0; // need to know this to play Battle music // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) // This distance could be made configurable later, but the setting must be marked with a big warning: // using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876) const float sqrProcessingDistance = 7168*7168; /// \todo move update logic to Actor class where appropriate // AI and magic effects update for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() <= sqrProcessingDistance; iter->second->getCharacterController()->setActive(inProcessingRange); if (iter->first == player) iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { updateActor(iter->first, duration); if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange) { if (timerUpdateAITargets == 0) { if (iter->first != player) adjustCommandedActor(iter->first); for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { if (it->first == iter->first || iter->first == player) // player is not AI-controlled continue; engageCombat(iter->first, it->first, it->first == player); } } if (timerUpdateHeadTrack == 0) { float sqrHeadTrackDistance = std::numeric_limits::max(); MWWorld::Ptr headTrackTarget; for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { if (it->first == iter->first) continue; updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance); } iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); } if (iter->first.getClass().isNpc() && iter->first != player) updateCrimePersuit(iter->first, duration); if (iter->first != player) { CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); if (isConscious(iter->first)) stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), iter->second->getAiState(), duration); if (stats.getAiSequence().isInCombat() && !stats.isDead()) hostilesCount++; } } if(iter->first.getTypeName() == typeid(ESM::NPC).name()) updateNpc(iter->first, duration); } } timerUpdateAITargets += duration; timerUpdateHeadTrack += duration; // Looping magic VFX update // Note: we need to do this before any of the animations are updated. // Reaching the text keys may trigger Hit / Spellcast (and as such, particles), // so updating VFX immediately after that would just remove the particle effects instantly. // There needs to be a magic effect update in between. for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) iter->second->getCharacterController()->updateContinuousVfx(); // Animation/movement update CharacterController* playerCharacter = NULL; for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { if (iter->first != player && (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() > sqrProcessingDistance) continue; if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) iter->second->getCharacterController()->skipAnim(); // Handle player last, in case a cell transition occurs by casting a teleportation spell // (would invalidate the iterator) if (iter->first == getPlayer()) { playerCharacter = iter->second->getCharacterController(); continue; } iter->second->getCharacterController()->update(duration); } if (playerCharacter) playerCharacter->update(duration); for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const MWWorld::Class &cls = iter->first.getClass(); CreatureStats &stats = cls.getCreatureStats(iter->first); //KnockedOutOneFrameLogic //Used for "OnKnockedOut" command //Put here to ensure that it's run for PRECISELY one frame. if (stats.getKnockedDown() && !stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Start it for one frame if nessesary stats.setKnockedDownOneFrame(true); } else if (stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Turn off KnockedOutOneframe stats.setKnockedDownOneFrame(false); stats.setKnockedDownOverOneFrame(true); } } killDeadActors(); // check if we still have any player enemies to switch music static int currentMusic = 0; if (currentMusic != 1 && hostilesCount == 0 && !(player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getSoundManager()->isMusicPlaying())) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); currentMusic = 1; } else if (currentMusic != 2 && hostilesCount > 0) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle")); currentMusic = 2; } static float sneakTimer = 0.f; // times update of sneak icon // if player is in sneak state see if anyone detects him if (playerCharacter && playerCharacter->isSneaking()) { static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice" const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const int radius = esmStore.get().find("fSneakUseDist")->getInt(); static float fSneakUseDelay = esmStore.get().find("fSneakUseDelay")->getFloat(); if (sneakTimer >= fSneakUseDelay) sneakTimer = 0.f; if (sneakTimer == 0.f) { // Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress. bool avoidedNotice = false; bool detected = false; for (PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { if (iter->first == player) // not the player continue; // is the player in range and can they be detected if ((iter->first.getRefData().getPosition().asVec3() - player.getRefData().getPosition().asVec3()).length2() <= radius*radius && MWBase::Environment::get().getWorld()->getLOS(player, iter->first)) { if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, iter->first)) { detected = true; avoidedNotice = false; MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); break; } else if (!detected) avoidedNotice = true; } } if (sneakSkillTimer >= fSneakUseDelay) sneakSkillTimer = 0.f; if (avoidedNotice && sneakSkillTimer == 0.f) player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 0); if (!detected) MWBase::Environment::get().getWindowManager()->setSneakVisibility(true); } sneakTimer += duration; sneakSkillTimer += duration; } else { sneakTimer = 0.f; MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); } } } void Actors::killDeadActors() { for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const MWWorld::Class &cls = iter->first.getClass(); CreatureStats &stats = cls.getCreatureStats(iter->first); if(!stats.isDead()) { if(iter->second->getCharacterController()->isDead()) { // Actor has been resurrected. Notify the CharacterController and re-enable collision. MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true); iter->second->getCharacterController()->resurrect(); } if(!stats.isDead()) continue; } if (iter->second->getCharacterController()->kill()) { // TODO: It's not known whether the soundgen tags scream, roar, and moan are reliable // for NPCs since some of the npc death animation files are missing them. // Play dying words MWBase::Environment::get().getDialogueManager()->say(iter->first, "hit"); iter->first.getClass().getCreatureStats(iter->first).notifyDied(); ++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())]; // Make sure spell effects with CasterLinked flag are removed for (PtrActorMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) { MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); spells.purge(stats.getActorId()); } // Apply soultrap if (iter->first.getTypeName() == typeid(ESM::Creature).name()) { SoulTrap soulTrap (iter->first); stats.getActiveSpells().visitEffectSources(soulTrap); } // Reset magic effects and recalculate derived effects // One case where we need this is to make sure bound items are removed upon death stats.modifyMagicEffects(MWMechanics::MagicEffects()); stats.getActiveSpells().clear(); calculateCreatureStatModifiers(iter->first, 0); MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); if (cls.isEssential(iter->first)) MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); } } } void Actors::restoreDynamicStats(bool sleep) { for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) restoreDynamicStats(iter->first, sleep); } int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const { float healthPerHour, magickaPerHour; getRestorationPerHourOfSleep(ptr, healthPerHour, magickaPerHour); CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); float healthHours = healthPerHour > 0 ? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour : 1.0f; float magickaHours = magickaPerHour > 0 ? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour : 1.0f; int autoHours = static_cast(std::ceil(std::max(1.f, std::max(healthHours, magickaHours)))); return autoHours; } int Actors::countDeaths (const std::string& id) const { std::map::const_iterator iter = mDeathCount.find(id); if(iter != mDeathCount.end()) return iter->second; return 0; } void Actors::forceStateUpdate(const MWWorld::Ptr & ptr) { PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) iter->second->getCharacterController()->forceStateUpdate(); } bool Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) { return iter->second->getCharacterController()->playGroup(groupName, mode, number); } else { std::cerr<< "Error in Actors::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId() << std::endl; return false; } } void Actors::skipAnimation(const MWWorld::Ptr& ptr) { PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) iter->second->getCharacterController()->skipAnim(); } bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) { PtrActorMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) return iter->second->getCharacterController()->isAnimPlaying(groupName); return false; } void Actors::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector& out) { for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { if ((iter->first.getRefData().getPosition().asVec3() - position).length2() <= radius*radius) out.push_back(iter->first); } } std::list Actors::getActorsSidingWith(const MWWorld::Ptr& actor) { std::list list; for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Class &cls = iter->first.getClass(); CreatureStats &stats = cls.getCreatureStats(iter->first); if (stats.isDead()) continue; // An actor counts as following if AiFollow or AiEscort is the current AiPackage, or there are only Combat packages before the AiFollow/AiEscort package for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) { if ((*it)->sideWithTarget() && (*it)->getTarget() == actor) list.push_back(iter->first); else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) break; } } return list; } std::list Actors::getActorsFollowing(const MWWorld::Ptr& actor) { std::list list; for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Class &cls = iter->first.getClass(); CreatureStats &stats = cls.getCreatureStats(iter->first); if (stats.isDead()) continue; // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) { if ((*it)->followTargetThroughDoors() && (*it)->getTarget() == actor) list.push_back(iter->first); else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) break; } } return list; } std::list Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor) { std::list list; for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Class &cls = iter->first.getClass(); CreatureStats &stats = cls.getCreatureStats(iter->first); if (stats.isDead()) continue; // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) { MWWorld::Ptr followTarget = static_cast(*it)->getTarget(); if (followTarget.isEmpty()) continue; if (followTarget == actor) list.push_back(static_cast(*it)->getFollowIndex()); break; } else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) break; } } return list; } std::list Actors::getActorsFighting(const MWWorld::Ptr& actor) { std::list list; std::vector neighbors; osg::Vec3f position (actor.getRefData().getPosition().asVec3()); getObjectsInRange(position, MWBase::Environment::get().getWorld()->getStore().get().find("fAlarmRadius")->getFloat(), neighbors); //only care about those within the alarm disance for(std::vector::iterator iter(neighbors.begin());iter != neighbors.end();++iter) { const MWWorld::Class &cls = iter->getClass(); CreatureStats &stats = cls.getCreatureStats(*iter); if (!stats.isDead() && stats.getAiSequence().isInCombat(actor)) list.push_front(*iter); } return list; } void Actors::write (ESM::ESMWriter& writer, Loading::Listener& listener) const { writer.startRecord(ESM::REC_DCOU); for (std::map::const_iterator it = mDeathCount.begin(); it != mDeathCount.end(); ++it) { writer.writeHNString("ID__", it->first); writer.writeHNT ("COUN", it->second); } writer.endRecord(ESM::REC_DCOU); } void Actors::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type == ESM::REC_DCOU) { while (reader.isNextSub("ID__")) { std::string id = reader.getHString(); int count; reader.getHNT (count, "COUN"); mDeathCount[id] = count; } } } void Actors::clear() { PtrActorMap::iterator it(mActors.begin()); for (; it != mActors.end(); ++it) { delete it->second; it->second = NULL; } mActors.clear(); mDeathCount.clear(); } void Actors::updateMagicEffects(const MWWorld::Ptr &ptr) { adjustMagicEffects(ptr); calculateCreatureStatModifiers(ptr, 0.f); if (ptr.getClass().isNpc()) calculateNpcStatModifiers(ptr, 0.f); } bool Actors::isReadyToBlock(const MWWorld::Ptr &ptr) const { PtrActorMap::const_iterator it = mActors.find(ptr); if (it == mActors.end()) return false; return it->second->getCharacterController()->isReadyToBlock(); } void Actors::fastForwardAi() { if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) return; // making a copy since fast-forward could move actor to a different cell and invalidate the mActors iterator PtrActorMap map = mActors; for (PtrActorMap::iterator it = map.begin(); it != map.end(); ++it) { MWWorld::Ptr ptr = it->first; if (ptr == getPlayer() || !isConscious(ptr) || ptr.getClass().getCreatureStats(ptr).isParalyzed()) continue; MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); seq.fastForward(ptr, it->second->getAiState()); } } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/actors.hpp000066400000000000000000000124221264522266000231720ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_ACTORS_H #define GAME_MWMECHANICS_ACTORS_H #include #include #include #include #include #include "movement.hpp" #include "../mwbase/world.hpp" namespace MWWorld { class Ptr; class CellStore; } namespace MWMechanics { class Actor; class Actors { std::map mDeathCount; void updateNpc(const MWWorld::Ptr &ptr, float duration); void adjustMagicEffects (const MWWorld::Ptr& creature); void calculateDynamicStats (const MWWorld::Ptr& ptr); void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration); void calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration); void calculateRestoration (const MWWorld::Ptr& ptr, float duration); void updateDrowning (const MWWorld::Ptr& ptr, float duration); void updateEquippedLight (const MWWorld::Ptr& ptr, float duration); void updateCrimePersuit (const MWWorld::Ptr& ptr, float duration); void killDeadActors (); public: Actors(); ~Actors(); typedef std::map PtrActorMap; PtrActorMap::const_iterator begin() { return mActors.begin(); } PtrActorMap::const_iterator end() { return mActors.end(); } /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) void updateMagicEffects (const MWWorld::Ptr& ptr); void addActor (const MWWorld::Ptr& ptr, bool updateImmediately=false); ///< Register an actor for stats management /// /// \note Dead actors are ignored. void removeActor (const MWWorld::Ptr& ptr); ///< Deregister an actor for stats management /// /// \note Ignored, if \a ptr is not a registered actor. void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); ///< Updates an actor with a new Ptr void dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore); ///< Deregister all actors (except for \a ignore) in the given cell. void update (float duration, bool paused); ///< Update actor stats and store desired velocity vectors in \a movement void updateActor (const MWWorld::Ptr& ptr, float duration); ///< This function is normally called automatically during the update process, but it can /// also be called explicitly at any time to force an update. /** Start combat between two actors @Notes: If againstPlayer = true then actor2 should be the Player. If one of the combatants is creature it should be actor1. */ void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer); void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance); void restoreDynamicStats(bool sleep); ///< If the player is sleeping, this should be called every hour. void restoreDynamicStats(const MWWorld::Ptr& actor, bool sleep); int getHoursToRest(const MWWorld::Ptr& ptr) const; ///< Calculate how many hours the given actor needs to rest in order to be fully healed void fastForwardAi(); ///< Simulate the passing of time int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. void forceStateUpdate(const MWWorld::Ptr &ptr); bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); void getObjectsInRange(const osg::Vec3f& position, float radius, std::vector& out); ///Returns the list of actors which are siding with the given actor in fights /**ie AiFollow or AiEscort is active and the target is the actor **/ std::list getActorsSidingWith(const MWWorld::Ptr& actor); std::list getActorsFollowing(const MWWorld::Ptr& actor); /// Get the list of AiFollow::mFollowIndex for all actors following this target std::list getActorsFollowingIndices(const MWWorld::Ptr& actor); ///Returns the list of actors which are fighting the given actor /**ie AiCombat is active and the target is the actor **/ std::list getActorsFighting(const MWWorld::Ptr& actor); void write (ESM::ESMWriter& writer, Loading::Listener& listener) const; void readRecord (ESM::ESMReader& reader, uint32_t type); void clear(); // Clear death counter bool isReadyToBlock(const MWWorld::Ptr& ptr) const; private: PtrActorMap mActors; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/actorutil.cpp000066400000000000000000000006031264522266000236760ustar00rootroot00000000000000#include "actorutil.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/player.hpp" namespace MWMechanics { MWWorld::Ptr getPlayer() { return MWBase::Environment::get().getWorld()->getPlayerPtr(); } bool isPlayerInCombat() { return MWBase::Environment::get().getWorld()->getPlayer().isInCombat(); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/actorutil.hpp000066400000000000000000000003131264522266000237010ustar00rootroot00000000000000#ifndef OPENMW_MWMECHANICS_ACTORUTIL_H #define OPENMW_MWMECHANICS_ACTORUTIL_H #include "../mwworld/ptr.hpp" namespace MWMechanics { MWWorld::Ptr getPlayer(); bool isPlayerInCombat(); } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aiactivate.cpp000066400000000000000000000047051264522266000240110ustar00rootroot00000000000000#include "aiactivate.hpp" #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwworld/class.hpp" #include "steering.hpp" #include "movement.hpp" MWMechanics::AiActivate::AiActivate(const std::string &objectId) : mObjectId(objectId) { } MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const { return new AiActivate(*this); } bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { ESM::Position pos = actor.getRefData().getPosition(); //position of the actor const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); if(target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should be checking whether the target is currently registered // with the MechanicsManager ) return true; //Target doesn't exist //Set the target desition from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close actor.getClass().getMovementSettings(actor).mPosition[1] = 0; MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); MWBase::Environment::get().getWorld()->activate(target, actor); return true; } else { pathTo(actor, dest, duration); //Go to the destination } return false; } int MWMechanics::AiActivate::getTypeId() const { return TypeIdActivate; } void MWMechanics::AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr activate(new ESM::AiSequence::AiActivate()); activate->mTargetId = mObjectId; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Activate; package.mPackage = activate.release(); sequence.mPackages.push_back(package); } MWMechanics::AiActivate::AiActivate(const ESM::AiSequence::AiActivate *activate) : mObjectId(activate->mTargetId) { } openmw-openmw-0.38.0/apps/openmw/mwmechanics/aiactivate.hpp000066400000000000000000000021061264522266000240070ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AIACTIVATE_H #define GAME_MWMECHANICS_AIACTIVATE_H #include "aipackage.hpp" #include #include "pathfinding.hpp" namespace ESM { namespace AiSequence { struct AiActivate; } } namespace MWMechanics { /// \brief Causes actor to walk to activatable object and activate it /** Will activate when close to object **/ class AiActivate : public AiPackage { public: /// Constructor /** \param objectId Reference to object to activate **/ AiActivate(const std::string &objectId); AiActivate(const ESM::AiSequence::AiActivate* activate); virtual AiActivate *clone() const; virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); virtual int getTypeId() const; virtual void writeState(ESM::AiSequence::AiSequence& sequence) const; private: std::string mObjectId; }; } #endif // GAME_MWMECHANICS_AIACTIVATE_H openmw-openmw-0.38.0/apps/openmw/mwmechanics/aiavoiddoor.cpp000066400000000000000000000060631264522266000241760ustar00rootroot00000000000000#include "aiavoiddoor.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "creaturestats.hpp" #include "movement.hpp" #include "actorutil.hpp" #include "steering.hpp" MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr) : AiPackage(), mDuration(1), mDoorPtr(doorPtr), mAdjAngle(0) { } bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { ESM::Position pos = actor.getRefData().getPosition(); if(mDuration == 1) //If it just started, get the actor position as the stuck detection thing mLastPos = pos; mDuration -= duration; //Update timer if(mDuration < 0) { float x = pos.pos[0] - mLastPos.pos[0]; float y = pos.pos[1] - mLastPos.pos[1]; float z = pos.pos[2] - mLastPos.pos[2]; float distance = x * x + y * y + z * z; if(distance < 10 * 10) { //Got stuck, didn't move if(mAdjAngle == 0) //Try going in various directions mAdjAngle = 1.57079632679f; //pi/2 else if (mAdjAngle == 1.57079632679f) mAdjAngle = -1.57079632679f; else mAdjAngle = 0; mDuration = 1; //reset timer } else //Not stuck return true; // We have tried backing up for more than one second, we've probably cleared it } if (!mDoorPtr.getClass().getDoorState(mDoorPtr)) return true; //Door is no longer opening ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door float x = pos.pos[0] - tPos.pos[0]; float y = pos.pos[1] - tPos.pos[1]; actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); // Turn away from the door and move when turn completed if (zTurn(actor, std::atan2(x,y) + mAdjAngle, osg::DegreesToRadians(5.f))) actor.getClass().getMovementSettings(actor).mPosition[1] = 1; else actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[0] = 0; // Make all nearby actors also avoid the door std::vector actors; MWBase::Environment::get().getMechanicsManager()->getActorsInRange(pos.asVec3(),100,actors); for(std::vector::iterator it = actors.begin(); it != actors.end(); ++it) { if(*it != getPlayer()) { //Not the player MWMechanics::AiSequence& seq = it->getClass().getCreatureStats(*it).getAiSequence(); if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) { //Only add it once seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it); } } } return false; } MWMechanics::AiAvoidDoor *MWMechanics::AiAvoidDoor::clone() const { return new AiAvoidDoor(*this); } int MWMechanics::AiAvoidDoor::getTypeId() const { return TypeIdAvoidDoor; } unsigned int MWMechanics::AiAvoidDoor::getPriority() const { return 2; } openmw-openmw-0.38.0/apps/openmw/mwmechanics/aiavoiddoor.hpp000066400000000000000000000021071264522266000241760ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H #define GAME_MWMECHANICS_AIAVOIDDOOR_H #include "aipackage.hpp" #include #include "pathfinding.hpp" #include #include "../mwworld/class.hpp" namespace MWMechanics { /// \brief AiPackage to have an actor avoid an opening door /** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it **/ class AiAvoidDoor : public AiPackage { public: /// Avoid door until the door is fully open AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); virtual AiAvoidDoor *clone() const; virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); virtual int getTypeId() const; virtual unsigned int getPriority() const; private: float mDuration; MWWorld::ConstPtr mDoorPtr; ESM::Position mLastPos; float mAdjAngle; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aicombat.cpp000066400000000000000000000742341264522266000234620ustar00rootroot00000000000000#include "aicombat.hpp" #include #include #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwrender/animation.hpp" #include "creaturestats.hpp" #include "steering.hpp" #include "movement.hpp" #include "character.hpp" #include "aicombataction.hpp" #include "combat.hpp" namespace { //chooses an attack depending on probability to avoid uniformity ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const osg::Vec3f& vLastTargetPos, float duration, int weapType, float strength); float getZAngleToDir(const osg::Vec3f& dir) { return std::atan2(dir.x(), dir.y()); } float getXAngleToDir(const osg::Vec3f& dir) { return -std::asin(dir.z() / dir.length()); } const float REACTION_INTERVAL = 0.25f; const float PATHFIND_Z_REACH = 50.0f; // distance at which actor pays more attention to decide whether to shortcut or stick to pathgrid const float PATHFIND_CAUTION_DIST = 500.0f; // distance after which actor (failed previously to shortcut) will try again const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f; // cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target; // magnitude of pits/obstacles is defined by PATHFIND_Z_REACH bool checkWayIsClear(const osg::Vec3f& from, const osg::Vec3f& to, float offsetXY) { if((to - from).length() >= PATHFIND_CAUTION_DIST || std::abs(from.z() - to.z()) <= PATHFIND_Z_REACH) { osg::Vec3f dir = to - from; dir.z() = 0; dir.normalize(); float verticalOffset = 200; // instead of '200' here we want the height of the actor osg::Vec3f _from = from + dir*offsetXY + osg::Vec3f(0,0,1) * verticalOffset; // cast up-down ray and find height in world space of hit float h = _from.z() - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, osg::Vec3f(0,0,-1), verticalOffset + PATHFIND_Z_REACH + 1); if(std::abs(from.z() - h) <= PATHFIND_Z_REACH) return true; } return false; } } namespace MWMechanics { /// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive. struct AiCombatStorage : AiTemporaryBase { float mAttackCooldown; float mTimerReact; float mTimerCombatMove; bool mReadyToAttack; bool mAttack; bool mFollowTarget; bool mCombatMove; osg::Vec3f mLastTargetPos; const MWWorld::CellStore* mCell; boost::shared_ptr mCurrentAction; float mActionCooldown; float mStrength; bool mForceNoShortcut; ESM::Position mShortcutFailPos; osg::Vec3f mLastActorPos; MWMechanics::Movement mMovement; AiCombatStorage(): mAttackCooldown(0), mTimerReact(0), mTimerCombatMove(0), mReadyToAttack(false), mAttack(false), mFollowTarget(false), mCombatMove(false), mLastTargetPos(0,0,0), mCell(NULL), mCurrentAction(), mActionCooldown(0), mStrength(), mForceNoShortcut(false), mLastActorPos(0,0,0), mMovement(){} void startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack); void updateCombatMove(float duration); void stopCombatMove(); void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, const ESM::Weapon* weapon, bool distantCombat); void updateAttack(CharacterController& characterController); void stopAttack(); }; AiCombat::AiCombat(const MWWorld::Ptr& actor) : mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()) {} AiCombat::AiCombat(const ESM::AiSequence::AiCombat *combat) { mTargetActorId = combat->mTargetActorId; } void AiCombat::init() { } /* * Current AiCombat movement states (as of 0.29.0), ignoring the details of the * attack states such as CombatMove, Strike and ReadyToAttack: * * +----(within strike range)----->attack--(beyond strike range)-->follow * | | ^ | | * | | | | | * pursue<---(beyond follow range)-----+ +----(within strike range)---+ | * ^ | * | | * +-------------------------(beyond follow range)--------------------+ * * * Below diagram is high level only, the code detail is a little different * (but including those detail will just complicate the diagram w/o adding much) * * +----------(same)-------------->attack---------(same)---------->follow * | |^^ ||| * | ||| ||| * | +--(same)-----------------+|+----------(same)------------+|| * | | | || * | | | (in range) || * | <---+ (too far) | || * pursue<-------------------------[door open]<-----+ || * ^^^ | || * ||| | || * ||+----------evade-----+ | || * || | [closed door] | || * |+----> maybe stuck, check --------------> back up, check door || * | ^ | ^ | ^ || * | | | | | | || * | | +---+ +---+ || * | +-------------------------------------------------------+| * | | * +---------------------------(same)---------------------------------+ * * FIXME: * * The new scheme is way too complicated, should really be implemented as a * proper state machine. * * TODO: * * Use the Observer Pattern to co-ordinate attacks, provide intelligence on * whether the target was hit, etc. */ bool AiCombat::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { // get or create temporary storage AiCombatStorage& storage = state.get(); //General description if (actor.getClass().getCreatureStats(actor).isDead()) return true; MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); if (target.isEmpty()) return false; if(!target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should be checking whether the target is currently registered // with the MechanicsManager || target.getClass().getCreatureStats(target).isDead()) return true; //Update every frame storage.updateCombatMove(duration); updateActorsMovement(actor, duration, storage.mMovement); storage.updateAttack(characterController); storage.mActionCooldown -= duration; float& timerReact = storage.mTimerReact; if(timerReact < REACTION_INTERVAL) { timerReact += duration; return false; } else { timerReact = 0; return reactionTimeActions(actor, characterController, storage, target); } } bool AiCombat::reactionTimeActions(const MWWorld::Ptr& actor, CharacterController& characterController, AiCombatStorage& storage, MWWorld::Ptr target) { MWMechanics::Movement& movement = storage.mMovement; if (isTargetMagicallyHidden(target)) { storage.stopAttack(); return false; // TODO: run away instead of doing nothing } const MWWorld::CellStore*& currentCell = storage.mCell; bool cellChange = currentCell && (actor.getCell() != currentCell); if(!currentCell || cellChange) { currentCell = actor.getCell(); } const MWWorld::Class& actorClass = actor.getClass(); actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); float& actionCooldown = storage.mActionCooldown; if (actionCooldown > 0) return false; float rangeAttack = 0; float rangeFollow = 0; boost::shared_ptr& currentAction = storage.mCurrentAction; if (characterController.readyToPrepareAttack()) { currentAction = prepareNextAction(actor, target); actionCooldown = currentAction->getActionCooldown(); } if (currentAction.get()) currentAction->getCombatRange(rangeAttack, rangeFollow); // FIXME: consider moving this stuff to ActionWeapon::getCombatRange const ESM::Weapon *weapon = NULL; MWMechanics::WeaponType weaptype = WeapType_None; float weapRange = 1.0f; // Get weapon characteristics MWBase::World* world = MWBase::Environment::get().getWorld(); if (actorClass.hasInventoryStore(actor)) { //Get weapon range MWWorld::ContainerStoreIterator weaponSlot = MWMechanics::getActiveWeapon(actorClass.getCreatureStats(actor), actorClass.getInventoryStore(actor), &weaptype); if (weaptype == WeapType_HandToHand) { static float fHandToHandReach = world->getStore().get().find("fHandToHandReach")->getFloat(); weapRange = fHandToHandReach; } else if (weaptype != WeapType_PickProbe && weaptype != WeapType_Spell && weaptype != WeapType_None) { // All other WeapTypes are actually weapons, so get is safe. weapon = weaponSlot->get()->mBase; weapRange = weapon->mData.mReach; } weapRange *= 100.0f; } else //is creature { weaptype = actorClass.getCreatureStats(actor).getDrawState() == DrawState_Spell ? WeapType_Spell : WeapType_HandToHand; weapRange = 150.0f; //TODO: use true attack range (the same problem in Creature::hit) } bool distantCombat = false; if (weaptype != WeapType_Spell) { // TODO: move to ActionWeapon if (weaptype == WeapType_BowAndArrow || weaptype == WeapType_Crossbow || weaptype == WeapType_Thrown) { rangeAttack = 1000; rangeFollow = 0; // not needed in ranged combat distantCombat = true; } else { rangeAttack = weapRange; rangeFollow = 300; } } else { distantCombat = (rangeAttack > 500); } bool& readyToAttack = storage.mReadyToAttack; // start new attack storage.startAttackIfReady(actor, characterController, weapon, distantCombat); /* * Some notes on meanings of variables: * * rangeAttack: * * - Distance where attack using the actor's weapon is possible: * longer for ranged weapons (obviously?) vs. melee weapons * - Determined by weapon's reach parameter; hardcoded value * for ranged weapon and for creatures * - Once within this distance mFollowTarget is triggered * * rangeFollow: * * - Applies to melee weapons or hand to hand only (or creatures without * weapons) * - Distance a little further away than the actor's weapon reach * i.e. rangeFollow > rangeAttack for melee weapons * - Hardcoded value (0 for ranged weapons) * - Once the target gets beyond this distance mFollowTarget is cleared * and a path to the target needs to be found * * mFollowTarget: * * - Once triggered, the actor follows the target with LOS shortcut * (the shortcut really only applies to cells where pathgrids are * available, since the default path without pathgrids is direct to * target even if LOS is not achieved) */ ESM::Position pos = actor.getRefData().getPosition(); osg::Vec3f vActorPos(pos.asVec3()); osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3()); osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target); osg::Vec3f& lastActorPos = storage.mLastActorPos; bool& followTarget = storage.mFollowTarget; bool isStuck = false; float speed = 0.0f; if(movement.mPosition[1] && (lastActorPos - vActorPos).length() < (speed = actorClass.getSpeed(actor)) * REACTION_INTERVAL / 2) isStuck = true; lastActorPos = vActorPos; // check if actor can move along z-axis bool canMoveByZ = (actorClass.canSwim(actor) && world->isSwimming(actor)) || world->isFlying(actor); // can't fight if attacker can't go where target is. E.g. A fish can't attack person on land. if (distToTarget >= rangeAttack && !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target)) { // TODO: start fleeing? storage.stopAttack(); return false; } // for distant combat we should know if target is in LOS even if distToTarget < rangeAttack bool inLOS = distantCombat ? world->getLOS(actor, target) : true; // (within attack dist) || (not quite attack dist while following) if(inLOS && (distToTarget < rangeAttack || (distToTarget <= rangeFollow && followTarget && !isStuck))) { mPathFinder.clearPath(); //Melee and Close-up combat // getXAngleToDir determines vertical angle to target: // if actor can move along z-axis it will control movement dir // if can't - it will control correct aiming. // note: in getZAngleToDir if we preserve dir.z then horizontal angle can be inaccurate if (distantCombat) { osg::Vec3f& lastTargetPos = storage.mLastTargetPos; vAimDir = AimDirToMovingTarget(actor, target, lastTargetPos, REACTION_INTERVAL, weaptype, storage.mStrength); lastTargetPos = vTargetPos; movement.mRotation[0] = getXAngleToDir(vAimDir); movement.mRotation[2] = getZAngleToDir(vAimDir); } else { movement.mRotation[0] = getXAngleToDir(vAimDir); movement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated } // (not quite attack dist while following) if (followTarget && distToTarget > rangeAttack) { //Close-up combat: just run up on target storage.stopCombatMove(); movement.mPosition[1] = 1; } else // (within attack dist) { storage.startCombatMove(actorClass.isNpc(), distantCombat, distToTarget, rangeAttack); readyToAttack = true; //only once got in melee combat, actor is allowed to use close-up shortcutting followTarget = true; } } else // remote pathfinding { bool preferShortcut = false; if (!distantCombat) inLOS = world->getLOS(actor, target); // check if shortcut is available bool& forceNoShortcut = storage.mForceNoShortcut; ESM::Position& shortcutFailPos = storage.mShortcutFailPos; if(inLOS && (!isStuck || readyToAttack) && (!forceNoShortcut || (shortcutFailPos.asVec3() - vActorPos).length() >= PATHFIND_SHORTCUT_RETRY_DIST)) { if(speed == 0.0f) speed = actorClass.getSpeed(actor); // maximum dist before pit/obstacle for actor to avoid them depending on his speed float maxAvoidDist = REACTION_INTERVAL * speed + speed / MAX_VEL_ANGULAR_RADIANS * 2; // *2 - for reliability preferShortcut = checkWayIsClear(vActorPos, vTargetPos, osg::Vec3f(vAimDir.x(), vAimDir.y(), 0).length() > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2); } // don't use pathgrid when actor can move in 3 dimensions if (canMoveByZ) { preferShortcut = true; movement.mRotation[0] = getXAngleToDir(vAimDir); } if(preferShortcut) { movement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); forceNoShortcut = false; shortcutFailPos.pos[0] = shortcutFailPos.pos[1] = shortcutFailPos.pos[2] = 0; mPathFinder.clearPath(); } else // if shortcut failed stick to path grid { if(!isStuck && shortcutFailPos.pos[0] == 0.0f && shortcutFailPos.pos[1] == 0.0f && shortcutFailPos.pos[2] == 0.0f) { forceNoShortcut = true; shortcutFailPos = pos; } followTarget = false; buildNewPath(actor, target); // should always return a path (even if it's just go straight on target.) assert(mPathFinder.isPathConstructed()); } if (readyToAttack) { // to stop possible sideway moving after target moved out of attack range storage.stopCombatMove(); readyToAttack = false; } movement.mPosition[1] = 1; } return false; } void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, MWMechanics::Movement& desiredMovement) { MWMechanics::Movement& actorMovementSettings = actor.getClass().getMovementSettings(actor); if (mPathFinder.isPathConstructed()) { const ESM::Position& pos = actor.getRefData().getPosition(); if (mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1])) { actorMovementSettings.mPosition[1] = 0; } else { evadeObstacles(actor, duration, pos); } } else { actorMovementSettings = desiredMovement; rotateActorOnAxis(actor, 2, actorMovementSettings, desiredMovement); rotateActorOnAxis(actor, 0, actorMovementSettings, desiredMovement); } } void AiCombat::rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, MWMechanics::Movement& actorMovementSettings, MWMechanics::Movement& desiredMovement) { actorMovementSettings.mRotation[axis] = 0; float& targetAngleRadians = desiredMovement.mRotation[axis]; if (targetAngleRadians != 0) { if (smoothTurn(actor, targetAngleRadians, axis)) { // actor now facing desired direction, no need to turn any more targetAngleRadians = 0; } } } bool AiCombat::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell) { if (!mPathFinder.getPath().empty()) { osg::Vec3f currPathTarget(PathFinder::MakeOsgVec3(mPathFinder.getPath().back())); osg::Vec3f newPathTarget = PathFinder::MakeOsgVec3(dest); float dist = (newPathTarget - currPathTarget).length(); float targetPosThreshold = (cell->isExterior()) ? 300.0f : 100.0f; return dist > targetPosThreshold; } else { // necessarily construct a new path return true; } } void AiCombat::buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { ESM::Pathgrid::Point newPathTarget = PathFinder::MakePathgridPoint(target.getRefData().getPosition()); //construct new path only if target has moved away more than on [targetPosThreshold] if (doesPathNeedRecalc(newPathTarget, actor.getCell()->getCell())) { ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actor.getRefData().getPosition())); mPathFinder.buildSyncedPath(start, newPathTarget, actor.getCell(), false); } } int AiCombat::getTypeId() const { return TypeIdCombat; } unsigned int AiCombat::getPriority() const { return 1; } MWWorld::Ptr AiCombat::getTarget() const { return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); } AiCombat *MWMechanics::AiCombat::clone() const { return new AiCombat(*this); } void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr combat(new ESM::AiSequence::AiCombat()); combat->mTargetActorId = mTargetActorId; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Combat; package.mPackage = combat.release(); sequence.mPackages.push_back(package); } void AiCombatStorage::startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack) { if (mMovement.mPosition[0] || mMovement.mPosition[1]) { mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); mCombatMove = true; } // only NPCs are smart enough to use dodge movements else if (isNpc && (!isDistantCombat || (distToTarget < rangeAttack / 2))) { //apply sideway movement (kind of dodging) with some probability if (Misc::Rng::rollClosedProbability() < 0.25) { mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; mTimerCombatMove = 0.05f + 0.15f * Misc::Rng::rollClosedProbability(); mCombatMove = true; } } if (isDistantCombat && distToTarget < rangeAttack / 4) { mMovement.mPosition[1] = -1; } } void AiCombatStorage::updateCombatMove(float duration) { if (mCombatMove) { mTimerCombatMove -= duration; if (mTimerCombatMove <= 0) { stopCombatMove(); } } } void AiCombatStorage::stopCombatMove() { mTimerCombatMove = 0; mMovement.mPosition[1] = mMovement.mPosition[0] = 0; mCombatMove = false; } void AiCombatStorage::startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, const ESM::Weapon* weapon, bool distantCombat) { if (mReadyToAttack && characterController.readyToStartAttack()) { if (mAttackCooldown <= 0) { mAttack = true; // attack starts just now characterController.setAttackingOrSpell(true); if (!distantCombat) chooseBestAttack(weapon, mMovement); mStrength = Misc::Rng::rollClosedProbability(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); float baseDelay = store.get().find("fCombatDelayCreature")->getFloat(); if (actor.getClass().isNpc()) { baseDelay = store.get().find("fCombatDelayNPC")->getFloat(); //say a provoking combat phrase int chance = store.get().find("iVoiceAttackOdds")->getInt(); if (Misc::Rng::roll0to99() < chance) { MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); } } mAttackCooldown = std::min(baseDelay + 0.01 * Misc::Rng::roll0to99(), baseDelay + 0.9); } else mAttackCooldown -= REACTION_INTERVAL; } } void AiCombatStorage::updateAttack(CharacterController& characterController) { if (mAttack && (characterController.getAttackStrength() >= mStrength || characterController.readyToPrepareAttack())) { mAttack = false; } characterController.setAttackingOrSpell(mAttack); } void AiCombatStorage::stopAttack() { mMovement.mPosition[0] = 0; mMovement.mPosition[1] = 0; mMovement.mPosition[2] = 0; mReadyToAttack = false; mAttack = false; } } namespace { ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { ESM::Weapon::AttackType attackType; if (weapon == NULL) { //hand-to-hand deal equal damage for each type float roll = Misc::Rng::rollClosedProbability(); if(roll <= 0.333f) //side punch { movement.mPosition[0] = Misc::Rng::rollClosedProbability() ? 1.0f : -1.0f; movement.mPosition[1] = 0; attackType = ESM::Weapon::AT_Slash; } else if(roll <= 0.666f) //forward punch { movement.mPosition[1] = 1; attackType = ESM::Weapon::AT_Thrust; } else { movement.mPosition[1] = movement.mPosition[0] = 0; attackType = ESM::Weapon::AT_Chop; } } else { //the more damage attackType deals the more probability it has int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; float roll = Misc::Rng::rollClosedProbability() * (slash + chop + thrust); if(roll <= slash) { movement.mPosition[0] = (Misc::Rng::rollClosedProbability() < 0.5f) ? 1.0f : -1.0f; movement.mPosition[1] = 0; attackType = ESM::Weapon::AT_Slash; } else if(roll <= (slash + thrust)) { movement.mPosition[1] = 1; attackType = ESM::Weapon::AT_Thrust; } else { movement.mPosition[1] = movement.mPosition[0] = 0; attackType = ESM::Weapon::AT_Chop; } } return attackType; } osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const osg::Vec3f& vLastTargetPos, float duration, int weapType, float strength) { float projSpeed; // get projectile speed (depending on weapon type) if (weapType == ESM::Weapon::MarksmanThrown) { static float fThrownWeaponMinSpeed = MWBase::Environment::get().getWorld()->getStore().get().find("fThrownWeaponMinSpeed")->getFloat(); static float fThrownWeaponMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get().find("fThrownWeaponMaxSpeed")->getFloat(); projSpeed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength; } else { static float fProjectileMinSpeed = MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMinSpeed")->getFloat(); static float fProjectileMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMaxSpeed")->getFloat(); projSpeed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength; } // idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same osg::Vec3f vTargetPos = target.getRefData().getPosition().asVec3(); osg::Vec3f vDirToTarget = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); float distToTarget = vDirToTarget.length(); osg::Vec3f vTargetMoveDir = vTargetPos - vLastTargetPos; vTargetMoveDir /= duration; // |vTargetMoveDir| is target real speed in units/sec now osg::Vec3f vPerpToDir = vDirToTarget ^ osg::Vec3f(0,0,1); // cross product vPerpToDir.normalize(); osg::Vec3f vDirToTargetNormalized = vDirToTarget; vDirToTargetNormalized.normalize(); // dot product float velPerp = vTargetMoveDir * vPerpToDir; float velDir = vTargetMoveDir * vDirToTargetNormalized; // time to collision between target and projectile float t_collision; float projVelDirSquared = projSpeed * projSpeed - velPerp * velPerp; osg::Vec3f vTargetMoveDirNormalized = vTargetMoveDir; vTargetMoveDirNormalized.normalize(); float projDistDiff = vDirToTarget * vTargetMoveDirNormalized; // dot product projDistDiff = std::sqrt(distToTarget * distToTarget - projDistDiff * projDistDiff); if (projVelDirSquared > 0) t_collision = projDistDiff / (std::sqrt(projVelDirSquared) - velDir); else t_collision = 0; // speed of projectile is not enough to reach moving target return vDirToTarget + vTargetMoveDir * t_collision; } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/aicombat.hpp000066400000000000000000000037611264522266000234640ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AICOMBAT_H #define GAME_MWMECHANICS_AICOMBAT_H #include "aipackage.hpp" #include "pathfinding.hpp" #include "movement.hpp" #include "obstacle.hpp" #include "../mwworld/cellstore.hpp" // for Doors #include "../mwbase/world.hpp" #include namespace ESM { namespace AiSequence { struct AiCombat; } } namespace MWMechanics { class Action; struct AiCombatStorage; /// \brief Causes the actor to fight another actor class AiCombat : public AiPackage { public: ///Constructor /** \param actor Actor to fight **/ AiCombat(const MWWorld::Ptr& actor); AiCombat (const ESM::AiSequence::AiCombat* combat); void init(); virtual AiCombat *clone() const; virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); virtual int getTypeId() const; virtual unsigned int getPriority() const; ///Returns target ID MWWorld::Ptr getTarget() const; virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; protected: virtual bool doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell); private: int mTargetActorId; void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); bool reactionTimeActions(const MWWorld::Ptr& actor, CharacterController& characterController, AiCombatStorage& storage, MWWorld::Ptr target); /// Transfer desired movement (from AiCombatStorage) to Actor void updateActorsMovement(const MWWorld::Ptr& actor, float duration, MWMechanics::Movement& movement); void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, MWMechanics::Movement& actorMovementSettings, MWMechanics::Movement& desiredMovement); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aicombataction.cpp000066400000000000000000000515641264522266000246610ustar00rootroot00000000000000#include "aicombataction.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/spellcasting.hpp" #include #include namespace { // RangeTypes using bitflags to allow multiple range types, as can be the case with spells having multiple effects. enum RangeTypes { Self = 0x1, Touch = 0x10, Target = 0x100 }; int getRangeTypes (const ESM::EffectList& effects) { int types = 0; for (std::vector::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it) { if (it->mRange == ESM::RT_Self) types |= Self; else if (it->mRange == ESM::RT_Touch) types |= Touch; else if (it->mRange == ESM::RT_Target) types |= Target; } return types; } void suggestCombatRange(int rangeTypes, float& rangeAttack, float& rangeFollow) { if (rangeTypes & Touch) { rangeAttack = 100.f; rangeFollow = 300.f; } else if (rangeTypes & Target) { rangeAttack = 1000.f; rangeFollow = 0.f; } else { // For Self spells, distance doesn't matter, so back away slightly to avoid enemy hits rangeAttack = 600.f; rangeFollow = 0.f; } } int numEffectsToCure (const MWWorld::Ptr& actor, int effectFilter=-1) { int toCure=0; const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells(); for (MWMechanics::ActiveSpells::TIterator it = activeSpells.begin(); it != activeSpells.end(); ++it) { const MWMechanics::ActiveSpells::ActiveSpellParams& params = it->second; for (std::vector::const_iterator effectIt = params.mEffects.begin(); effectIt != params.mEffects.end(); ++effectIt) { int effectId = effectIt->mEffectId; if (effectFilter != -1 && effectId != effectFilter) continue; const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectId); if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && effectIt->mDuration > 3 // Don't attempt to cure if effect runs out shortly anyway ) ++toCure; } } return toCure; } } namespace MWMechanics { float ratePotion (const MWWorld::Ptr &item, const MWWorld::Ptr& actor) { if (item.getTypeName() != typeid(ESM::Potion).name()) return 0.f; const ESM::Potion* potion = item.get()->mBase; return rateEffects(potion->mEffects, actor, MWWorld::Ptr()); } float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& target, int type, float arrowRating, float boltRating) { if (item.getTypeName() != typeid(ESM::Weapon).name()) return 0.f; const ESM::Weapon* weapon = item.get()->mBase; if (type != -1 && weapon->mData.mType != type) return 0.f; float rating=0.f; if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) { rating = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f; } else { for (int i=0; i<2; ++i) { rating += weapon->mData.mSlash[i]; rating += weapon->mData.mThrust[i]; rating += weapon->mData.mChop[i]; } rating /= 6.f; } if (item.getClass().hasItemHealth(item)) { if (item.getClass().getItemHealth(item) == 0) return 0.f; rating *= item.getClass().getItemHealth(item) / float(item.getClass().getItemMaxHealth(item)); } if (weapon->mData.mType == ESM::Weapon::MarksmanBow) { if (arrowRating <= 0.f) rating = 0.f; else rating += arrowRating; } else if (weapon->mData.mType == ESM::Weapon::MarksmanCrossbow) { if (boltRating <= 0.f) rating = 0.f; else rating += boltRating; } if (!weapon->mEnchant.empty()) { const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(weapon->mEnchant); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes && (item.getCellRef().getEnchantmentCharge() == -1 || item.getCellRef().getEnchantmentCharge() >= enchantment->mData.mCost)) rating += rateEffects(enchantment->mEffects, actor, target); } int skill = item.getClass().getEquipmentSkill(item); if (skill != -1) rating *= actor.getClass().getSkill(actor, skill) / 100.f; return rating; } float rateSpell(const ESM::Spell *spell, const MWWorld::Ptr &actor, const MWWorld::Ptr& target) { const CreatureStats& stats = actor.getClass().getCreatureStats(actor); if (MWMechanics::getSpellSuccessChance(spell, actor) == 0) return 0.f; if (spell->mData.mType != ESM::Spell::ST_Spell) return 0.f; // Don't make use of racial bonus spells, like MW. Can be made optional later if (actor.getClass().isNpc()) { std::string raceid = actor.get()->mBase->mRace; const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(raceid); if (race->mPowers.exists(spell->mId)) return 0.f; } if (spell->mData.mCost > stats.getMagicka().getCurrent()) return 0.f; // Spells don't stack, so early out if the spell is still active on the target int types = getRangeTypes(spell->mEffects); if ((types & Self) && stats.getActiveSpells().isSpellActive(spell->mId)) return 0.f; if ( ((types & Touch) || (types & Target)) && target.getClass().getCreatureStats(target).getActiveSpells().isSpellActive(spell->mId)) return 0.f; return rateEffects(spell->mEffects, actor, target); } float rateMagicItem(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor, const MWWorld::Ptr& target) { if (ptr.getClass().getEnchantment(ptr).empty()) return 0.f; const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(ptr.getClass().getEnchantment(ptr)); if (enchantment->mData.mType == ESM::Enchantment::CastOnce) { return rateEffects(enchantment->mEffects, actor, target); } else { //if (!ptr.getClass().canBeEquipped(ptr, actor)) return 0.f; } } float rateEffect(const ESM::ENAMstruct &effect, const MWWorld::Ptr &actor, const MWWorld::Ptr &target) { // NOTE: target may be empty float rating = 1; switch (effect.mEffectID) { case ESM::MagicEffect::Soultrap: case ESM::MagicEffect::AlmsiviIntervention: case ESM::MagicEffect::DivineIntervention: case ESM::MagicEffect::CalmHumanoid: case ESM::MagicEffect::CalmCreature: case ESM::MagicEffect::FrenzyHumanoid: case ESM::MagicEffect::FrenzyCreature: case ESM::MagicEffect::DemoralizeHumanoid: case ESM::MagicEffect::DemoralizeCreature: case ESM::MagicEffect::RallyHumanoid: case ESM::MagicEffect::RallyCreature: case ESM::MagicEffect::Charm: case ESM::MagicEffect::DetectAnimal: case ESM::MagicEffect::DetectEnchantment: case ESM::MagicEffect::DetectKey: case ESM::MagicEffect::Telekinesis: case ESM::MagicEffect::Mark: case ESM::MagicEffect::Recall: case ESM::MagicEffect::Jump: case ESM::MagicEffect::WaterBreathing: case ESM::MagicEffect::SwiftSwim: case ESM::MagicEffect::WaterWalking: case ESM::MagicEffect::SlowFall: case ESM::MagicEffect::Light: case ESM::MagicEffect::Lock: case ESM::MagicEffect::Open: case ESM::MagicEffect::TurnUndead: case ESM::MagicEffect::WeaknessToCommonDisease: case ESM::MagicEffect::WeaknessToBlightDisease: case ESM::MagicEffect::WeaknessToCorprusDisease: case ESM::MagicEffect::CureCommonDisease: case ESM::MagicEffect::CureBlightDisease: case ESM::MagicEffect::CureCorprusDisease: case ESM::MagicEffect::Invisibility: return 0.f; case ESM::MagicEffect::Feather: if (actor.getClass().getEncumbrance(actor) - actor.getClass().getCapacity(actor) >= 0) return 100.f; else return 0.f; case ESM::MagicEffect::Levitate: return 0.f; // AI isn't designed to take advantage of this, and could be perceived as unfair anyway case ESM::MagicEffect::BoundBoots: case ESM::MagicEffect::BoundHelm: if (actor.getClass().isNpc()) { // Beast races can't wear helmets or boots std::string raceid = actor.get()->mBase->mRace; const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(raceid); if (race->mData.mFlags & ESM::Race::Beast) return 0.f; } // Intended fall-through // Creatures can not wear armor case ESM::MagicEffect::BoundCuirass: case ESM::MagicEffect::BoundGloves: if (!actor.getClass().isNpc()) return 0.f; break; case ESM::MagicEffect::RestoreHealth: case ESM::MagicEffect::RestoreMagicka: case ESM::MagicEffect::RestoreFatigue: if (effect.mRange == ESM::RT_Self) { int priority = 1; if (effect.mEffectID == ESM::MagicEffect::RestoreHealth) priority = 10; const DynamicStat& current = actor.getClass().getCreatureStats(actor). getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); float toHeal = (effect.mMagnMin + effect.mMagnMax)/2.f * effect.mDuration; // Effect doesn't heal more than we need, *or* we are below 1/2 health if (current.getModified() - current.getCurrent() > toHeal || current.getCurrent() < current.getModified()*0.5) { return 10000.f * priority - (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion } else return -10000.f * priority; // Save for later } break; // Prefer Cure effects over Dispel, because Dispel also removes positive effects case ESM::MagicEffect::Dispel: return 1000.f * numEffectsToCure(actor); case ESM::MagicEffect::CureParalyzation: return 1001.f * numEffectsToCure(actor, ESM::MagicEffect::Paralyze); case ESM::MagicEffect::CurePoison: return 1001.f * numEffectsToCure(actor, ESM::MagicEffect::Poison); case ESM::MagicEffect::DisintegrateArmor: // TODO: check if actor is wearing armor case ESM::MagicEffect::DisintegrateWeapon: // TODO: check if actor is wearing weapon break; case ESM::MagicEffect::DamageAttribute: case ESM::MagicEffect::DrainAttribute: if (!target.isEmpty() && target.getClass().getCreatureStats(target).getAttribute(effect.mAttribute).getModified() <= 0) return 0.f; { if (effect.mAttribute >= 0 && effect.mAttribute < ESM::Attribute::Length) { const float attributePriorities[ESM::Attribute::Length] = { 1.0f, // Strength 0.5f, // Intelligence 0.6f, // Willpower 0.7f, // Agility 0.5f, // Speed 0.8f, // Endurance 0.7f, // Personality 0.3f // Luck }; rating *= attributePriorities[effect.mAttribute]; } } break; case ESM::MagicEffect::DamageSkill: case ESM::MagicEffect::DrainSkill: if (target.isEmpty() || !target.getClass().isNpc()) return 0.f; if (target.getClass().getNpcStats(target).getSkill(effect.mSkill).getModified() <= 0) return 0.f; break; default: break; } // TODO: for non-cumulative effects (e.g. paralyze), check if the target is already suffering from them // TODO: could take into account target's resistance/weakness against the effect const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); rating *= magicEffect->mData.mBaseCost; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) rating *= (effect.mMagnMin + effect.mMagnMax)/2.f; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) rating *= effect.mDuration; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) rating *= -1.f; // Currently treating all "on target" or "on touch" effects to target the enemy actor. // Combat AI is egoistic, so doesn't consider applying positive effects to friendly actors. if (effect.mRange != ESM::RT_Self) rating *= -1.f; return rating; } float rateEffects(const ESM::EffectList &list, const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { // NOTE: target may be empty float rating = 0.f; for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) { rating += rateEffect(*it, actor, target); } return rating; } void ActionSpell::prepare(const MWWorld::Ptr &actor) { actor.getClass().getCreatureStats(actor).getSpells().setSelectedSpell(mSpellId); actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Spell); if (actor.getClass().hasInventoryStore(actor)) { MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); inv.setSelectedEnchantItem(inv.end()); } } void ActionSpell::getCombatRange(float& rangeAttack, float& rangeFollow) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(mSpellId); int types = getRangeTypes(spell->mEffects); suggestCombatRange(types, rangeAttack, rangeFollow); } void ActionEnchantedItem::prepare(const MWWorld::Ptr &actor) { actor.getClass().getCreatureStats(actor).getSpells().setSelectedSpell(std::string()); actor.getClass().getInventoryStore(actor).setSelectedEnchantItem(mItem); actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Spell); } void ActionEnchantedItem::getCombatRange(float& rangeAttack, float& rangeFollow) { const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(mItem->getClass().getEnchantment(*mItem)); int types = getRangeTypes(enchantment->mEffects); suggestCombatRange(types, rangeAttack, rangeFollow); } void ActionPotion::getCombatRange(float& rangeAttack, float& rangeFollow) { // distance doesn't matter, so back away slightly to avoid enemy hits rangeAttack = 600.f; rangeFollow = 0.f; } void ActionPotion::prepare(const MWWorld::Ptr &actor) { actor.getClass().apply(actor, mPotion.getCellRef().getRefId(), actor); actor.getClass().getContainerStore(actor).remove(mPotion, 1, actor); } void ActionWeapon::prepare(const MWWorld::Ptr &actor) { if (actor.getClass().hasInventoryStore(actor)) { if (mWeapon.isEmpty()) actor.getClass().getInventoryStore(actor).unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, actor); else { MWWorld::ActionEquip equip(mWeapon); equip.execute(actor); } if (!mAmmunition.isEmpty()) { MWWorld::ActionEquip equip(mAmmunition); equip.execute(actor); } } actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Weapon); } void ActionWeapon::getCombatRange(float& rangeAttack, float& rangeFollow) { // Already done in AiCombat itself } boost::shared_ptr prepareNextAction(const MWWorld::Ptr &actor, const MWWorld::Ptr &target) { Spells& spells = actor.getClass().getCreatureStats(actor).getSpells(); float bestActionRating = 0.f; // Default to hand-to-hand combat boost::shared_ptr bestAction (new ActionWeapon(MWWorld::Ptr())); if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) { bestAction->prepare(actor); return bestAction; } if (actor.getClass().hasInventoryStore(actor)) { MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { float rating = ratePotion(*it, actor); if (rating > bestActionRating) { bestActionRating = rating; bestAction.reset(new ActionPotion(*it)); } } for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { float rating = rateMagicItem(*it, actor, target); if (rating > bestActionRating) { bestActionRating = rating; bestAction.reset(new ActionEnchantedItem(it)); } } float bestArrowRating = 0; MWWorld::Ptr bestArrow; for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { float rating = rateWeapon(*it, actor, target, ESM::Weapon::Arrow); if (rating > bestArrowRating) { bestArrowRating = rating; bestArrow = *it; } } float bestBoltRating = 0; MWWorld::Ptr bestBolt; for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { float rating = rateWeapon(*it, actor, target, ESM::Weapon::Bolt); if (rating > bestBoltRating) { bestBoltRating = rating; bestBolt = *it; } } for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { std::vector equipmentSlots = it->getClass().getEquipmentSlots(*it).first; if (std::find(equipmentSlots.begin(), equipmentSlots.end(), (int)MWWorld::InventoryStore::Slot_CarriedRight) == equipmentSlots.end()) continue; float rating = rateWeapon(*it, actor, target, -1, bestArrowRating, bestBoltRating); if (rating > bestActionRating) { const ESM::Weapon* weapon = it->get()->mBase; MWWorld::Ptr ammo; if (weapon->mData.mType == ESM::Weapon::MarksmanBow) ammo = bestArrow; else if (weapon->mData.mType == ESM::Weapon::MarksmanCrossbow) ammo = bestBolt; bestActionRating = rating; bestAction.reset(new ActionWeapon(*it, ammo)); } } } for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ESM::Spell* spell = it->first; float rating = rateSpell(spell, actor, target); if (rating > bestActionRating) { bestActionRating = rating; bestAction.reset(new ActionSpell(spell->mId)); } } if (bestAction.get()) bestAction->prepare(actor); return bestAction; } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/aicombataction.hpp000066400000000000000000000065301264522266000246570ustar00rootroot00000000000000#ifndef OPENMW_AICOMBAT_ACTION_H #define OPENMW_AICOMBAT_ACTION_H #include #include "../mwworld/ptr.hpp" #include "../mwworld/containerstore.hpp" #include namespace MWMechanics { class Action { public: virtual ~Action() {} virtual void prepare(const MWWorld::Ptr& actor) = 0; virtual void getCombatRange (float& rangeAttack, float& rangeFollow) = 0; virtual float getActionCooldown() { return 0.f; } }; class ActionSpell : public Action { public: ActionSpell(const std::string& spellId) : mSpellId(spellId) {} std::string mSpellId; /// Sets the given spell as selected on the actor's spell list. virtual void prepare(const MWWorld::Ptr& actor); virtual void getCombatRange (float& rangeAttack, float& rangeFollow); }; class ActionEnchantedItem : public Action { public: ActionEnchantedItem(const MWWorld::ContainerStoreIterator& item) : mItem(item) {} MWWorld::ContainerStoreIterator mItem; /// Sets the given item as selected enchanted item in the actor's InventoryStore. virtual void prepare(const MWWorld::Ptr& actor); virtual void getCombatRange (float& rangeAttack, float& rangeFollow); /// Since this action has no animation, apply a small cool down for using it virtual float getActionCooldown() { return 1.f; } }; class ActionPotion : public Action { public: ActionPotion(const MWWorld::Ptr& potion) : mPotion(potion) {} MWWorld::Ptr mPotion; /// Drinks the given potion. virtual void prepare(const MWWorld::Ptr& actor); virtual void getCombatRange (float& rangeAttack, float& rangeFollow); /// Since this action has no animation, apply a small cool down for using it virtual float getActionCooldown() { return 1.f; } }; class ActionWeapon : public Action { private: MWWorld::Ptr mAmmunition; MWWorld::Ptr mWeapon; public: /// \a weapon may be empty for hand-to-hand combat ActionWeapon(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo = MWWorld::Ptr()) : mAmmunition(ammo), mWeapon(weapon) {} /// Equips the given weapon. virtual void prepare(const MWWorld::Ptr& actor); virtual void getCombatRange (float& rangeAttack, float& rangeFollow); }; float rateSpell (const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); float rateMagicItem (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Ptr &target); float ratePotion (const MWWorld::Ptr& item, const MWWorld::Ptr &actor); /// @param type Skip all weapons that are not of this type (i.e. return rating 0) float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor, const MWWorld::Ptr& target, int type=-1, float arrowRating=0.f, float boltRating=0.f); /// @note target may be empty float rateEffect (const ESM::ENAMstruct& effect, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); /// @note target may be empty float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); boost::shared_ptr prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& target); } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aiescort.cpp000066400000000000000000000131251264522266000235040ustar00rootroot00000000000000#include "aiescort.hpp" #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "steering.hpp" #include "movement.hpp" /* TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0. TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z. TODO: Take account for actors being in different cells. */ namespace MWMechanics { AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) : mActorId(actorId), mX(x), mY(y), mZ(z), mRemainingDuration(static_cast(duration)) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { mMaxDist = 450; // The CS Help File states that if a duration is given, the AI package will run for that long // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) mRemainingDuration = 0; } AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mRemainingDuration(static_cast(duration)) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { mMaxDist = 450; // The CS Help File states that if a duration is given, the AI package will run for that long // BUT if a location is given, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) mRemainingDuration = 0; } AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort) : mActorId(escort->mTargetId), mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ) , mMaxDist(450) , mRemainingDuration(escort->mRemainingDuration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { } AiEscort *MWMechanics::AiEscort::clone() const { return new AiEscort(*this); } bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { // If AiEscort has ran for as long or longer then the duration specified // and the duration is not infinite, the package is complete. if(mRemainingDuration != 0) { mRemainingDuration -= duration; if (duration <= 0) return true; } if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3())) return false; if (!mCellId.empty() && mCellId != actor.getCell()->getCell()->getCellId().mWorldspace) return false; // Not in the correct cell, pause and rely on the player to go back through a teleport door actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); const float* const leaderPos = actor.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos; double differenceBetween[3]; for (short counter = 0; counter < 3; counter++) differenceBetween[counter] = (leaderPos[counter] - followerPos[counter]); double distanceBetweenResult = (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]); if(distanceBetweenResult <= mMaxDist * mMaxDist) { ESM::Pathgrid::Point point(static_cast(mX), static_cast(mY), static_cast(mZ)); point.mAutogenerated = 0; point.mConnectionNum = 0; point.mUnknown = 0; if(pathTo(actor,point,duration)) //Returns true on path complete return true; mMaxDist = 450; } else { // Stop moving if the player is to far away MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); actor.getClass().getMovementSettings(actor).mPosition[1] = 0; mMaxDist = 250; } return false; } int AiEscort::getTypeId() const { return TypeIdEscort; } MWWorld::Ptr AiEscort::getTarget() { return MWBase::Environment::get().getWorld()->getPtr(mActorId, false); } void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr escort(new ESM::AiSequence::AiEscort()); escort->mData.mX = mX; escort->mData.mY = mY; escort->mData.mZ = mZ; escort->mTargetId = mActorId; escort->mRemainingDuration = mRemainingDuration; escort->mCellId = mCellId; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Escort; package.mPackage = escort.release(); sequence.mPackages.push_back(package); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/aiescort.hpp000066400000000000000000000034161264522266000235130ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AIESCORT_H #define GAME_MWMECHANICS_AIESCORT_H #include "aipackage.hpp" #include #include "pathfinding.hpp" namespace ESM { namespace AiSequence { struct AiEscort; } } namespace MWMechanics { /// \brief AI Package to have an NPC lead the player to a specific point class AiEscort : public AiPackage { public: /// Implementation of AiEscort /** The Actor will escort the specified actor to the world position x, y, z until they reach their position, or they run out of time \implement AiEscort **/ AiEscort(const std::string &actorId,int duration, float x, float y, float z); /// Implementation of AiEscortCell /** The Actor will escort the specified actor to the cell position x, y, z until they reach their position, or they run out of time \implement AiEscortCell **/ AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); AiEscort(const ESM::AiSequence::AiEscort* escort); virtual AiEscort *clone() const; virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); virtual int getTypeId() const; MWWorld::Ptr getTarget(); virtual bool sideWithTarget() const { return true; } void writeState(ESM::AiSequence::AiSequence &sequence) const; private: std::string mActorId; std::string mCellId; float mX; float mY; float mZ; float mMaxDist; float mRemainingDuration; // In seconds int mCellX; int mCellY; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aifollow.cpp000066400000000000000000000165641264522266000235210ustar00rootroot00000000000000#include "aifollow.hpp" #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "creaturestats.hpp" #include "movement.hpp" #include "steering.hpp" namespace MWMechanics { struct AiFollowStorage : AiTemporaryBase { float mTimer; bool mMoving; AiFollowStorage() : mTimer(0.f), mMoving(false) {} }; int AiFollow::mFollowIndexCounter = 0; AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mActorRefId(actorId), mActorId(-1), mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { } AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) : mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mActorRefId(actorId), mActorId(-1), mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++) { } AiFollow::AiFollow(const std::string &actorId, bool commanded) : mAlwaysFollow(true), mCommanded(commanded), mRemainingDuration(0), mX(0), mY(0), mZ(0) , mActorRefId(actorId), mActorId(-1), mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { } AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded), mRemainingDuration(follow->mRemainingDuration) , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) , mActorRefId(follow->mTargetId), mActorId(-1) , mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++) { } bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { MWWorld::Ptr target = getTarget(); if (target.isEmpty() || !target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should be checking whether the target is currently registered // with the MechanicsManager ) return false; // Target is not here right now, wait for it to return actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); AiFollowStorage& storage = state.get(); // AiFollow requires the target to be in range and within sight for the initial activation if (!mActive) { storage.mTimer -= duration; if (storage.mTimer < 0) { if ((actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length2() < 500*500 && MWBase::Environment::get().getWorld()->getLOS(actor, target)) mActive = true; storage.mTimer = 0.5f; } } if (!mActive) return false; ESM::Position pos = actor.getRefData().getPosition(); //position of the actor float followDistance = 180; // When there are multiple actors following the same target, they form a group with each group member at 180*(i+1) distance to the target int i=0; std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingIndices(target); followers.sort(); for (std::list::iterator it = followers.begin(); it != followers.end(); ++it) { if (*it == mFollowIndex) followDistance *= (i+1); ++i; } if(!mAlwaysFollow) //Update if you only follow for a bit { //Check if we've run out of time if (mRemainingDuration != 0) { mRemainingDuration -= duration; if (duration <= 0) return true; } if((pos.pos[0]-mX)*(pos.pos[0]-mX) + (pos.pos[1]-mY)*(pos.pos[1]-mY) + (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < followDistance*followDistance) //Close-ish to final position { if(actor.getCell()->isExterior()) //Outside? { if(mCellId == "") //No cell to travel to return true; } else { if(mCellId == actor.getCell()->getCell()->mName) //Cell to travel to return true; } } } //Set the target destination from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; float dist = distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]); if (storage.mMoving) //Stop when you get close storage.mMoving = (dist > followDistance); else { const float threshold = 10; storage.mMoving = (dist > followDistance + threshold); } if(!storage.mMoving) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; // turn towards target anyway float directionX = target.getRefData().getPosition().pos[0] - actor.getRefData().getPosition().pos[0]; float directionY = target.getRefData().getPosition().pos[1] - actor.getRefData().getPosition().pos[1]; zTurn(actor, std::atan2(directionX,directionY), osg::DegreesToRadians(5.f)); } else { pathTo(actor, dest, duration); //Go to the destination } //Check if you're far away if(dist > 450) actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run else if(dist < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk return false; } std::string AiFollow::getFollowedActor() { return mActorRefId; } AiFollow *MWMechanics::AiFollow::clone() const { return new AiFollow(*this); } int AiFollow::getTypeId() const { return TypeIdFollow; } bool AiFollow::isCommanded() const { return mCommanded; } void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr follow(new ESM::AiSequence::AiFollow()); follow->mData.mX = mX; follow->mData.mY = mY; follow->mData.mZ = mZ; follow->mTargetId = mActorRefId; follow->mRemainingDuration = mRemainingDuration; follow->mCellId = mCellId; follow->mAlwaysFollow = mAlwaysFollow; follow->mCommanded = mCommanded; follow->mActive = mActive; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Follow; package.mPackage = follow.release(); sequence.mPackages.push_back(package); } MWWorld::Ptr AiFollow::getTarget() { if (mActorId == -2) return MWWorld::Ptr(); if (mActorId == -1) { MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorRefId, false); if (target.isEmpty()) { mActorId = -2; return target; } else mActorId = target.getClass().getCreatureStats(target).getActorId(); } if (mActorId != -1) return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); else return MWWorld::Ptr(); } int AiFollow::getFollowIndex() const { return mFollowIndex; } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/aifollow.hpp000066400000000000000000000044771264522266000235260ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AIFOLLOW_H #define GAME_MWMECHANICS_AIFOLLOW_H #include "aipackage.hpp" #include #include "pathfinding.hpp" #include namespace ESM { namespace AiSequence { struct AiFollow; } } namespace MWMechanics { /// \brief AiPackage for an actor to follow another actor/the PC /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely **/ class AiFollow : public AiPackage { public: /// Follow Actor for duration or until you arrive at a world position AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); /// Follow Actor for duration or until you arrive at a position in a cell AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z); /// Follow Actor indefinitively AiFollow(const std::string &ActorId, bool commanded=false); AiFollow(const ESM::AiSequence::AiFollow* follow); MWWorld::Ptr getTarget(); virtual bool sideWithTarget() const { return true; } virtual bool followTargetThroughDoors() const { return true; } virtual AiFollow *clone() const; virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); virtual int getTypeId() const; /// Returns the actor being followed std::string getFollowedActor(); virtual void writeState (ESM::AiSequence::AiSequence& sequence) const; bool isCommanded() const; int getFollowIndex() const; private: /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ bool mAlwaysFollow; bool mCommanded; float mRemainingDuration; // Seconds float mX; float mY; float mZ; std::string mActorRefId; int mActorId; std::string mCellId; bool mActive; // have we spotted the target? int mFollowIndex; static int mFollowIndexCounter; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aipackage.cpp000066400000000000000000000125671264522266000236110ustar00rootroot00000000000000#include "aipackage.hpp" #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "creaturestats.hpp" #include "movement.hpp" #include "../mwworld/action.hpp" #include "steering.hpp" #include "actorutil.hpp" #include "coordinateconverter.hpp" MWMechanics::AiPackage::~AiPackage() {} MWWorld::Ptr MWMechanics::AiPackage::getTarget() { return MWWorld::Ptr(); } bool MWMechanics::AiPackage::sideWithTarget() const { return false; } bool MWMechanics::AiPackage::followTargetThroughDoors() const { return false; } MWMechanics::AiPackage::AiPackage() : mTimer(0.26f) { //mTimer starts at .26 to force initial pathbuild } bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration) { //Update various Timers mTimer += duration; //Update timer ESM::Position pos = actor.getRefData().getPosition(); //position of the actor /// Stops the actor when it gets too close to a unloaded cell //... At current time, this test is unnecessary. AI shuts down when actor is more than 7168 //... units from player, and exterior cells are 8192 units long and wide. //... But AI processing distance may increase in the future. if (isNearInactiveCell(pos)) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; return false; } //*********************** /// Checks if you can't get to the end position at all, adds end position to end of path /// Rebuilds path every quarter of a second, in case the target has moved //*********************** if(mTimer > 0.25) { const ESM::Cell *cell = actor.getCell()->getCell(); if (doesPathNeedRecalc(dest, cell)) { //Only rebuild path if it's moved mPathFinder.buildSyncedPath(pos.pos, dest, actor.getCell(), true); //Rebuild path, in case the target has moved mPrevDest = dest; } if(!mPathFinder.getPath().empty()) //Path has points in it { ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path if(distance(dest, lastPos) > 100) //End of the path is far from the destination mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go } mTimer = 0; } //************************ /// Checks if you aren't moving; attempts to unstick you //************************ if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1])) //Path finished? return true; else { evadeObstacles(actor, duration, pos); } return false; } void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float duration, const ESM::Position& pos) { zTurn(actor, mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])); MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor); if (mObstacleCheck.check(actor, duration)) { // first check if we're walking into a door MWWorld::Ptr door = getNearbyDoor(actor); if (door != MWWorld::Ptr()) // NOTE: checks interior cells only { if (!door.getCellRef().getTeleport() && door.getCellRef().getTrap().empty() && door.getCellRef().getLockLevel() <= 0 && door.getClass().getDoorState(door) == 0) { MWBase::Environment::get().getWorld()->activateDoor(door, 1); } } else // probably walking into another NPC { mObstacleCheck.takeEvasiveAction(movement); } } else { //Not stuck, so reset things movement.mPosition[1] = 1; //Just run forward } } bool MWMechanics::AiPackage::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell) { return distance(mPrevDest, dest) > 10; } bool MWMechanics::AiPackage::isTargetMagicallyHidden(const MWWorld::Ptr& target) { const MagicEffects& magicEffects(target.getClass().getCreatureStats(target).getMagicEffects()); return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0) || (magicEffects.get(ESM::MagicEffect::Chameleon).getMagnitude() > 75); } bool MWMechanics::AiPackage::isNearInactiveCell(const ESM::Position& actorPos) { const ESM::Cell* playerCell(getPlayer().getCell()->getCell()); if (playerCell->isExterior()) { // get actor's distance from origin of center cell osg::Vec3f actorOffset(actorPos.asVec3()); CoordinateConverter(playerCell).toLocal(actorOffset); // currently assumes 3 x 3 grid for exterior cells, with player at center cell. // ToDo: (Maybe) use "exterior cell load distance" setting to get count of actual active cells // While AI Process distance is 7168, AI shuts down actors before they reach edges of 3 x 3 grid. const float distanceFromEdge = 200.0; float minThreshold = (-1.0f * ESM::Land::REAL_SIZE) + distanceFromEdge; float maxThreshold = (2.0f * ESM::Land::REAL_SIZE) - distanceFromEdge; return (actorOffset[0] < minThreshold) || (maxThreshold < actorOffset[0]) || (actorOffset[1] < minThreshold) || (maxThreshold < actorOffset[1]); } else { return false; } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/aipackage.hpp000066400000000000000000000073171264522266000236130ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AIPACKAGE_H #define GAME_MWMECHANICS_AIPACKAGE_H #include "pathfinding.hpp" #include #include "obstacle.hpp" #include "aistate.hpp" namespace MWWorld { class Ptr; } namespace ESM { struct Cell; namespace AiSequence { struct AiSequence; } } namespace MWMechanics { class CharacterController; /// \brief Base class for AI packages class AiPackage { public: ///Enumerates the various AITypes availible. enum TypeId { TypeIdNone = -1, TypeIdWander = 0, TypeIdTravel = 1, TypeIdEscort = 2, TypeIdFollow = 3, TypeIdActivate = 4, TypeIdCombat = 5, TypeIdPursue = 6, TypeIdAvoidDoor = 7 }; ///Default constructor AiPackage(); ///Default Deconstructor virtual ~AiPackage(); ///Clones the package virtual AiPackage *clone() const = 0; /// Updates and runs the package (Should run every frame) /// \return Package completed? virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) = 0; /// Returns the TypeID of the AiPackage /// \see enum TypeId virtual int getTypeId() const = 0; /// Higher number is higher priority (0 being the lowest) virtual unsigned int getPriority() const {return 0;} virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {} /// Simulates the passing of time virtual void fastForward(const MWWorld::Ptr& actor, AiState& state) {} /// Get the target actor the AI is targeted at (not applicable to all AI packages, default return empty Ptr) virtual MWWorld::Ptr getTarget(); /// Return true if having this AiPackage makes the actor side with the target in fights (default false) virtual bool sideWithTarget() const; /// Return true if the actor should follow the target through teleport doors (default false) virtual bool followTargetThroughDoors() const; bool isTargetMagicallyHidden(const MWWorld::Ptr& target); protected: /// Causes the actor to attempt to walk to the specified location /** \return If the actor has arrived at his destination **/ bool pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration); virtual bool doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell); void evadeObstacles(const MWWorld::Ptr& actor, float duration, const ESM::Position& pos); // TODO: all this does not belong here, move into temporary storage PathFinder mPathFinder; ObstacleCheck mObstacleCheck; float mTimer; ESM::Pathgrid::Point mPrevDest; bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) const { // Maximum travel distance for vanilla compatibility. // Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well. // We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways. return (pos1 - pos2).length2() <= 7168*7168; } private: bool isNearInactiveCell(const ESM::Position& actorPos); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aipursue.cpp000066400000000000000000000055241264522266000235340ustar00rootroot00000000000000#include "aipursue.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "../mwworld/action.hpp" #include "../mwmechanics/creaturestats.hpp" #include "movement.hpp" #include "creaturestats.hpp" namespace MWMechanics { AiPursue::AiPursue(const MWWorld::Ptr& actor) : mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()) { } AiPursue::AiPursue(const ESM::AiSequence::AiPursue *pursue) : mTargetActorId(pursue->mTargetActorId) { } AiPursue *MWMechanics::AiPursue::clone() const { return new AiPursue(*this); } bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { if(actor.getClass().getCreatureStats(actor).isDead()) return true; ESM::Position pos = actor.getRefData().getPosition(); //position of the actor const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); //The target to follow if(target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should be checking whether the target is currently registered // with the MechanicsManager ) return true; //Target doesn't exist if (isTargetMagicallyHidden(target)) return true; if(target.getClass().getCreatureStats(target).isDead()) return true; actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); //Set the target desition from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close actor.getClass().getMovementSettings(actor).mPosition[1] = 0; target.getClass().activate(target,actor).get()->execute(actor); //Arrest player return true; } else { pathTo(actor, dest, duration); //Go to the destination } actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run return false; } int AiPursue::getTypeId() const { return TypeIdPursue; } MWWorld::Ptr AiPursue::getTarget() const { return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); } void AiPursue::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr pursue(new ESM::AiSequence::AiPursue()); pursue->mTargetActorId = mTargetActorId; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Pursue; package.mPackage = pursue.release(); sequence.mPackages.push_back(package); } } // namespace MWMechanics openmw-openmw-0.38.0/apps/openmw/mwmechanics/aipursue.hpp000066400000000000000000000023651264522266000235410ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AIPURSUE_H #define GAME_MWMECHANICS_AIPURSUE_H #include "aipackage.hpp" #include "../mwbase/world.hpp" #include "pathfinding.hpp" namespace ESM { namespace AiSequence { struct AiPursue; } } namespace MWMechanics { /// \brief Makes the actor very closely follow the actor /** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them. Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the path is completed). **/ class AiPursue : public AiPackage { public: ///Constructor /** \param actor Actor to pursue **/ AiPursue(const MWWorld::Ptr& actor); AiPursue(const ESM::AiSequence::AiPursue* pursue); virtual AiPursue *clone() const; virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); virtual int getTypeId() const; MWWorld::Ptr getTarget() const; virtual void writeState (ESM::AiSequence::AiSequence& sequence) const; private: int mTargetActorId; // The actor to pursue }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aisequence.cpp000066400000000000000000000311271264522266000240170ustar00rootroot00000000000000#include "aisequence.hpp" #include #include "aipackage.hpp" #include "aistate.hpp" #include "aiwander.hpp" #include "aiescort.hpp" #include "aitravel.hpp" #include "aifollow.hpp" #include "aiactivate.hpp" #include "aicombat.hpp" #include "aipursue.hpp" #include "actorutil.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" namespace MWMechanics { void AiSequence::copy (const AiSequence& sequence) { for (std::list::const_iterator iter (sequence.mPackages.begin()); iter!=sequence.mPackages.end(); ++iter) mPackages.push_back ((*iter)->clone()); } AiSequence::AiSequence() : mDone (false), mLastAiPackage(-1) {} AiSequence::AiSequence (const AiSequence& sequence) { copy (sequence); mDone = sequence.mDone; mLastAiPackage = sequence.mLastAiPackage; } AiSequence& AiSequence::operator= (const AiSequence& sequence) { if (this!=&sequence) { clear(); copy (sequence); mDone = sequence.mDone; mLastAiPackage = sequence.mLastAiPackage; } return *this; } AiSequence::~AiSequence() { clear(); } int AiSequence::getTypeId() const { if (mPackages.empty()) return -1; return mPackages.front()->getTypeId(); } bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const { if (getTypeId() != AiPackage::TypeIdCombat) return false; const AiCombat *combat = static_cast(mPackages.front()); targetActor = combat->getTarget(); return !targetActor.isEmpty(); } std::list::const_iterator AiSequence::begin() const { return mPackages.begin(); } std::list::const_iterator AiSequence::end() const { return mPackages.end(); } std::list::const_iterator AiSequence::erase(std::list::const_iterator package) { // Not sure if manually terminated packages should trigger mDone, probably not? for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) { if (package == it) { return mPackages.erase(it); } } throw std::runtime_error("can't find package to erase"); } bool AiSequence::isInCombat() const { for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) return true; } return false; } bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const { for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { const AiCombat *combat = static_cast(*it); if (combat->getTarget() == actor) return true; } } return false; } void AiSequence::stopCombat() { for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) it = mPackages.erase(it); else ++it; } } void AiSequence::stopPursuit() { for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ) { if ((*it)->getTypeId() == AiPackage::TypeIdPursue) it = mPackages.erase(it); else ++it; } } bool AiSequence::isPackageDone() const { return mDone; } void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { if(actor != getPlayer()) { if (!mPackages.empty()) { MWMechanics::AiPackage* package = mPackages.front(); mLastAiPackage = package->getTypeId(); // if active package is combat one, choose nearest target if (mLastAiPackage == AiPackage::TypeIdCombat) { std::list::iterator itActualCombat; float nearestDist = std::numeric_limits::max(); osg::Vec3f vActorPos = actor.getRefData().getPosition().asVec3(); for(std::list::iterator it = mPackages.begin(); it != mPackages.end();) { if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; MWWorld::Ptr target = static_cast(*it)->getTarget(); // target disappeared (e.g. summoned creatures) if (target.isEmpty()) { delete *it; it = mPackages.erase(it); } else { const ESM::Position &targetPos = target.getRefData().getPosition(); float distTo = (targetPos.asVec3() - vActorPos).length(); // Small threshold for changing target if (it == mPackages.begin()) distTo = std::max(0.f, distTo - 50.f); if (distTo < nearestDist) { nearestDist = distTo; itActualCombat = it; } ++it; } } if (!mPackages.empty()) { if (nearestDist < std::numeric_limits::max() && mPackages.begin() != itActualCombat) { // move combat package with nearest target to the front mPackages.splice(mPackages.begin(), mPackages, itActualCombat); } package = mPackages.front(); mLastAiPackage = package->getTypeId(); } else { mDone = true; return; } } if (package->execute (actor,characterController,state,duration)) { // To account for the rare case where AiPackage::execute() queued another AI package // (e.g. AiPursue executing a dialogue script that uses startCombat) std::list::iterator toRemove = std::find(mPackages.begin(), mPackages.end(), package); mPackages.erase(toRemove); delete package; mDone = true; } else { mDone = false; } } } } void AiSequence::clear() { for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) delete *iter; mPackages.clear(); } void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor) { if (actor == getPlayer()) throw std::runtime_error("Can't add AI packages to player"); if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPursue) { // Notify AiWander of our current position so we can return to it after combat finished for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) { if((*iter)->getTypeId() == AiPackage::TypeIdPursue && package.getTypeId() == AiPackage::TypeIdPursue && static_cast(*iter)->getTarget() == static_cast(&package)->getTarget()) { return; // target is already pursued } if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat && static_cast(*iter)->getTarget() == static_cast(&package)->getTarget()) { return; // already in combat with this actor } else if ((*iter)->getTypeId() == AiPackage::TypeIdWander) static_cast(*iter)->setReturnPosition(actor.getRefData().getPosition().asVec3()); } } for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) { if((*it)->getPriority() <= package.getPriority()) { mPackages.insert(it,package.clone()); return; } } mPackages.push_front (package.clone()); } AiPackage* MWMechanics::AiSequence::getActivePackage() { if(mPackages.empty()) throw std::runtime_error(std::string("No AI Package!")); else return mPackages.front(); } void AiSequence::fill(const ESM::AIPackageList &list) { for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) { MWMechanics::AiPackage* package; if (it->mType == ESM::AI_Wander) { ESM::AIWander data = it->mWander; std::vector idles; idles.reserve(8); for (int i=0; i<8; ++i) idles.push_back(data.mIdle[i]); package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0); } else if (it->mType == ESM::AI_Escort) { ESM::AITarget data = it->mTarget; package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } else if (it->mType == ESM::AI_Travel) { ESM::AITravel data = it->mTravel; package = new MWMechanics::AiTravel(data.mX, data.mY, data.mZ); } else if (it->mType == ESM::AI_Activate) { ESM::AIActivate data = it->mActivate; package = new MWMechanics::AiActivate(data.mName.toString()); } else //if (it->mType == ESM::AI_Follow) { ESM::AITarget data = it->mTarget; package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } mPackages.push_back(package); } } void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const { for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) { (*iter)->writeState(sequence); } } void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) { if (!sequence.mPackages.empty()) clear(); for (std::vector::const_iterator it = sequence.mPackages.begin(); it != sequence.mPackages.end(); ++it) { switch (it->mType) { case ESM::AiSequence::Ai_Wander: { MWMechanics::AiWander* wander = new AiWander( static_cast(it->mPackage)); mPackages.push_back(wander); break; } case ESM::AiSequence::Ai_Travel: { MWMechanics::AiTravel* travel = new AiTravel( static_cast(it->mPackage)); mPackages.push_back(travel); break; } case ESM::AiSequence::Ai_Escort: { MWMechanics::AiEscort* escort = new AiEscort( static_cast(it->mPackage)); mPackages.push_back(escort); break; } case ESM::AiSequence::Ai_Follow: { MWMechanics::AiFollow* follow = new AiFollow( static_cast(it->mPackage)); mPackages.push_back(follow); break; } case ESM::AiSequence::Ai_Activate: { MWMechanics::AiActivate* activate = new AiActivate( static_cast(it->mPackage)); mPackages.push_back(activate); break; } case ESM::AiSequence::Ai_Combat: { MWMechanics::AiCombat* combat = new AiCombat( static_cast(it->mPackage)); mPackages.push_back(combat); break; } case ESM::AiSequence::Ai_Pursue: { MWMechanics::AiPursue* pursue = new AiPursue( static_cast(it->mPackage)); mPackages.push_back(pursue); break; } default: break; } } } void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state) { if (!mPackages.empty()) { MWMechanics::AiPackage* package = mPackages.front(); package->fastForward(actor, state); } } } // namespace MWMechanics openmw-openmw-0.38.0/apps/openmw/mwmechanics/aisequence.hpp000066400000000000000000000103111264522266000240140ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AISEQUENCE_H #define GAME_MWMECHANICS_AISEQUENCE_H #include #include //#include "aistate.hpp" namespace MWWorld { class Ptr; } namespace ESM { namespace AiSequence { struct AiSequence; } } namespace MWMechanics { class AiPackage; class CharacterController; template< class Base > class DerivedClassStorage; struct AiTemporaryBase; typedef DerivedClassStorage AiState; /// \brief Sequence of AI-packages for a single actor /** The top-most AI package is run each frame. When completed, it is removed from the stack. **/ class AiSequence { ///AiPackages to run though std::list mPackages; ///Finished with top AIPackage, set for one frame bool mDone; ///Copy AiSequence void copy (const AiSequence& sequence); /// The type of AI package that ran last int mLastAiPackage; public: ///Default constructor AiSequence(); /// Copy Constructor AiSequence (const AiSequence& sequence); /// Assignment operator AiSequence& operator= (const AiSequence& sequence); virtual ~AiSequence(); /// Iterator may be invalidated by any function calls other than begin() or end(). std::list::const_iterator begin() const; std::list::const_iterator end() const; std::list::const_iterator erase (std::list::const_iterator package); /// Returns currently executing AiPackage type /** \see enum AiPackage::TypeId **/ int getTypeId() const; /// Get the typeid of the Ai package that ran last /** NOT the currently "active" Ai package that will be run in the next frame. This difference is important when an Ai package has just finished and been removed. \see enum AiPackage::TypeId **/ int getLastRunTypeId() const { return mLastAiPackage; } /// Return true and assign target if combat package is currently active, return false otherwise bool getCombatTarget (MWWorld::Ptr &targetActor) const; /// Is there any combat package? bool isInCombat () const; /// Are we in combat with this particular actor? bool isInCombat (const MWWorld::Ptr& actor) const; bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const; ///< Function assumes that actor can have only 1 target apart player /// Removes all combat packages until first non-combat or stack empty. void stopCombat(); /// Has a package been completed during the last update? bool isPackageDone() const; /// Removes all pursue packages until first non-pursue or stack empty. void stopPursuit(); /// Execute current package, switching if needed. void execute (const MWWorld::Ptr& actor, CharacterController& characterController, MWMechanics::AiState& state, float duration); /// Simulate the passing of time using the currently active AI package void fastForward(const MWWorld::Ptr &actor, AiState &state); /// Remove all packages. void clear(); ///< Add \a package to the front of the sequence /** Suspends current package @param actor The actor that owns this AiSequence **/ void stack (const AiPackage& package, const MWWorld::Ptr& actor); /// Return the current active package. /** If there is no active package, it will throw an exception **/ AiPackage* getActivePackage(); /// Fills the AiSequence with packages /** Typically used for loading from the ESM \see ESM::AIPackageList **/ void fill (const ESM::AIPackageList& list); void writeState (ESM::AiSequence::AiSequence& sequence) const; void readState (const ESM::AiSequence::AiSequence& sequence); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aistate.hpp000066400000000000000000000051721264522266000233350ustar00rootroot00000000000000#ifndef AISTATE_H #define AISTATE_H #include #include namespace MWMechanics { /** \brief stores one object of any class derived from Base. * Requesting a certain derived class via get() either returns * the stored object if it has the correct type or otherwise replaces * it with an object of the requested type. */ template< class Base > class DerivedClassStorage { private: Base* mStorage; //if needed you have to provide a clone member function DerivedClassStorage( const DerivedClassStorage& other ); DerivedClassStorage& operator=( const DerivedClassStorage& ); public: /// \brief returns reference to stored object or deletes it and creates a fitting template< class Derived > Derived& get() { Derived* result = dynamic_cast(mStorage); if(!result) { if(mStorage) delete mStorage; mStorage = result = new Derived(); } //return a reference to the (new allocated) object return *result; } template< class Derived > void store( const Derived& payload ) { if(mStorage) delete mStorage; mStorage = new Derived(payload); } /// \brief takes ownership of the passed object template< class Derived > void moveIn( Derived* p ) { if(mStorage) delete mStorage; mStorage = p; } bool empty() const { return mStorage == NULL; } const std::type_info& getType() const { return typeid(mStorage); } DerivedClassStorage():mStorage(NULL){} ~DerivedClassStorage() { if(mStorage) delete mStorage; } }; /// \brief base class for the temporary storage of AiPackages. /** * Each AI package with temporary values needs a AiPackageStorage class * which is derived from AiTemporaryBase. The Actor holds a container * AiState where one of these storages can be stored at a time. * The execute(...) member function takes this container as an argument. * */ struct AiTemporaryBase { virtual ~AiTemporaryBase(){} }; /// \brief Container for AI package status. typedef DerivedClassStorage AiState; } #endif // AISTATE_H openmw-openmw-0.38.0/apps/openmw/mwmechanics/aitravel.cpp000066400000000000000000000057571264522266000235160ustar00rootroot00000000000000#include "aitravel.hpp" #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "steering.hpp" #include "movement.hpp" #include "creaturestats.hpp" namespace MWMechanics { AiTravel::AiTravel(float x, float y, float z) : mX(x),mY(y),mZ(z) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { } AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel) : mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { } AiTravel *MWMechanics::AiTravel::clone() const { return new AiTravel(*this); } bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { ESM::Position pos = actor.getRefData().getPosition(); actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), pos.asVec3())) return false; if (pathTo(actor, ESM::Pathgrid::Point(static_cast(mX), static_cast(mY), static_cast(mZ)), duration)) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; return true; } return false; } bool AiTravel::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell) { bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; if (!mPathFinder.isPathConstructed() || cellChange) { mCellX = cell->mData.mX; mCellY = cell->mData.mY; return true; } return false; } int AiTravel::getTypeId() const { return TypeIdTravel; } void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state) { if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3())) return; // does not do any validation on the travel target (whether it's in air, inside collision geometry, etc), // that is the user's responsibility MWBase::Environment::get().getWorld()->moveObject(actor, mX, mY, mZ); actor.getClass().adjustPosition(actor, false); } void AiTravel::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr travel(new ESM::AiSequence::AiTravel()); travel->mData.mX = mX; travel->mData.mY = mY; travel->mData.mZ = mZ; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Travel; package.mPackage = travel.release(); sequence.mPackages.push_back(package); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/aitravel.hpp000066400000000000000000000023211264522266000235030ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AITRAVEL_H #define GAME_MWMECHANICS_AITRAVEL_H #include "aipackage.hpp" #include "pathfinding.hpp" namespace ESM { namespace AiSequence { struct AiTravel; } } namespace MWMechanics { /// \brief Causes the AI to travel to the specified point class AiTravel : public AiPackage { public: /// Default constructor AiTravel(float x, float y, float z); AiTravel(const ESM::AiSequence::AiTravel* travel); /// Simulates the passing of time virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); void writeState(ESM::AiSequence::AiSequence &sequence) const; virtual AiTravel *clone() const; virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); virtual int getTypeId() const; protected: virtual bool doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell); private: float mX; float mY; float mZ; int mCellX; int mCellY; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/aiwander.cpp000066400000000000000000001015631264522266000234710ustar00rootroot00000000000000#include "aiwander.hpp" #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" #include "creaturestats.hpp" #include "steering.hpp" #include "movement.hpp" #include "coordinateconverter.hpp" #include "actorutil.hpp" namespace MWMechanics { static const int COUNT_BEFORE_RESET = 10; static const float DOOR_CHECK_INTERVAL = 1.5f; static const float REACTION_INTERVAL = 0.25f; static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player static const int GREETING_SHOULD_END = 10; // to prevent overcrowding static const int DESTINATION_TOLERANCE = 64; // distance must be long enough that NPC will need to move to get there. static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2; const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = { std::string("idle2"), std::string("idle3"), std::string("idle4"), std::string("idle5"), std::string("idle6"), std::string("idle7"), std::string("idle8"), std::string("idle9"), }; /// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive. struct AiWanderStorage : AiTemporaryBase { // the z rotation angle to reach // when mTurnActorGivingGreetingToFacePlayer is true float mTargetAngleRadians; bool mTurnActorGivingGreetingToFacePlayer; float mReaction; // update some actions infrequently AiWander::GreetingState mSaidGreeting; int mGreetingTimer; const MWWorld::CellStore* mCell; // for detecting cell change // AiWander states AiWander::WanderState mState; unsigned short mIdleAnimation; std::vector mBadIdles; // Idle animations that when called cause errors PathFinder mPathFinder; AiWanderStorage(): mTargetAngleRadians(0), mTurnActorGivingGreetingToFacePlayer(false), mReaction(0), mSaidGreeting(AiWander::Greet_None), mGreetingTimer(0), mCell(NULL), mState(AiWander::Wander_ChooseAction), mIdleAnimation(0), mBadIdles() {}; }; AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) , mStoredInitialActorPosition(false) { mIdle.resize(8, 0); init(); } void AiWander::init() { // NOTE: mDistance and mDuration must be set already mStuckCount = 0;// TODO: maybe no longer needed mDoorCheckDuration = 0; mTrimCurrentNode = false; mHasReturnPosition = false; mReturnPosition = osg::Vec3f(0,0,0); if(mDistance < 0) mDistance = 0; if(mDuration < 0) mDuration = 0; if(mDuration == 0) mTimeOfDay = 0; mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mPopulateAvailableNodes = true; } AiPackage * MWMechanics::AiWander::clone() const { return new AiWander(*this); } /* * AiWander high level states (0.29.0). Not entirely accurate in some cases * e.g. non-NPC actors do not greet and some creatures may be moving even in * the IdleNow state. * * [select node, * build path] * +---------->MoveNow----------->Walking * | | * [allowed | | * nodes] | [hello if near] | * start--->ChooseAction----->IdleNow | * ^ ^ | | * | | | | * | +-----------+ | * | | * +----------------------------------+ * * * New high level states. Not exactly as per vanilla (e.g. door stuff) * but the differences are required because our physics does not work like * vanilla and therefore have to compensate/work around. * * [select node, [if stuck evade * build path] or remove nodes if near door] * +---------->MoveNow<---------->Walking * | ^ | | * | |(near door) | | * [allowed | | | | * nodes] | [hello if near] | | * start--->ChooseAction----->IdleNow | | * ^ ^ | ^ | | * | | | | (stuck near | | * | +-----------+ +---------------+ | * | player) | * +----------------------------------+ * * NOTE: non-time critical operations are run once every 250ms or so. * * TODO: It would be great if door opening/closing can be detected and pathgrid * links dynamically updated. Currently (0.29.0) AiWander allows choosing a * destination beyond closed doors which sometimes makes the actors stuck at the * door and impossible for the player to open the door. * * For now detect being stuck at the door and simply delete the nodes from the * allowed set. The issue is when the door opens the allowed set is not * re-calculated. However this would not be an issue in most cases since hostile * actors will enter combat (i.e. no longer wandering) and different pathfinding * will kick in. */ bool AiWander::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { // get or create temporary storage AiWanderStorage& storage = state.get(); const MWWorld::CellStore*& currentCell = storage.mCell; MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); if(cStats.isDead() || cStats.getHealth().getCurrent() <= 0) return true; // Don't bother with dead actors bool cellChange = currentCell && (actor.getCell() != currentCell); if(!currentCell || cellChange) { currentCell = actor.getCell(); mPopulateAvailableNodes = true; } cStats.setDrawState(DrawState_Nothing); cStats.setMovementFlag(CreatureStats::Flag_Run, false); ESM::Position pos = actor.getRefData().getPosition(); doPerFrameActionsForState(actor, duration, storage, pos); playIdleDialogueRandomly(actor); float& lastReaction = storage.mReaction; lastReaction += duration; if (REACTION_INTERVAL <= lastReaction) { lastReaction = 0; return reactionTimeActions(actor, storage, currentCell, cellChange, pos); } else return false; } bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos) { if (isPackageCompleted(actor, storage)) { return true; } // Initialization to discover & store allowed node points for this actor. if (mPopulateAvailableNodes) { getAllowedNodes(actor, currentCell->getCell()); } // Actor becomes stationary - see above URL's for previous research if(mAllowedNodes.empty()) mDistance = 0; // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles. if(mDistance && cellChange) mDistance = 0; // For stationary NPCs, move back to the starting location if another AiPackage moved us elsewhere if (cellChange) mHasReturnPosition = false; if (mDistance == 0 && mHasReturnPosition && (pos.asVec3() - mReturnPosition).length2() > (DESTINATION_TOLERANCE * DESTINATION_TOLERANCE)) { returnToStartLocation(actor, storage, pos); } // Allow interrupting a walking actor to trigger a greeting WanderState& wanderState = storage.mState; if ((wanderState == Wander_IdleNow) || (wanderState == Wander_Walking)) { playGreetingIfPlayerGetsTooClose(actor, storage); } if ((wanderState == Wander_MoveNow) && mDistance) { // Construct a new path if there isn't one if(!storage.mPathFinder.isPathConstructed()) { if (mAllowedNodes.size()) { setPathToAnAllowedNode(actor, storage, pos); } } } return false; // AiWander package not yet completed } bool AiWander::isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage) { if (mDuration) { // End package if duration is complete or mid-night hits: MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); if ((currentTime.getHour() >= mStartTime.getHour() + mDuration) || (int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay())) { if (!mRepeat) { stopWalking(actor, storage); return true; } else { mStartTime = currentTime; } } } // if get here, not yet completed return false; } void AiWander::returnToStartLocation(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos) { if (!storage.mPathFinder.isPathConstructed()) { ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mReturnPosition)); // actor position is already in world co-ordinates ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); // don't take shortcuts for wandering storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell(), false); if (storage.mPathFinder.isPathConstructed()) { storage.mState = Wander_Walking; } } } void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos) { switch (storage.mState) { case Wander_IdleNow: onIdleStatePerFrameActions(actor, duration, storage); break; case Wander_Walking: onWalkingStatePerFrameActions(actor, duration, storage, pos); break; case Wander_ChooseAction: onChooseActionStatePerFrameActions(actor, storage); break; case Wander_MoveNow: break; // nothing to do default: // should never get here assert(false); break; } } void AiWander::onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage) { // Check if an idle actor is too close to a door - if so start walking mDoorCheckDuration += duration; if (mDoorCheckDuration >= DOOR_CHECK_INTERVAL) { mDoorCheckDuration = 0; // restart timer if (mDistance && // actor is not intended to be stationary proximityToDoor(actor, MIN_DIST_TO_DOOR_SQUARED*1.6f*1.6f)) // NOTE: checks interior cells only { storage.mState = Wander_MoveNow; mTrimCurrentNode = false; // just in case return; } } bool& rotate = storage.mTurnActorGivingGreetingToFacePlayer; if (rotate) { // Reduce the turning animation glitch by using a *HUGE* value of // epsilon... TODO: a proper fix might be in either the physics or the // animation subsystem if (zTurn(actor, storage.mTargetAngleRadians, osg::DegreesToRadians(5.f))) rotate = false; } // Check if idle animation finished GreetingState& greetingState = storage.mSaidGreeting; if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None)) { storage.mState = Wander_ChooseAction; } } void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos) { // Are we there yet? if (storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE)) { stopWalking(actor, storage); storage.mState = Wander_ChooseAction; mHasReturnPosition = false; } else { // have not yet reached the destination evadeObstacles(actor, storage, duration, pos); } } void AiWander::onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage) { short unsigned& idleAnimation = storage.mIdleAnimation; idleAnimation = getRandomIdle(); if (!idleAnimation && mDistance) { storage.mState = Wander_MoveNow; return; } if(idleAnimation) { if(std::find(storage.mBadIdles.begin(), storage.mBadIdles.end(), idleAnimation)==storage.mBadIdles.end()) { if(!playIdle(actor, idleAnimation)) { storage.mBadIdles.push_back(idleAnimation); storage.mState = Wander_ChooseAction; return; } } } // Recreate vanilla (broken?) behavior of resetting start time of AIWander: mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); storage.mState = Wander_IdleNow; } void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos) { // turn towards the next point in mPath zTurn(actor, storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])); MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor); if (mObstacleCheck.check(actor, duration)) { // first check if we're walking into a door if (proximityToDoor(actor)) // NOTE: checks interior cells only { // remove allowed points then select another random destination mTrimCurrentNode = true; trimAllowedNodes(mAllowedNodes, storage.mPathFinder); mObstacleCheck.clear(); storage.mPathFinder.clearPath(); storage.mState = Wander_MoveNow; } else // probably walking into another NPC { // TODO: diagonal should have same animation as walk forward // but doesn't seem to do that? mObstacleCheck.takeEvasiveAction(movement); } mStuckCount++; // TODO: maybe no longer needed } else { movement.mPosition[1] = 1; } // if stuck for sufficiently long, act like current location was the destination if (mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset { //std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl; mObstacleCheck.clear(); stopWalking(actor, storage); storage.mState = Wander_ChooseAction; mStuckCount = 0; } } void AiWander::playIdleDialogueRandomly(const MWWorld::Ptr& actor) { int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); if (hello > 0 && !MWBase::Environment::get().getWorld()->isSwimming(actor) && MWBase::Environment::get().getSoundManager()->sayDone(actor)) { MWWorld::Ptr player = getPlayer(); static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() .get().find("fVoiceIdleOdds")->getFloat(); float roll = Misc::Rng::rollProbability() * 10000.0f; // In vanilla MW the chance was FPS dependent, and did not allow proper changing of fVoiceIdleOdds // due to the roll being an integer. // Our implementation does not have these issues, so needs to be recalibrated. We chose to // use the chance MW would have when run at 60 FPS with the default value of the GMST for calibration. float x = fVoiceIdleOdds * 0.6f * (MWBase::Environment::get().getFrameDuration() / 0.1f); // Only say Idle voices when player is in LOS // A bit counterintuitive, likely vanilla did this to reduce the appearance of // voices going through walls? const ESM::Position& pos = actor.getRefData().getPosition(); if (roll < x && (player.getRefData().getPosition().asVec3() - pos.asVec3()).length2() < 3000 * 3000 // maybe should be fAudioVoiceDefaultMaxDistance*fAudioMaxDistanceMult instead && MWBase::Environment::get().getWorld()->getLOS(player, actor)) MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } } void AiWander::playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage) { // Play a random voice greeting if the player gets too close int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); float helloDistance = static_cast(hello); static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore() .get().find("iGreetDistanceMultiplier")->getInt(); helloDistance *= iGreetDistanceMultiplier; MWWorld::Ptr player = getPlayer(); osg::Vec3f playerPos(player.getRefData().getPosition().asVec3()); osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); float playerDistSqr = (playerPos - actorPos).length2(); int& greetingTimer = storage.mGreetingTimer; GreetingState& greetingState = storage.mSaidGreeting; if (greetingState == Greet_None) { if ((playerDistSqr <= helloDistance*helloDistance) && !player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor)) greetingTimer++; if (greetingTimer >= GREETING_SHOULD_START) { greetingState = Greet_InProgress; MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); greetingTimer = 0; } } if (greetingState == Greet_InProgress) { greetingTimer++; if (storage.mState == Wander_Walking) { stopWalking(actor, storage); mObstacleCheck.clear(); storage.mState = Wander_IdleNow; } turnActorToFacePlayer(actorPos, playerPos, storage); if (greetingTimer >= GREETING_SHOULD_END) { greetingState = Greet_Done; greetingTimer = 0; } } if (greetingState == MWMechanics::AiWander::Greet_Done) { float resetDist = 2 * helloDistance; if (playerDistSqr >= resetDist*resetDist) greetingState = Greet_None; } } void AiWander::turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage) { osg::Vec3f dir = playerPosition - actorPosition; float faceAngleRadians = std::atan2(dir.x(), dir.y()); storage.mTargetAngleRadians = faceAngleRadians; storage.mTurnActorGivingGreetingToFacePlayer = true; } void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos) { unsigned int randNode = Misc::Rng::rollDice(mAllowedNodes.size()); ESM::Pathgrid::Point dest(mAllowedNodes[randNode]); ToWorldCoordinates(dest, storage.mCell->getCell()); // actor position is already in world co-ordinates ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actorPos)); // don't take shortcuts for wandering storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell(), false); if (storage.mPathFinder.isPathConstructed()) { // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; mAllowedNodes.erase(mAllowedNodes.begin() + randNode); // check if mCurrentNode was taken out of mAllowedNodes if (mTrimCurrentNode && mAllowedNodes.size() > 1) mTrimCurrentNode = false; else mAllowedNodes.push_back(mCurrentNode); mCurrentNode = temp; storage.mState = Wander_Walking; } // Choose a different node and delete this one from possible nodes because it is uncreachable: else mAllowedNodes.erase(mAllowedNodes.begin() + randNode); } void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell) { CoordinateConverter(cell).toWorld(point); } void AiWander::trimAllowedNodes(std::vector& nodes, const PathFinder& pathfinder) { // TODO: how to add these back in once the door opens? // Idea: keep a list of detected closed doors (see aicombat.cpp) // Every now and then check whether one of the doors is opened. (maybe // at the end of playing idle?) If the door is opened then re-calculate // allowed nodes starting from the spawn point. std::list paths = pathfinder.getPath(); while(paths.size() >= 2) { ESM::Pathgrid::Point pt = paths.back(); for(unsigned int j = 0; j < nodes.size(); j++) { // FIXME: doesn't hadle a door with the same X/Y // co-ordinates but with a different Z if(nodes[j].mX == pt.mX && nodes[j].mY == pt.mY) { nodes.erase(nodes.begin() + j); break; } } paths.pop_back(); } } int AiWander::getTypeId() const { return TypeIdWander; } void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage) { storage.mPathFinder.clearPath(); actor.getClass().getMovementSettings(actor).mPosition[1] = 0; } bool AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) { if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle)) { const std::string& groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle]; return MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, groupName, 0, 1); } else { std::cerr<< "Attempted to play out of range idle animation \""<checkAnimationPlaying(actor, groupName); } else { return false; } } void AiWander::setReturnPosition(const osg::Vec3f& position) { if (!mHasReturnPosition) { mHasReturnPosition = true; mReturnPosition = position; } } short unsigned AiWander::getRandomIdle() { unsigned short idleRoll = 0; short unsigned selectedAnimation = 0; for(unsigned int counter = 0; counter < mIdle.size(); counter++) { static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore() .get().find("fIdleChanceMultiplier")->getFloat(); unsigned short idleChance = static_cast(fIdleChanceMultiplier * mIdle[counter]); unsigned short randSelect = (int)(Misc::Rng::rollProbability() * int(100 / fIdleChanceMultiplier)); if(randSelect < idleChance && randSelect > idleRoll) { selectedAnimation = counter + GroupIndex_MinIdle; idleRoll = randSelect; } } return selectedAnimation; } void AiWander::fastForward(const MWWorld::Ptr& actor, AiState &state) { if (mDistance == 0) return; if (mPopulateAvailableNodes) getAllowedNodes(actor, actor.getCell()->getCell()); if (mAllowedNodes.empty()) return; state.moveIn(new AiWanderStorage()); int index = Misc::Rng::rollDice(mAllowedNodes.size()); ESM::Pathgrid::Point dest = mAllowedNodes[index]; dest.mX += OffsetToPreventOvercrowding(); dest.mY += OffsetToPreventOvercrowding(); ToWorldCoordinates(dest, actor.getCell()->getCell()); MWBase::Environment::get().getWorld()->moveObject(actor, static_cast(dest.mX), static_cast(dest.mY), static_cast(dest.mZ)); actor.getClass().adjustPosition(actor, false); // may have changed cell mPopulateAvailableNodes = true; } int AiWander::OffsetToPreventOvercrowding() { return static_cast(DESTINATION_TOLERANCE * (Misc::Rng::rollProbability() * 2.0f - 1.0f)); } void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell) { if (!mStoredInitialActorPosition) { mInitialActorPosition = actor.getRefData().getPosition().asVec3(); mStoredInitialActorPosition = true; } // infrequently used, therefore no benefit in caching it as a member const ESM::Pathgrid * pathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); // If there is no path this actor doesn't go anywhere. See: // https://forum.openmw.org/viewtopic.php?t=1556 // http://www.fliggerty.com/phpBB3/viewtopic.php?f=30&t=5833 // Note: In order to wander, need at least two points. if(!pathgrid || (pathgrid->mPoints.size() < 2)) mDistance = 0; // A distance value passed into the constructor indicates how far the // actor can wander from the spawn position. AiWander assumes that // pathgrid points are available, and uses them to randomly select wander // destinations within the allowed set of pathgrid points (nodes). // ... pathgrids don't usually include water, so swimmers ignore them if (mDistance && !actor.getClass().isPureWaterCreature(actor)) { // get NPC's position in local (i.e. cell) co-ordinates osg::Vec3f npcPos(mInitialActorPosition); CoordinateConverter(cell).toLocal(npcPos); // mAllowedNodes for this actor with pathgrid point indexes based on mDistance // NOTE: mPoints and mAllowedNodes are in local co-ordinates int pointIndex = 0; for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) { osg::Vec3f nodePos(PathFinder::MakeOsgVec3(pathgrid->mPoints[counter])); if((npcPos - nodePos).length2() <= mDistance * mDistance) { mAllowedNodes.push_back(pathgrid->mPoints[counter]); pointIndex = counter; } } if (mAllowedNodes.size() == 1) { AddNonPathGridAllowedPoints(npcPos, pathgrid, pointIndex); } if(!mAllowedNodes.empty()) { SetCurrentNodeToClosestAllowedNode(npcPos); } } mPopulateAvailableNodes = false; } // When only one path grid point in wander distance, // additional points for NPC to wander to are: // 1. NPC's initial location // 2. Partway along the path between the point and its connected points. void AiWander::AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex) { mAllowedNodes.push_back(PathFinder::MakePathgridPoint(npcPos)); for (std::vector::const_iterator it = pathGrid->mEdges.begin(); it != pathGrid->mEdges.end(); ++it) { if (it->mV0 == pointIndex) { AddPointBetweenPathGridPoints(pathGrid->mPoints[it->mV0], pathGrid->mPoints[it->mV1]); } } } void AiWander::AddPointBetweenPathGridPoints(const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end) { osg::Vec3f vectorStart = PathFinder::MakeOsgVec3(start); osg::Vec3f delta = PathFinder::MakeOsgVec3(end) - vectorStart; float length = delta.length(); delta.normalize(); int distance = std::max(mDistance / 2, MINIMUM_WANDER_DISTANCE); // must not travel longer than distance between waypoints or NPC goes past waypoint distance = std::min(distance, static_cast(length)); delta *= distance; mAllowedNodes.push_back(PathFinder::MakePathgridPoint(vectorStart + delta)); } void AiWander::SetCurrentNodeToClosestAllowedNode(osg::Vec3f npcPos) { float distanceToClosestNode = std::numeric_limits::max(); unsigned int index = 0; for (unsigned int counterThree = 0; counterThree < mAllowedNodes.size(); counterThree++) { osg::Vec3f nodePos(PathFinder::MakeOsgVec3(mAllowedNodes[counterThree])); float tempDist = (npcPos - nodePos).length2(); if (tempDist < distanceToClosestNode) { index = counterThree; distanceToClosestNode = tempDist; } } mCurrentNode = mAllowedNodes[index]; mAllowedNodes.erase(mAllowedNodes.begin() + index); } void AiWander::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr wander(new ESM::AiSequence::AiWander()); wander->mData.mDistance = mDistance; wander->mData.mDuration = mDuration; wander->mData.mTimeOfDay = mTimeOfDay; wander->mStartTime = mStartTime.toEsm(); assert (mIdle.size() == 8); for (int i=0; i<8; ++i) wander->mData.mIdle[i] = mIdle[i]; wander->mData.mShouldRepeat = mRepeat; wander->mStoredInitialActorPosition = mStoredInitialActorPosition; if (mStoredInitialActorPosition) wander->mInitialActorPosition = mInitialActorPosition; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Wander; package.mPackage = wander.release(); sequence.mPackages.push_back(package); } AiWander::AiWander (const ESM::AiSequence::AiWander* wander) : mDistance(wander->mData.mDistance) , mDuration(wander->mData.mDuration) , mTimeOfDay(wander->mData.mTimeOfDay) , mRepeat(wander->mData.mShouldRepeat != 0) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mStartTime(MWWorld::TimeStamp(wander->mStartTime)) { if (mStoredInitialActorPosition) mInitialActorPosition = wander->mInitialActorPosition; for (int i=0; i<8; ++i) mIdle.push_back(wander->mData.mIdle[i]); init(); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/aiwander.hpp000066400000000000000000000142041264522266000234710ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_AIWANDER_H #define GAME_MWMECHANICS_AIWANDER_H #include "aipackage.hpp" #include #include "pathfinding.hpp" #include "obstacle.hpp" #include "../mwworld/timestamp.hpp" #include "aistate.hpp" namespace ESM { struct Cell; namespace AiSequence { struct AiWander; } } namespace MWMechanics { struct AiWanderStorage; /// \brief Causes the Actor to wander within a specified range class AiWander : public AiPackage { public: /// Constructor /** \param distance Max distance the ACtor will wander \param duration Time, in hours, that this package will be preformed \param timeOfDay Start time of the package, if it has a duration. Currently unimplemented \param idle Chances of each idle to play (9 in total) \param repeat Repeat wander or not **/ AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat); AiWander (const ESM::AiSequence::AiWander* wander); virtual AiPackage *clone() const; virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); virtual int getTypeId() const; /// Set the position to return to for a stationary (non-wandering) actor /** In case another AI package moved the actor elsewhere **/ void setReturnPosition (const osg::Vec3f& position); virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); enum GreetingState { Greet_None, Greet_InProgress, Greet_Done }; enum WanderState { Wander_ChooseAction, Wander_IdleNow, Wander_MoveNow, Wander_Walking }; private: // NOTE: mDistance and mDuration must be set already void init(); void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage); /// Have the given actor play an idle animation /// @return Success or error bool playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); short unsigned getRandomIdle(); void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos); void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage); void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos); void playIdleDialogueRandomly(const MWWorld::Ptr& actor); void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage); void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos); void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos); void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage); bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos); bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage); void returnToStartLocation(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos); int mDistance; // how far the actor can wander from the spawn point int mDuration; int mTimeOfDay; std::vector mIdle; bool mRepeat; bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position, // if we had the actor in the AiWander constructor... osg::Vec3f mReturnPosition; osg::Vec3f mInitialActorPosition; bool mStoredInitialActorPosition; // do we need to calculate allowed nodes based on mDistance bool mPopulateAvailableNodes; MWWorld::TimeStamp mStartTime; // allowed pathgrid nodes based on mDistance from the spawn point std::vector mAllowedNodes; void getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell); ESM::Pathgrid::Point mCurrentNode; bool mTrimCurrentNode; void trimAllowedNodes(std::vector& nodes, const PathFinder& pathfinder); // ObstacleCheck mObstacleCheck; float mDoorCheckDuration; int mStuckCount; // constants for converting idleSelect values into groupNames enum GroupIndex { GroupIndex_MinIdle = 2, GroupIndex_MaxIdle = 9 }; /// convert point from local (i.e. cell) to world co-ordinates void ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell); void SetCurrentNodeToClosestAllowedNode(osg::Vec3f npcPos); void AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex); void AddPointBetweenPathGridPoints(const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end); /// lookup table for converting idleSelect value to groupName static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1]; static int OffsetToPreventOvercrowding(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/alchemy.cpp000066400000000000000000000351171264522266000233220ustar00rootroot00000000000000#include "alchemy.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/manualref.hpp" #include "magiceffects.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" MWMechanics::Alchemy::Alchemy() : mValue(0) { } std::set MWMechanics::Alchemy::listEffects() const { std::map effects; for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) { if (!iter->isEmpty()) { const MWWorld::LiveCellRef *ingredient = iter->get(); std::set seenEffects; for (int i=0; i<4; ++i) if (ingredient->mBase->mData.mEffectID[i]!=-1) { EffectKey key ( ingredient->mBase->mData.mEffectID[i], ingredient->mBase->mData.mSkills[i]!=-1 ? ingredient->mBase->mData.mSkills[i] : ingredient->mBase->mData.mAttributes[i]); if (seenEffects.insert(key).second) ++effects[key]; } } } std::set effects2; for (std::map::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) if (iter->second>1) effects2.insert (iter->first); return effects2; } void MWMechanics::Alchemy::applyTools (int flags, float& value) const { bool magnitude = !(flags & ESM::MagicEffect::NoMagnitude); bool duration = !(flags & ESM::MagicEffect::NoDuration); bool negative = (flags & ESM::MagicEffect::Harmful) != 0; int tool = negative ? ESM::Apparatus::Retort : ESM::Apparatus::Albemic; int setup = 0; if (!mTools[tool].isEmpty() && !mTools[ESM::Apparatus::Calcinator].isEmpty()) setup = 1; else if (!mTools[tool].isEmpty()) setup = 2; else if (!mTools[ESM::Apparatus::Calcinator].isEmpty()) setup = 3; else return; float toolQuality = setup==1 || setup==2 ? mTools[tool].get()->mBase->mData.mQuality : 0; float calcinatorQuality = setup==1 || setup==3 ? mTools[ESM::Apparatus::Calcinator].get()->mBase->mData.mQuality : 0; float quality = 1; switch (setup) { case 1: quality = negative ? 2 * toolQuality + 3 * calcinatorQuality : (magnitude && duration ? 2 * toolQuality + calcinatorQuality : 2/3.0f * (toolQuality + calcinatorQuality) + 0.5f); break; case 2: quality = negative ? 1+toolQuality : (magnitude && duration ? toolQuality : toolQuality + 0.5f); break; case 3: quality = magnitude && duration ? calcinatorQuality : calcinatorQuality + 0.5f; break; } if (setup==3 || !negative) { value += quality; } else { if (quality==0) throw std::runtime_error ("invalid derived alchemy apparatus quality"); value /= quality; } } void MWMechanics::Alchemy::updateEffects() { mEffects.clear(); mValue = 0; if (countIngredients()<2 || mAlchemist.isEmpty() || mTools[ESM::Apparatus::MortarPestle].isEmpty()) return; // find effects std::set effects (listEffects()); // general alchemy factor float x = getAlchemyFactor(); x *= mTools[ESM::Apparatus::MortarPestle].get()->mBase->mData.mQuality; x *= MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionStrengthMult")->getFloat(); // value mValue = static_cast ( x * MWBase::Environment::get().getWorld()->getStore().get().find ("iAlchemyMod")->getFloat()); // build quantified effect list for (std::set::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) { const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find (iter->mId); if (magicEffect->mData.mBaseCost<=0) { std::ostringstream os; os << "invalid base cost for magic effect " << iter->mId; throw std::runtime_error (os.str()); } float fPotionT1MagMul = MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1MagMult")->getFloat(); if (fPotionT1MagMul<=0) throw std::runtime_error ("invalid gmst: fPotionT1MagMul"); float fPotionT1DurMult = MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1DurMult")->getFloat(); if (fPotionT1DurMult<=0) throw std::runtime_error ("invalid gmst: fPotionT1DurMult"); float magnitude = magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude ? 1 : (x / fPotionT1MagMul) / magicEffect->mData.mBaseCost; float duration = magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration ? 1 : (x / fPotionT1DurMult) / magicEffect->mData.mBaseCost; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) applyTools (magicEffect->mData.mFlags, magnitude); if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) applyTools (magicEffect->mData.mFlags, duration); duration = roundf(duration); magnitude = roundf(magnitude); if (magnitude>0 && duration>0) { ESM::ENAMstruct effect; effect.mEffectID = iter->mId; effect.mAttribute = -1; effect.mSkill = -1; if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) effect.mSkill = iter->mArg; else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) effect.mAttribute = iter->mArg; effect.mRange = 0; effect.mArea = 0; effect.mDuration = static_cast(duration); effect.mMagnMin = effect.mMagnMax = static_cast(magnitude); mEffects.push_back (effect); } } } const ESM::Potion *MWMechanics::Alchemy::getRecord(const ESM::Potion& toFind) const { const MWWorld::Store &potions = MWBase::Environment::get().getWorld()->getStore().get(); MWWorld::Store::iterator iter = potions.begin(); for (; iter != potions.end(); ++iter) { if (iter->mEffects.mList.size() != mEffects.size()) continue; if (iter->mName != toFind.mName || iter->mScript != toFind.mScript || iter->mData.mWeight != toFind.mData.mWeight || iter->mData.mValue != toFind.mData.mValue || iter->mData.mAutoCalc != toFind.mData.mAutoCalc) continue; // Don't choose an ID that came from the content files, would have unintended side effects // where alchemy can be used to produce quest-relevant items if (!potions.isDynamic(iter->mId)) continue; bool mismatch = false; for (int i=0; i (iter->mEffects.mList.size()); ++i) { const ESM::ENAMstruct& first = iter->mEffects.mList[i]; const ESM::ENAMstruct& second = mEffects[i]; if (first.mEffectID!=second.mEffectID || first.mArea!=second.mArea || first.mRange!=second.mRange || first.mSkill!=second.mSkill || first.mAttribute!=second.mAttribute || first.mMagnMin!=second.mMagnMin || first.mMagnMax!=second.mMagnMax || first.mDuration!=second.mDuration) { mismatch = true; break; } } if (!mismatch) return &(*iter); } return 0; } void MWMechanics::Alchemy::removeIngredients() { bool needsUpdate = false; for (TIngredientsContainer::iterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) if (!iter->isEmpty()) { iter->getContainerStore()->remove(*iter, 1, mAlchemist); if (iter->getRefData().getCount()<1) { needsUpdate = true; *iter = MWWorld::Ptr(); } } if (needsUpdate) updateEffects(); } void MWMechanics::Alchemy::addPotion (const std::string& name) { ESM::Potion newRecord; newRecord.mData.mWeight = 0; for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) if (!iter->isEmpty()) newRecord.mData.mWeight += iter->get()->mBase->mData.mWeight; if (countIngredients() > 0) newRecord.mData.mWeight /= countIngredients(); newRecord.mData.mValue = mValue; newRecord.mData.mAutoCalc = 0; newRecord.mName = name; int index = Misc::Rng::rollDice(6); assert (index>=0 && index<6); static const char *meshes[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" }; newRecord.mModel = "m\\misc_potion_" + std::string (meshes[index]) + "_01.nif"; newRecord.mIcon = "m\\tx_potion_" + std::string (meshes[index]) + "_01.dds"; newRecord.mEffects.mList = mEffects; const ESM::Potion* record = getRecord(newRecord); if (!record) record = MWBase::Environment::get().getWorld()->createRecord (newRecord); mAlchemist.getClass().getContainerStore (mAlchemist).add (record->mId, 1, mAlchemist); } void MWMechanics::Alchemy::increaseSkill() { mAlchemist.getClass().skillUsageSucceeded (mAlchemist, ESM::Skill::Alchemy, 0); } float MWMechanics::Alchemy::getAlchemyFactor() const { const CreatureStats& creatureStats = mAlchemist.getClass().getCreatureStats (mAlchemist); const NpcStats& npcStats = mAlchemist.getClass().getNpcStats (mAlchemist); return (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + 0.1f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified() + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()); } int MWMechanics::Alchemy::countIngredients() const { int ingredients = 0; for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) if (!iter->isEmpty()) ++ingredients; return ingredients; } void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc) { mAlchemist = npc; mIngredients.resize (4); std::fill (mIngredients.begin(), mIngredients.end(), MWWorld::Ptr()); mTools.resize (4); std::fill (mTools.begin(), mTools.end(), MWWorld::Ptr()); mEffects.clear(); MWWorld::ContainerStore& store = npc.getClass().getContainerStore (npc); for (MWWorld::ContainerStoreIterator iter (store.begin (MWWorld::ContainerStore::Type_Apparatus)); iter!=store.end(); ++iter) { MWWorld::LiveCellRef* ref = iter->get(); int type = ref->mBase->mData.mType; if (type<0 || type>=static_cast (mTools.size())) throw std::runtime_error ("invalid apparatus type"); if (!mTools[type].isEmpty()) if (ref->mBase->mData.mQuality<=mTools[type].get()->mBase->mData.mQuality) continue; mTools[type] = *iter; } } MWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::beginTools() const { return mTools.begin(); } MWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::endTools() const { return mTools.end(); } MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::beginIngredients() const { return mIngredients.begin(); } MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::endIngredients() const { return mIngredients.end(); } void MWMechanics::Alchemy::clear() { mAlchemist = MWWorld::Ptr(); mTools.clear(); mIngredients.clear(); mEffects.clear(); } int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient) { // find a free slot int slot = -1; for (int i=0; i (mIngredients.size()); ++i) if (mIngredients[i].isEmpty()) { slot = i; break; } if (slot==-1) return -1; for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) if (!iter->isEmpty() && Misc::StringUtils::ciEqual(ingredient.getCellRef().getRefId(), iter->getCellRef().getRefId())) return -1; mIngredients[slot] = ingredient; updateEffects(); return slot; } void MWMechanics::Alchemy::removeIngredient (int index) { if (index>=0 && index (mIngredients.size())) { mIngredients[index] = MWWorld::Ptr(); updateEffects(); } } MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::beginEffects() const { return mEffects.begin(); } MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const { return mEffects.end(); } MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name) { if (mTools[ESM::Apparatus::MortarPestle].isEmpty()) return Result_NoMortarAndPestle; if (countIngredients()<2) return Result_LessThanTwoIngredients; if (name.empty()) return Result_NoName; if (listEffects().empty()) { removeIngredients(); return Result_NoEffects; } if (beginEffects() == endEffects()) { // all effects were nullified due to insufficient skill removeIngredients(); return Result_RandomFailure; } if (getAlchemyFactor() < Misc::Rng::roll0to99()) { removeIngredients(); return Result_RandomFailure; } addPotion (name); removeIngredients(); increaseSkill(); return Result_Success; } std::string MWMechanics::Alchemy::suggestPotionName() { std::set effects = listEffects(); if (effects.empty()) return ""; int effectId = effects.begin()->mId; return MWBase::Environment::get().getWorld()->getStore().get().find( ESM::MagicEffect::effectIdToString(effectId))->getString(); } openmw-openmw-0.38.0/apps/openmw/mwmechanics/alchemy.hpp000066400000000000000000000076171264522266000233330ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_ALCHEMY_H #define GAME_MWMECHANICS_ALCHEMY_H #include #include #include #include "../mwworld/ptr.hpp" namespace ESM { struct Potion; } namespace MWMechanics { struct EffectKey; /// \brief Potion creation via alchemy skill class Alchemy { public: Alchemy(); typedef std::vector TToolsContainer; typedef TToolsContainer::const_iterator TToolsIterator; typedef std::vector TIngredientsContainer; typedef TIngredientsContainer::const_iterator TIngredientsIterator; typedef std::vector TEffectsContainer; typedef TEffectsContainer::const_iterator TEffectsIterator; enum Result { Result_Success, Result_NoMortarAndPestle, Result_LessThanTwoIngredients, Result_NoName, Result_NoEffects, Result_RandomFailure }; private: MWWorld::Ptr mAlchemist; TToolsContainer mTools; TIngredientsContainer mIngredients; TEffectsContainer mEffects; int mValue; void applyTools (int flags, float& value) const; void updateEffects(); const ESM::Potion *getRecord(const ESM::Potion& toFind) const; ///< Try to find a potion record similar to \a toFind in the record store, or return 0 if not found /// \note Does not account for record ID, model or icon void removeIngredients(); ///< Remove selected ingredients from alchemist's inventory, cleanup selected ingredients and /// update effect list accordingly. void addPotion (const std::string& name); ///< Add a potion to the alchemist's inventory. void increaseSkill(); ///< Increase alchemist's skill. float getAlchemyFactor() const; int countIngredients() const; TEffectsIterator beginEffects() const; TEffectsIterator endEffects() const; public: void setAlchemist (const MWWorld::Ptr& npc); ///< Set alchemist and configure alchemy setup accordingly. \a npc may be empty to indicate that /// there is no alchemist (alchemy session has ended). TToolsIterator beginTools() const; ///< \attention Iterates over tool slots, not over tools. Some of the slots may be empty. TToolsIterator endTools() const; TIngredientsIterator beginIngredients() const; ///< \attention Iterates over ingredient slots, not over ingredients. Some of the slots may be empty. TIngredientsIterator endIngredients() const; void clear(); ///< Remove alchemist, tools and ingredients. std::set listEffects() const; ///< List all effects shared by at least two ingredients. int addIngredient (const MWWorld::Ptr& ingredient); ///< Add ingredient into the next free slot. /// /// \return Slot index or -1, if adding failed because of no free slot or the ingredient type being /// listed already. void removeIngredient (int index); ///< Remove ingredient from slot (calling this function on an empty slot is a no-op). std::string suggestPotionName (); ///< Suggest a name for the potion, based on the current effects Result create (const std::string& name); ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and /// adjust the skills of the alchemist accordingly. /// \param name must not be an empty string, or Result_NoName is returned }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/autocalcspell.cpp000066400000000000000000000231151264522266000245260ustar00rootroot00000000000000#include "autocalcspell.hpp" #include #include #include "../mwworld/esmstore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" namespace MWMechanics { struct SchoolCaps { int mCount; int mLimit; bool mReachedLimit; int mMinCost; std::string mWeakestSpell; }; std::vector autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race) { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat(); float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; static int iAutoSpellSchoolMax[6]; static bool init = false; if (!init) { for (int i=0; i<6; ++i) { const std::string& gmstName = "iAutoSpell" + schools[i] + "Max"; iAutoSpellSchoolMax[i] = gmst.find(gmstName)->getInt(); } init = true; } std::map schoolCaps; for (int i=0; i<6; ++i) { SchoolCaps caps; caps.mCount = 0; caps.mLimit = iAutoSpellSchoolMax[i]; caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0; caps.mMinCost = INT_MAX; caps.mWeakestSpell.clear(); schoolCaps[i] = caps; } std::vector selectedSpells; const MWWorld::Store &spells = MWBase::Environment::get().getWorld()->getStore().get(); // Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the // Store must preserve the record ordering as it was in the content files. for (MWWorld::Store::iterator iter = spells.begin(); iter != spells.end(); ++iter) { const ESM::Spell* spell = &*iter; if (spell->mData.mType != ESM::Spell::ST_Spell) continue; if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc)) continue; static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->getInt(); if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost) continue; if (race && race->mPowers.exists(spell->mId)) continue; if (!attrSkillCheck(spell, actorSkills, actorAttributes)) continue; int school; float skillTerm; calcWeakestSchool(spell, actorSkills, school, skillTerm); assert(school >= 0 && school < 6); SchoolCaps& cap = schoolCaps[school]; if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost) continue; static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->getFloat(); if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance) continue; selectedSpells.push_back(spell->mId); if (cap.mReachedLimit) { std::vector::iterator found = std::find(selectedSpells.begin(), selectedSpells.end(), cap.mWeakestSpell); if (found != selectedSpells.end()) selectedSpells.erase(found); cap.mMinCost = INT_MAX; for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = spells.find(*weakIt); //int testSchool; //float dummySkillTerm; //calcWeakestSchool(testSpell, actorSkills, testSchool, dummySkillTerm); // Note: if there are multiple spells with the same cost, we pick the first one we found. // So the algorithm depends on the iteration order of the outer loop. if ( // There is a huge bug here. It is not checked that weakestSpell is of the correct school. // As result multiple SchoolCaps could have the same mWeakestSpell. Erasing the weakest spell would then fail if another school // already erased it, and so the number of spells would often exceed the sum of limits. // This bug cannot be fixed without significantly changing the results of the spell autocalc, which will not have been playtested. //testSchool == school && testSpell->mData.mCost < cap.mMinCost) { cap.mMinCost = testSpell->mData.mCost; cap.mWeakestSpell = testSpell->mId; } } } else { cap.mCount += 1; if (cap.mCount == cap.mLimit) cap.mReachedLimit = true; if (spell->mData.mCost < cap.mMinCost) { cap.mWeakestSpell = spell->mId; cap.mMinCost = spell->mData.mCost; } } } return selectedSpells; } bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes) { const std::vector& effects = spell->mEffects.mList; for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get().find("iAutoSpellAttSkillMin")->getInt(); if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)) { assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length); if (actorSkills[effectIt->mSkill] < iAutoSpellAttSkillMin) return false; } if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)) { assert (effectIt->mAttribute >= 0 && effectIt->mAttribute < ESM::Attribute::Length); if (actorAttributes[effectIt->mAttribute] < iAutoSpellAttSkillMin) return false; } } return true; } ESM::Skill::SkillEnum mapSchoolToSkill(int school) { std::map schoolSkillMap; // maps spell school to skill id schoolSkillMap[0] = ESM::Skill::Alteration; schoolSkillMap[1] = ESM::Skill::Conjuration; schoolSkillMap[3] = ESM::Skill::Illusion; schoolSkillMap[2] = ESM::Skill::Destruction; schoolSkillMap[4] = ESM::Skill::Mysticism; schoolSkillMap[5] = ESM::Skill::Restoration; assert(schoolSkillMap.find(school) != schoolSkillMap.end()); return schoolSkillMap[school]; } void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm) { float minChance = std::numeric_limits::max(); const ESM::EffectList& effects = spell->mEffects; for (std::vector::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it) { const ESM::ENAMstruct& effect = *it; float x = static_cast(effect.mDuration); const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) x = std::max(1.f, x); x *= 0.1f * magicEffect->mData.mBaseCost; x *= 0.5f * (effect.mMagnMin + effect.mMagnMax); x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost; if (effect.mRange == ESM::RT_Target) x *= 1.5f; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEffectCostMult")->getFloat(); x *= fEffectCostMult; float s = 2.f * actorSkills[mapSchoolToSkill(magicEffect->mData.mSchool)]; if (s - x < minChance) { minChance = s - x; effectiveSchool = magicEffect->mData.mSchool; skillTerm = s; } } } float calcAutoCastChance(const ESM::Spell *spell, const int *actorSkills, const int *actorAttributes, int effectiveSchool) { if (spell->mData.mType != ESM::Spell::ST_Spell) return 100.f; if (spell->mData.mFlags & ESM::Spell::F_Always) return 100.f; float skillTerm = 0; if (effectiveSchool != -1) skillTerm = 2.f * actorSkills[mapSchoolToSkill(effectiveSchool)]; else calcWeakestSchool(spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this float castChance = skillTerm - spell->mData.mCost + 0.2f * actorAttributes[ESM::Attribute::Willpower] + 0.1f * actorAttributes[ESM::Attribute::Luck]; return castChance; } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/autocalcspell.hpp000066400000000000000000000016731264522266000245400ustar00rootroot00000000000000#ifndef OPENMW_AUTOCALCSPELL_H #define OPENMW_AUTOCALCSPELL_H #include #include #include #include #include namespace MWMechanics { /// Contains algorithm for calculating an NPC's spells based on stats /// @note We might want to move this code to a component later, so the editor can use it for preview purposes std::vector autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race); // Helpers bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes); ESM::Skill::SkillEnum mapSchoolToSkill(int school); void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm); float calcAutoCastChance(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool); } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/character.cpp000066400000000000000000002454711264522266000236420ustar00rootroot00000000000000/* * OpenMW - The completely unofficial reimplementation of Morrowind * * This file (character.cpp) is part of the OpenMW package. * * OpenMW is distributed as free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 3, 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 * version 3 along with this program. If not, see * http://www.gnu.org/licenses/ . */ #include "character.hpp" #include #include "movement.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" #include "security.hpp" #include "actorutil.hpp" #include #include #include #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/statemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" namespace { // Wraps a value to (-PI, PI] void wrap(float& rad) { if (rad>0) rad = std::fmod(rad+osg::PI, 2.0f*osg::PI)-osg::PI; else rad = std::fmod(rad-osg::PI, 2.0f*osg::PI)+osg::PI; } std::string toString(int num) { std::ostringstream stream; stream << num; return stream.str(); } std::string getBestAttack (const ESM::Weapon* weapon) { int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; if (slash >= chop && slash >= thrust) return "slash"; else if (chop >= slash && chop >= thrust) return "chop"; else return "thrust"; } // Converts a movement Run state to its equivalent Walk state. MWMechanics::CharacterState runStateToWalkState (MWMechanics::CharacterState state) { using namespace MWMechanics; CharacterState ret = state; switch (state) { case CharState_RunForward: ret = CharState_WalkForward; break; case CharState_RunBack: ret = CharState_WalkBack; break; case CharState_RunLeft: ret = CharState_WalkLeft; break; case CharState_RunRight: ret = CharState_WalkRight; break; case CharState_SwimRunForward: ret = CharState_SwimWalkForward; break; case CharState_SwimRunBack: ret = CharState_SwimWalkBack; break; case CharState_SwimRunLeft: ret = CharState_SwimWalkLeft; break; case CharState_SwimRunRight: ret = CharState_SwimWalkRight; break; default: break; } return ret; } float getFallDamage(const MWWorld::Ptr& ptr, float fallHeight) { MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat(); if (fallHeight >= fallDistanceMin) { const float acrobaticsSkill = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Acrobatics)); const float jumpSpellBonus = ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Jump).getMagnitude(); const float fallAcroBase = store.find("fFallAcroBase")->getFloat(); const float fallAcroMult = store.find("fFallAcroMult")->getFloat(); const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat(); const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat(); float x = fallHeight - fallDistanceMin; x -= (1.5f * acrobaticsSkill) + jumpSpellBonus; x = std::max(0.0f, x); float a = fallAcroBase + fallAcroMult * (100 - acrobaticsSkill); x = fallDistanceBase + fallDistanceMult * x; x *= a; return x; } return 0.f; } } namespace MWMechanics { struct StateInfo { CharacterState state; const char groupname[32]; }; static const StateInfo sMovementList[] = { { CharState_WalkForward, "walkforward" }, { CharState_WalkBack, "walkback" }, { CharState_WalkLeft, "walkleft" }, { CharState_WalkRight, "walkright" }, { CharState_SwimWalkForward, "swimwalkforward" }, { CharState_SwimWalkBack, "swimwalkback" }, { CharState_SwimWalkLeft, "swimwalkleft" }, { CharState_SwimWalkRight, "swimwalkright" }, { CharState_RunForward, "runforward" }, { CharState_RunBack, "runback" }, { CharState_RunLeft, "runleft" }, { CharState_RunRight, "runright" }, { CharState_SwimRunForward, "swimrunforward" }, { CharState_SwimRunBack, "swimrunback" }, { CharState_SwimRunLeft, "swimrunleft" }, { CharState_SwimRunRight, "swimrunright" }, { CharState_SneakForward, "sneakforward" }, { CharState_SneakBack, "sneakback" }, { CharState_SneakLeft, "sneakleft" }, { CharState_SneakRight, "sneakright" }, { CharState_Jump, "jump" }, { CharState_TurnLeft, "turnleft" }, { CharState_TurnRight, "turnright" }, }; static const StateInfo *sMovementListEnd = &sMovementList[sizeof(sMovementList)/sizeof(sMovementList[0])]; class FindCharState { CharacterState state; public: FindCharState(CharacterState _state) : state(_state) { } bool operator()(const StateInfo &info) const { return info.state == state; } }; static const struct WeaponInfo { WeaponType type; const char shortgroup[16]; const char longgroup[16]; } sWeaponTypeList[] = { { WeapType_HandToHand, "hh", "handtohand" }, { WeapType_OneHand, "1h", "weapononehand" }, { WeapType_TwoHand, "2c", "weapontwohand" }, { WeapType_TwoWide, "2w", "weapontwowide" }, { WeapType_BowAndArrow, "1h", "bowandarrow" }, { WeapType_Crossbow, "crossbow", "crossbow" }, { WeapType_Thrown, "1h", "throwweapon" }, { WeapType_PickProbe, "1h", "pickprobe" }, { WeapType_Spell, "spell", "spellcast" }, }; static const WeaponInfo *sWeaponTypeListEnd = &sWeaponTypeList[sizeof(sWeaponTypeList)/sizeof(sWeaponTypeList[0])]; class FindWeaponType { WeaponType type; public: FindWeaponType(WeaponType _type) : type(_type) { } bool operator()(const WeaponInfo &weap) const { return weap.type == type; } }; std::string CharacterController::chooseRandomGroup (const std::string& prefix, int* num) { int numAnims=0; while (mAnimation->hasAnimation(prefix + toString(numAnims+1))) ++numAnims; int roll = Misc::Rng::rollDice(numAnims) + 1; // [1, numAnims] if (num) *num = roll; return prefix + toString(roll); } void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force) { // hit recoils/knockdown animations handling if(mPtr.getClass().isActor()) { bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock(); if(mHitState == CharState_None) { if ((mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0 || mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0) && mAnimation->hasAnimation("knockout")) { mHitState = CharState_KnockOut; mCurrentHit = "knockout"; mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul); mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true); } else if(knockdown && mAnimation->hasAnimation("knockdown")) { mHitState = CharState_KnockDown; mCurrentHit = "knockdown"; mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0); } else if (recovery) { std::string anim = chooseRandomGroup("hit"); if (mAnimation->hasAnimation(anim)) { mHitState = CharState_Hit; mCurrentHit = anim; mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0); } } else if (block && mAnimation->hasAnimation("shield")) { mHitState = CharState_Block; mCurrentHit = "shield"; MWRender::Animation::AnimPriority priorityBlock (Priority_Hit); priorityBlock[MWRender::Animation::BoneGroup_LeftArm] = Priority_Block; mAnimation->play(mCurrentHit, priorityBlock, MWRender::Animation::BlendMask_All, true, 1, "block start", "block stop", 0.0f, 0); } // Cancel upper body animations if (mHitState == CharState_KnockDown || mHitState == CharState_KnockOut) { if (mUpperBodyState > UpperCharState_WeapEquiped) { mAnimation->disable(mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; } else if (mUpperBodyState > UpperCharState_Nothing && mUpperBodyState < UpperCharState_WeapEquiped) { mAnimation->disable(mCurrentWeapon); mUpperBodyState = UpperCharState_Nothing; } } } else if(!mAnimation->isPlaying(mCurrentHit)) { mCurrentHit.erase(); if (knockdown) mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false); if (recovery) mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false); if (block) mPtr.getClass().getCreatureStats(mPtr).setBlock(false); mHitState = CharState_None; } else if (mHitState == CharState_KnockOut && mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() > 0) { mHitState = CharState_KnockDown; mAnimation->disable(mCurrentHit); mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0); } } const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); if (!mPtr.getClass().isBipedal(mPtr)) weap = sWeaponTypeListEnd; if(force || jump != mJumpState) { bool startAtLoop = (jump == mJumpState); mJumpState = jump; std::string jumpAnimName; MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All; if(mJumpState != JumpState_None) { jumpAnimName = "jump"; if(weap != sWeaponTypeListEnd) { jumpAnimName += weap->shortgroup; if(!mAnimation->hasAnimation(jumpAnimName)) { jumpmask = MWRender::Animation::BlendMask_LowerBody; jumpAnimName = "jump"; } } } if(mJumpState == JumpState_InAir) { mAnimation->disable(mCurrentJump); mCurrentJump = jumpAnimName; if (mAnimation->hasAnimation("jump")) mAnimation->play(mCurrentJump, Priority_Jump, jumpmask, false, 1.0f, (startAtLoop?"loop start":"start"), "stop", 0.0f, ~0ul); } else { mAnimation->disable(mCurrentJump); mCurrentJump.clear(); if (mAnimation->hasAnimation("jump")) mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true, 1.0f, "loop stop", "stop", 0.0f, 0); } } if(force || movement != mMovementState) { mMovementState = movement; std::string movementAnimName; MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All; const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState)); if(movestate != sMovementListEnd) { movementAnimName = movestate->groupname; if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) { movementAnimName += weap->shortgroup; if(!mAnimation->hasAnimation(movementAnimName)) { movemask = MWRender::Animation::BlendMask_LowerBody; movementAnimName = movestate->groupname; } } if(!mAnimation->hasAnimation(movementAnimName)) { std::string::size_type swimpos = movementAnimName.find("swim"); if(swimpos == std::string::npos) { std::string::size_type runpos = movementAnimName.find("run"); if (runpos != std::string::npos) { movementAnimName.replace(runpos, runpos+3, "walk"); if (!mAnimation->hasAnimation(movementAnimName)) movementAnimName.clear(); } else movementAnimName.clear(); } else { movementAnimName.erase(swimpos, 4); if (weap != sWeaponTypeListEnd) { std::string weapMovementAnimName = movementAnimName + weap->shortgroup; if(mAnimation->hasAnimation(weapMovementAnimName)) movementAnimName = weapMovementAnimName; else movemask = MWRender::Animation::BlendMask_LowerBody; } if (!mAnimation->hasAnimation(movementAnimName)) movementAnimName.clear(); } } } /* If we're playing the same animation, restart from the loop start instead of the * beginning. */ int mode = ((movementAnimName == mCurrentMovement) ? 2 : 1); mMovementAnimationControlled = true; mAnimation->disable(mCurrentMovement); mCurrentMovement = movementAnimName; if(!mCurrentMovement.empty()) { bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !MWBase::Environment::get().getWorld()->isFlying(mPtr); // For non-flying creatures, MW uses the Walk animation to calculate the animation velocity // even if we are running. This must be replicated, otherwise the observed speed would differ drastically. std::string anim = mCurrentMovement; mAdjustMovementAnimSpeed = true; if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() && !(mPtr.get()->mBase->mFlags & ESM::Creature::Flies)) { CharacterState walkState = runStateToWalkState(mMovementState); const StateInfo *stateinfo = std::find_if(sMovementList, sMovementListEnd, FindCharState(walkState)); anim = stateinfo->groupname; mMovementAnimSpeed = mAnimation->getVelocity(anim); if (mMovementAnimSpeed <= 1.0f) { // Another bug: when using a fallback animation (e.g. RunForward as fallback to SwimRunForward), // then the equivalent Walk animation will not use a fallback, and if that animation doesn't exist // we will play without any scaling. // Makes the speed attribute of most water creatures totally useless. // And again, this can not be fixed without patching game data. mAdjustMovementAnimSpeed = false; mMovementAnimSpeed = 1.f; } } else { mMovementAnimSpeed = mAnimation->getVelocity(anim); if (mMovementAnimSpeed <= 1.0f) { // The first person anims don't have any velocity to calculate a speed multiplier from. // We use the third person velocities instead. // FIXME: should be pulled from the actual animation, but it is not presently loaded. mMovementAnimSpeed = (isrunning ? 222.857f : 154.064f); mMovementAnimationControlled = false; } } mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false, 1.f, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); } } // idle handled last as it can depend on the other states // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), // the idle animation should be displayed if ((mUpperBodyState != UpperCharState_Nothing || (mMovementState != CharState_None && mMovementState != CharState_TurnLeft && mMovementState != CharState_TurnRight) || mHitState != CharState_None) && !mPtr.getClass().isBipedal(mPtr)) idle = CharState_None; if(force || idle != mIdleState) { mIdleState = idle; std::string idle; MWRender::Animation::AnimPriority idlePriority (Priority_Default); // Only play "idleswim" or "idlesneak" if they exist. Otherwise, fallback to // "idle"+weapon or "idle". if(mIdleState == CharState_IdleSwim && mAnimation->hasAnimation("idleswim")) { idle = "idleswim"; idlePriority = Priority_SwimIdle; } else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation("idlesneak")) { idle = "idlesneak"; idlePriority[MWRender::Animation::BoneGroup_LowerBody] = Priority_SneakIdleLowerBody; } else if(mIdleState != CharState_None) { idle = "idle"; if(weap != sWeaponTypeListEnd) { idle += weap->shortgroup; if(!mAnimation->hasAnimation(idle)) idle = "idle"; } } mAnimation->disable(mCurrentIdle); mCurrentIdle = idle; if(!mCurrentIdle.empty()) mAnimation->play(mCurrentIdle, idlePriority, MWRender::Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, ~0ul, true); } } void getWeaponGroup(WeaponType weaptype, std::string &group) { const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); if(info != sWeaponTypeListEnd) group = info->longgroup; else group.clear(); } MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) { if(stats.getDrawState() == DrawState_Spell) { *weaptype = WeapType_Spell; return inv.end(); } if(stats.getDrawState() == MWMechanics::DrawState_Weapon) { MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if(weapon == inv.end()) *weaptype = WeapType_HandToHand; else { const std::string &type = weapon->getTypeName(); if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) *weaptype = WeapType_PickProbe; else if(type == typeid(ESM::Weapon).name()) { MWWorld::LiveCellRef *ref = weapon->get(); ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType; switch(type) { case ESM::Weapon::ShortBladeOneHand: case ESM::Weapon::LongBladeOneHand: case ESM::Weapon::BluntOneHand: case ESM::Weapon::AxeOneHand: case ESM::Weapon::Arrow: case ESM::Weapon::Bolt: *weaptype = WeapType_OneHand; break; case ESM::Weapon::LongBladeTwoHand: case ESM::Weapon::BluntTwoClose: case ESM::Weapon::AxeTwoHand: *weaptype = WeapType_TwoHand; break; case ESM::Weapon::BluntTwoWide: case ESM::Weapon::SpearTwoWide: *weaptype = WeapType_TwoWide; break; case ESM::Weapon::MarksmanBow: *weaptype = WeapType_BowAndArrow; break; case ESM::Weapon::MarksmanCrossbow: *weaptype = WeapType_Crossbow; break; case ESM::Weapon::MarksmanThrown: *weaptype = WeapType_Thrown; break; } } } return weapon; } return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); } void CharacterController::playDeath(float startpoint, CharacterState death) { switch (death) { case CharState_SwimDeath: mCurrentDeath = "swimdeath"; break; case CharState_DeathKnockDown: mCurrentDeath = "deathknockdown"; break; case CharState_DeathKnockOut: mCurrentDeath = "deathknockout"; break; default: mCurrentDeath = "death" + toString(death - CharState_Death1 + 1); } mDeathState = death; mPtr.getClass().getCreatureStats(mPtr).setDeathAnimation(mDeathState - CharState_Death1); // For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually. // Note that these animations wouldn't actually be visible (due to the Death animation's priority being higher). // However, they could still trigger text keys, such as Hit events, or sounds. mMovementState = CharState_None; mAnimation->disable(mCurrentMovement); mCurrentMovement = ""; mUpperBodyState = UpperCharState_Nothing; mAnimation->disable(mCurrentWeapon); mCurrentWeapon = ""; mHitState = CharState_None; mAnimation->disable(mCurrentHit); mCurrentHit = ""; mIdleState = CharState_None; mAnimation->disable(mCurrentIdle); mCurrentIdle = ""; mJumpState = JumpState_None; mAnimation->disable(mCurrentJump); mCurrentJump = ""; mMovementAnimationControlled = true; mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::BlendMask_All, false, 1.0f, "start", "stop", startpoint, 0); } void CharacterController::playRandomDeath(float startpoint) { if (mPtr == getPlayer()) { // The first-person animations do not include death, so we need to // force-switch to third person before playing the death animation. MWBase::Environment::get().getWorld()->useDeathCamera(); } if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath")) { mDeathState = CharState_SwimDeath; } else if (mHitState == CharState_KnockDown && mAnimation->hasAnimation("deathknockdown")) { mDeathState = CharState_DeathKnockDown; } else if (mHitState == CharState_KnockOut && mAnimation->hasAnimation("deathknockout")) { mDeathState = CharState_DeathKnockOut; } else { int selected=0; chooseRandomGroup("death", &selected); mDeathState = static_cast(CharState_Death1 + (selected-1)); } playDeath(startpoint, mDeathState); } CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) : mPtr(ptr) , mAnimation(anim) , mIdleState(CharState_None) , mMovementState(CharState_None) , mAdjustMovementAnimSpeed(false) , mHasMovedInXY(false) , mMovementAnimationControlled(true) , mDeathState(CharState_None) , mHitState(CharState_None) , mUpperBodyState(UpperCharState_Nothing) , mJumpState(JumpState_None) , mWeaponType(WeapType_None) , mAttackStrength(0.f) , mSkipAnim(false) , mSecondsOfSwimming(0) , mSecondsOfRunning(0) , mTurnAnimationThreshold(0) , mAttackingOrSpell(false) { if(!mAnimation) return; mAnimation->setTextKeyListener(this); const MWWorld::Class &cls = mPtr.getClass(); if(cls.isActor()) { /* Accumulate along X/Y only for now, until we can figure out how we should * handle knockout and death which moves the character down. */ mAnimation->setAccumulation(osg::Vec3f(1.0f, 1.0f, 0.0f)); if (cls.hasInventoryStore(mPtr)) { getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); if (mWeaponType != WeapType_None) { mUpperBodyState = UpperCharState_WeapEquiped; getWeaponGroup(mWeaponType, mCurrentWeapon); } if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) { mAnimation->showWeapons(true); mAnimation->setWeaponGroup(mCurrentWeapon); } mAnimation->showCarriedLeft(updateCarriedLeftVisible(mWeaponType)); } if(!cls.getCreatureStats(mPtr).isDead()) mIdleState = CharState_Idle; else { // Set the death state, but don't play it yet // We will play it in the first frame, but only if no script set the skipAnim flag mDeathState = static_cast(CharState_Death1 + mPtr.getClass().getCreatureStats(mPtr).getDeathAnimation()); } } else { /* Don't accumulate with non-actors. */ mAnimation->setAccumulation(osg::Vec3f(0.f, 0.f, 0.f)); mIdleState = CharState_Idle; } if(mDeathState == CharState_None) refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true); mAnimation->runAnimation(0.f); } CharacterController::~CharacterController() { if (mAnimation) mAnimation->setTextKeyListener(NULL); } void split(const std::string &s, char delim, std::vector &elems) { std::stringstream ss(s); std::string item; while (std::getline(ss, item, delim)) { elems.push_back(item); } } void CharacterController::handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, const std::multimap &map) { const std::string &evt = key->second; if(evt.compare(0, 7, "sound: ") == 0) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); return; } if(evt.compare(0, 10, "soundgen: ") == 0) { std::string soundgen = evt.substr(10); // The event can optionally contain volume and pitch modifiers float volume=1.f, pitch=1.f; if (soundgen.find(" ") != std::string::npos) { std::vector tokens; split(soundgen, ' ', tokens); soundgen = tokens[0]; if (tokens.size() >= 2) { std::stringstream stream; stream << tokens[1]; stream >> volume; } if (tokens.size() >= 3) { std::stringstream stream; stream << tokens[2]; stream >> pitch; } } std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0) { // Don't make foot sounds local for the player, it makes sense to keep them // positioned on the ground. sndMgr->playSound3D(mPtr, sound, volume, pitch, MWBase::SoundManager::Play_TypeFoot, MWBase::SoundManager::Play_NoPlayerLocal); } else sndMgr->playSound3D(mPtr, sound, volume, pitch); } return; } if(evt.compare(0, groupname.size(), groupname) != 0 || evt.compare(groupname.size(), 2, ": ") != 0) { // Not ours, skip it return; } size_t off = groupname.size()+2; size_t len = evt.size() - off; if(evt.compare(off, len, "equip attach") == 0) mAnimation->showWeapons(true); else if(evt.compare(off, len, "unequip detach") == 0) mAnimation->showWeapons(false); else if(evt.compare(off, len, "chop hit") == 0) mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop); else if(evt.compare(off, len, "slash hit") == 0) mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash); else if(evt.compare(off, len, "thrust hit") == 0) mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust); else if(evt.compare(off, len, "hit") == 0) { if (groupname == "attack1") mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop); else if (groupname == "attack2") mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash); else if (groupname == "attack3") mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust); else mPtr.getClass().hit(mPtr, mAttackStrength); } else if (!groupname.empty() && groupname.compare(0, groupname.size()-1, "attack") == 0 && evt.compare(off, len, "start") == 0) { std::multimap::const_iterator hitKey = key; // Not all animations have a hit key defined. If there is none, the hit happens with the start key. bool hasHitKey = false; while (hitKey != map.end()) { if (hitKey->second == groupname + ": hit") { hasHitKey = true; break; } if (hitKey->second == groupname + ": stop") break; ++hitKey; } if (!hasHitKey) { if (groupname == "attack1") mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop); else if (groupname == "attack2") mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash); else if (groupname == "attack3") mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust); } } else if (evt.compare(off, len, "shoot attach") == 0) mAnimation->attachArrow(); else if (evt.compare(off, len, "shoot release") == 0) mAnimation->releaseArrow(mAttackStrength); else if (evt.compare(off, len, "shoot follow attach") == 0) mAnimation->attachArrow(); else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release" // Make sure this key is actually for the RangeType we are casting. The flame atronach has // the same animation for all range types, so there are 3 "release" keys on the same time, one for each range type. && evt.compare(off, len, mAttackType + " release") == 0) { MWBase::Environment::get().getWorld()->castSpell(mPtr); } else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0) mPtr.getClass().block(mPtr); } void CharacterController::updatePtr(const MWWorld::Ptr &ptr) { mPtr = ptr; } void CharacterController::updateIdleStormState(bool inwater) { bool inStormDirection = false; if (MWBase::Environment::get().getWorld()->isInStorm()) { osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); osg::Vec3f characterDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0); inStormDirection = std::acos(stormDirection * characterDirection / (stormDirection.length() * characterDirection.length())) > osg::DegreesToRadians(120.f); } if (inStormDirection && !inwater && mUpperBodyState == UpperCharState_Nothing && mAnimation->hasAnimation("idlestorm")) { float complete = 0; mAnimation->getInfo("idlestorm", &complete); if (complete == 0) mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::BlendMask_RightArm, false, 1.0f, "start", "loop start", 0.0f, 0); else if (complete == 1) mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::BlendMask_RightArm, false, 1.0f, "loop start", "loop stop", 0.0f, ~0ul); } else { if (mUpperBodyState == UpperCharState_Nothing) { if (mAnimation->isPlaying("idlestorm")) { if (mAnimation->getCurrentTime("idlestorm") < mAnimation->getTextKeyTime("idlestorm: loop stop")) { mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::BlendMask_RightArm, true, 1.0f, "loop stop", "stop", 0.0f, 0); } } } else mAnimation->disable("idlestorm"); } } void CharacterController::castSpell(const std::string &spellid) { static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.get().find(spellid); const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0); const ESM::MagicEffect *effect; effect = store.get().find(effectentry.mEffectID); const ESM::Static* castStatic; if (!effect->mCasting.empty()) castStatic = store.get().find (effect->mCasting); else castStatic = store.get().find ("VFX_DefaultCast"); mAnimation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(!effect->mCastSound.empty()) sndMgr->playSound3D(mPtr, effect->mCastSound, 1.0f, 1.0f); else sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f); } bool CharacterController::updateCreatureState() { const MWWorld::Class &cls = mPtr.getClass(); CreatureStats &stats = cls.getCreatureStats(mPtr); WeaponType weapType = WeapType_None; if(stats.getDrawState() == DrawState_Weapon) weapType = WeapType_HandToHand; else if (stats.getDrawState() == DrawState_Spell) weapType = WeapType_Spell; if (weapType != mWeaponType) { mWeaponType = weapType; if (mAnimation->isPlaying(mCurrentWeapon)) mAnimation->disable(mCurrentWeapon); } if(mAttackingOrSpell) { if(mUpperBodyState == UpperCharState_Nothing && mHitState == CharState_None) { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); std::string startKey = "start"; std::string stopKey = "stop"; if (weapType == WeapType_Spell) { const std::string spellid = stats.getSpells().getSelectedSpell(); if (!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) { castSpell(spellid); if (!mAnimation->hasAnimation("spellcast")) MWBase::Environment::get().getWorld()->castSpell(mPtr); // No "release" text key to use, so cast immediately else { const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellid); const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0); switch(effectentry.mRange) { case 0: mAttackType = "self"; break; case 1: mAttackType = "touch"; break; case 2: mAttackType = "target"; break; } startKey = mAttackType + " " + startKey; stopKey = mAttackType + " " + stopKey; mCurrentWeapon = "spellcast"; } } else mCurrentWeapon = ""; } if (weapType != WeapType_Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation { int roll = Misc::Rng::rollDice(3); // [0, 2] if (roll == 0) mCurrentWeapon = "attack1"; else if (roll == 1) mCurrentWeapon = "attack2"; else mCurrentWeapon = "attack3"; } if (!mCurrentWeapon.empty()) { mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::BlendMask_All, true, 1, startKey, stopKey, 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability()); } } mAttackingOrSpell = false; } bool animPlaying = mAnimation->getInfo(mCurrentWeapon); if (!animPlaying) mUpperBodyState = UpperCharState_Nothing; return false; } bool CharacterController::updateCarriedLeftVisible(WeaponType weaptype) const { // Shields/torches shouldn't be visible during any operation involving two hands // There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop", // but they are also present in weapon drawing animation. switch (weaptype) { case WeapType_Spell: case WeapType_BowAndArrow: case WeapType_Crossbow: case WeapType_HandToHand: case WeapType_TwoHand: case WeapType_TwoWide: return false; default: return true; } } bool CharacterController::updateWeaponState() { const MWWorld::Class &cls = mPtr.getClass(); CreatureStats &stats = cls.getCreatureStats(mPtr); WeaponType weaptype = WeapType_None; if(stats.getDrawState() == DrawState_Weapon) weaptype = WeapType_HandToHand; else if (stats.getDrawState() == DrawState_Spell) weaptype = WeapType_Spell; const bool isWerewolf = cls.isNpc() && cls.getNpcStats(mPtr).isWerewolf(); std::string soundid; if (mPtr.getClass().hasInventoryStore(mPtr)) { MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype); if(weapon != inv.end() && !(weaptype == WeapType_None && mWeaponType == WeapType_Spell)) { soundid = (weaptype == WeapType_None) ? weapon->getClass().getDownSoundId(*weapon) : weapon->getClass().getUpSoundId(*weapon); } } MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; bool forcestateupdate = false; if(weaptype != mWeaponType && mHitState != CharState_KnockDown && mHitState != CharState_KnockOut && mHitState != CharState_Hit) { forcestateupdate = true; mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype)); std::string weapgroup; if(weaptype == WeapType_None) { if ((!isWerewolf || mWeaponType != WeapType_Spell)) { getWeaponGroup(mWeaponType, weapgroup); mAnimation->play(weapgroup, priorityWeapon, MWRender::Animation::BlendMask_All, true, 1.0f, "unequip start", "unequip stop", 0.0f, 0); mUpperBodyState = UpperCharState_UnEquipingWeap; } } else { getWeaponGroup(weaptype, weapgroup); mAnimation->showWeapons(false); mAnimation->setWeaponGroup(weapgroup); mAnimation->play(weapgroup, priorityWeapon, MWRender::Animation::BlendMask_All, true, 1.0f, "equip start", "equip stop", 0.0f, 0); mUpperBodyState = UpperCharState_EquipingWeap; if(isWerewolf) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Sound *sound = store.get().searchRandom("WolfEquip"); if(sound) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f); } } } if(!soundid.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f); } mWeaponType = weaptype; getWeaponGroup(mWeaponType, mCurrentWeapon); } if(isWerewolf) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && mHasMovedInXY && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mWeaponType == WeapType_None) { if(!sndMgr->getSoundPlaying(mPtr, "WolfRun")) sndMgr->playSound3D(mPtr, "WolfRun", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); } else sndMgr->stopSound3D(mPtr, "WolfRun"); } // Cancel attack if we no longer have ammunition bool ammunition = true; bool isWeapon = false; float weapSpeed = 1.f; if (mPtr.getClass().hasInventoryStore(mPtr)) { MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype); isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name()); if(isWeapon) weapSpeed = weapon->get()->mBase->mData.mSpeed; MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (mWeaponType == WeapType_Crossbow) ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt); else if (mWeaponType == WeapType_BowAndArrow) ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Arrow); if (!ammunition && mUpperBodyState > UpperCharState_WeapEquiped) { mAnimation->disable(mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; } } float complete; bool animPlaying; if(mAttackingOrSpell) { if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block)) { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackType.clear(); if(mWeaponType == WeapType_Spell) { // Unset casting flag, otherwise pressing the mouse button down would // continue casting every frame if there is no animation mAttackingOrSpell = false; if (mPtr == getPlayer()) { MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false); } const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); // For the player, set the spell we want to cast // This has to be done at the start of the casting animation, // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation) if (mPtr == getPlayer()) { std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell(); stats.getSpells().setSelectedSpell(selectedSpell); } std::string spellid = stats.getSpells().getSelectedSpell(); if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) { castSpell(spellid); const ESM::Spell *spell = store.get().find(spellid); const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0); const ESM::MagicEffect *effect; effect = store.get().find(effectentry.mEffectID); const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Hands"); if (mAnimation->getNode("Bip01 L Hand")) mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 L Hand", effect->mParticle); if (mAnimation->getNode("Bip01 R Hand")) mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 R Hand", effect->mParticle); switch(effectentry.mRange) { case 0: mAttackType = "self"; break; case 1: mAttackType = "touch"; break; case 2: mAttackType = "target"; break; } mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, true, weapSpeed, mAttackType+" start", mAttackType+" stop", 0.0f, 0); mUpperBodyState = UpperCharState_CastingSpell; } if (mPtr.getClass().hasInventoryStore(mPtr)) { MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); if (inv.getSelectedEnchantItem() != inv.end()) { // Enchanted items cast immediately (no animation) MWBase::Environment::get().getWorld()->castSpell(mPtr); } } } else if(mWeaponType == WeapType_PickProbe) { MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::Ptr item = *weapon; // TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes. MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject(); std::string resultMessage, resultSound; if(!target.isEmpty()) { if(item.getTypeName() == typeid(ESM::Lockpick).name()) Security(mPtr).pickLock(target, item, resultMessage, resultSound); else if(item.getTypeName() == typeid(ESM::Probe).name()) Security(mPtr).probeTrap(target, item, resultMessage, resultSound); } mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, true, 1.0f, "start", "stop", 0.0, 0); mUpperBodyState = UpperCharState_FollowStartToFollowStop; if(!resultMessage.empty()) MWBase::Environment::get().getWindowManager()->messageBox(resultMessage); if(!resultSound.empty()) MWBase::Environment::get().getSoundManager()->playSound(resultSound, 1.0f, 1.0f); } else if (ammunition) { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) mAttackType = "shoot"; else { if(isWeapon && mPtr == getPlayer() && Settings::Manager::getBool("best attack", "Game")) { MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight); mAttackType = getBestAttack(weapon->get()->mBase); } else determineAttackType(); } mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false, weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; } } animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && mHitState != CharState_KnockDown) mAttackStrength = complete; } else { animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && mHitState != CharState_KnockDown) { float attackStrength = complete; if (!mPtr.getClass().isNpc()) { // most creatures don't actually have an attack wind-up animation, so use a uniform random value // (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings) // Note: vanilla MW uses a random value for *all* non-player actors, but we probably don't need to go that far. attackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability()); } if(mWeaponType != WeapType_Crossbow && mWeaponType != WeapType_BowAndArrow) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(isWerewolf) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Sound *sound = store.get().searchRandom("WolfSwing"); if(sound) sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f); } else { std::string sound = "SwishM"; if(attackStrength < 0.5f) sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack else if(attackStrength < 1.0f) sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack else sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack } } mAttackStrength = attackStrength; mAnimation->disable(mCurrentWeapon); mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false, weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 1.0f-complete, 0); complete = 0.f; mUpperBodyState = UpperCharState_MaxAttackToMinHit; } else if (mHitState == CharState_KnockDown) { if (mUpperBodyState > UpperCharState_WeapEquiped) mUpperBodyState = UpperCharState_WeapEquiped; mAnimation->disable(mCurrentWeapon); } } mAnimation->setPitchFactor(0.f); if (mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) { switch (mUpperBodyState) { case UpperCharState_StartToMinAttack: mAnimation->setPitchFactor(complete); break; case UpperCharState_MinAttackToMaxAttack: case UpperCharState_MaxAttackToMinHit: case UpperCharState_MinHitToHit: mAnimation->setPitchFactor(1.f); break; case UpperCharState_FollowStartToFollowStop: if (animPlaying) mAnimation->setPitchFactor(1.f-complete); break; default: break; } } else if (mWeaponType == WeapType_Crossbow) { switch (mUpperBodyState) { case UpperCharState_EquipingWeap: mAnimation->setPitchFactor(complete); break; case UpperCharState_UnEquipingWeap: mAnimation->setPitchFactor(1.f-complete); break; case UpperCharState_WeapEquiped: case UpperCharState_StartToMinAttack: case UpperCharState_MinAttackToMaxAttack: case UpperCharState_MaxAttackToMinHit: case UpperCharState_MinHitToHit: case UpperCharState_FollowStartToFollowStop: mAnimation->setPitchFactor(1.f); break; default: break; } } if(!animPlaying) { if(mUpperBodyState == UpperCharState_EquipingWeap || mUpperBodyState == UpperCharState_FollowStartToFollowStop || mUpperBodyState == UpperCharState_CastingSpell) { if (ammunition && mWeaponType == WeapType_Crossbow) mAnimation->attachArrow(); mUpperBodyState = UpperCharState_WeapEquiped; } else if(mUpperBodyState == UpperCharState_UnEquipingWeap) mUpperBodyState = UpperCharState_Nothing; } else if(complete >= 1.0f) { std::string start, stop; switch(mUpperBodyState) { case UpperCharState_StartToMinAttack: start = mAttackType+" min attack"; stop = mAttackType+" max attack"; mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; case UpperCharState_MinAttackToMaxAttack: //hack to avoid body pos desync when jumping/sneaking in 'max attack' state if(!mAnimation->isPlaying(mCurrentWeapon)) mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false, 0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0); break; case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { start = mAttackType+" min hit"; stop = mAttackType+" release"; } else { start = mAttackType+" min hit"; stop = mAttackType+" hit"; } mUpperBodyState = UpperCharState_MinHitToHit; break; case UpperCharState_MinHitToHit: if(mAttackType == "shoot") { start = mAttackType+" follow start"; stop = mAttackType+" follow stop"; } else { float str = mAttackStrength; start = mAttackType+((str < 0.5f) ? " small follow start" : (str < 1.0f) ? " medium follow start" : " large follow start"); stop = mAttackType+((str < 0.5f) ? " small follow stop" : (str < 1.0f) ? " medium follow stop" : " large follow stop"); } mUpperBodyState = UpperCharState_FollowStartToFollowStop; break; default: break; } if(!start.empty()) { mAnimation->disable(mCurrentWeapon); if (mUpperBodyState == UpperCharState_FollowStartToFollowStop) mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, true, weapSpeed, start, stop, 0.0f, 0); else mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false, weapSpeed, start, stop, 0.0f, 0); } } if (mPtr.getClass().hasInventoryStore(mPtr)) { MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && updateCarriedLeftVisible(mWeaponType)) { mAnimation->play("torch", Priority_Torch, MWRender::Animation::BlendMask_LeftArm, false, 1.0f, "start", "stop", 0.0f, (~(size_t)0), true); } else if (mAnimation->isPlaying("torch")) { mAnimation->disable("torch"); } } mAnimation->setAccurateAiming(mUpperBodyState > UpperCharState_WeapEquiped); return forcestateupdate; } void CharacterController::update(float duration) { MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Class &cls = mPtr.getClass(); osg::Vec3f movement(0.f, 0.f, 0.f); float speed = 0.f; updateMagicEffects(); if(!cls.isActor()) { if(mAnimQueue.size() > 1) { if(mAnimation->isPlaying(mAnimQueue.front().first) == false) { mAnimation->disable(mAnimQueue.front().first); mAnimQueue.pop_front(); mAnimation->play(mAnimQueue.front().first, Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, mAnimQueue.front().second); } } } else if(!cls.getCreatureStats(mPtr).isDead()) { bool onground = world->isOnGround(mPtr); bool inwater = world->isSwimming(mPtr); bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool flying = world->isFlying(mPtr); // Can't run while flying (see speed formula in Npc/Creature::getSpeed) bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !flying; CreatureStats &stats = cls.getCreatureStats(mPtr); //Force Jump Logic bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || std::abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5); if(!inwater && !flying) { //Force Jump if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceJump)) { if(onground) { cls.getMovementSettings(mPtr).mPosition[2] = 1; } else cls.getMovementSettings(mPtr).mPosition[2] = 0; } //Force Move Jump, only jump if they're otherwise moving if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceMoveJump) && isMoving) { if(onground) { cls.getMovementSettings(mPtr).mPosition[2] = 1; } else cls.getMovementSettings(mPtr).mPosition[2] = 0; } } osg::Vec3f vec(cls.getMovementSettings(mPtr).asVec3()); vec.normalize(); if(mHitState != CharState_None && mJumpState == JumpState_None) vec = osg::Vec3f(0.f, 0.f, 0.f); osg::Vec3f rot = cls.getRotationVector(mPtr); speed = cls.getSpeed(mPtr); vec.x() *= speed; vec.y() *= speed; CharacterState movestate = CharState_None; CharacterState idlestate = CharState_SpecialIdle; JumpingState jumpstate = JumpState_None; bool forcestateupdate = false; mHasMovedInXY = std::abs(vec.x())+std::abs(vec.y()) > 0.0f; isrunning = isrunning && mHasMovedInXY; // advance athletics if(mHasMovedInXY && mPtr == getPlayer()) { if(inwater) { mSecondsOfSwimming += duration; while(mSecondsOfSwimming > 1) { cls.skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 1); mSecondsOfSwimming -= 1; } } else if(isrunning) { mSecondsOfRunning += duration; while(mSecondsOfRunning > 1) { cls.skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 0); mSecondsOfRunning -= 1; } } } // reduce fatigue const MWWorld::Store &gmst = world->getStore().get(); float fatigueLoss = 0; static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->getFloat(); static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->getFloat(); static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->getFloat(); static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->getFloat(); static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->getFloat(); static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->getFloat(); static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->getFloat(); static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->getFloat(); const float encumbrance = cls.getEncumbrance(mPtr) / cls.getCapacity(mPtr); if (encumbrance < 1) { if (sneak) fatigueLoss = fFatigueSneakBase + encumbrance * fFatigueSneakMult; else { if (inwater) { if (!isrunning) fatigueLoss = fFatigueSwimWalkBase + encumbrance * fFatigueSwimWalkMult; else fatigueLoss = fFatigueSwimRunBase + encumbrance * fFatigueSwimRunMult; } if (isrunning) fatigueLoss = fFatigueRunBase + encumbrance * fFatigueRunMult; } } fatigueLoss *= duration; DynamicStat fatigue = cls.getCreatureStats(mPtr).getFatigue(); fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss, fatigue.getCurrent() < 0); cls.getCreatureStats(mPtr).setFatigue(fatigue); if(sneak || inwater || flying) vec.z() = 0.0f; if (inwater || flying) cls.getCreatureStats(mPtr).land(); bool inJump = true; if(!onground && !flying && !inwater) { // In the air (either getting up —ascending part of jump— or falling). if (world->isSlowFalling(mPtr)) { // SlowFalling spell effect is active, do not keep previous fall height cls.getCreatureStats(mPtr).land(); } forcestateupdate = (mJumpState != JumpState_InAir); jumpstate = JumpState_InAir; static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat(); static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat(); float factor = fJumpMoveBase + fJumpMoveMult * mPtr.getClass().getSkill(mPtr, ESM::Skill::Acrobatics)/100.f; factor = std::min(1.f, factor); vec.x() *= factor; vec.y() *= factor; vec.z() = 0.0f; } else if(vec.z() > 0.0f && mJumpState == JumpState_None) { // Started a jump. float z = cls.getJump(mPtr); if (z > 0) { if(vec.x() == 0 && vec.y() == 0) vec = osg::Vec3f(0.0f, 0.0f, z); else { osg::Vec3f lat (vec.x(), vec.y(), 0.0f); lat.normalize(); vec = osg::Vec3f(lat.x(), lat.y(), 1.0f) * z * 0.707f; } // advance acrobatics if (mPtr == getPlayer()) cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0); // decrease fatigue const MWWorld::Store &gmst = world->getStore().get(); const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat(); const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat(); float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr); if (normalizedEncumbrance > 1) normalizedEncumbrance = 1; const float fatigueDecrease = fatigueJumpBase + (1 - normalizedEncumbrance) * fatigueJumpMult; DynamicStat fatigue = cls.getCreatureStats(mPtr).getFatigue(); fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease); cls.getCreatureStats(mPtr).setFatigue(fatigue); } } else if(mJumpState == JumpState_InAir) { forcestateupdate = true; jumpstate = JumpState_Landing; vec.z() = 0.0f; float height = cls.getCreatureStats(mPtr).land(); float healthLost = getFallDamage(mPtr, height); if (healthLost > 0.0f) { const float fatigueTerm = cls.getCreatureStats(mPtr).getFatigueTerm(); // inflict fall damages DynamicStat health = cls.getCreatureStats(mPtr).getHealth(); float realHealthLost = static_cast(healthLost * (1.0f - 0.25f * fatigueTerm)); health.setCurrent(health.getCurrent() - realHealthLost); cls.getCreatureStats(mPtr).setHealth(health); cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), true); const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); if (healthLost > (acrobaticsSkill * fatigueTerm)) { cls.getCreatureStats(mPtr).setKnockedDown(true); } else { // report acrobatics progression if (mPtr == getPlayer()) cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); } } } else { jumpstate = JumpState_None; vec.z() = 0.0f; inJump = false; if(std::abs(vec.x()/2.0f) > std::abs(vec.y())) { if(vec.x() > 0.0f) movestate = (inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight))); else if(vec.x() < 0.0f) movestate = (inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft))); } else if(vec.y() != 0.0f) { if(vec.y() > 0.0f) movestate = (inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward))); else if(vec.y() < 0.0f) movestate = (inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); } else if(rot.z() != 0.0f && !inwater && !sneak && !MWBase::Environment::get().getWorld()->isFirstPerson()) { if(rot.z() > 0.0f) movestate = CharState_TurnRight; else if(rot.z() < 0.0f) movestate = CharState_TurnLeft; } } mTurnAnimationThreshold -= duration; if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft) mTurnAnimationThreshold = 0.05f; else if (movestate == CharState_None && (mMovementState == CharState_TurnRight || mMovementState == CharState_TurnLeft) && mTurnAnimationThreshold > 0) { movestate = mMovementState; } if (onground) cls.getCreatureStats(mPtr).land(); if(movestate != CharState_None) clearAnimQueue(); if(mAnimQueue.empty() || inwater || sneak) { idlestate = (inwater ? CharState_IdleSwim : (sneak && !inJump ? CharState_IdleSneak : CharState_Idle)); } else if(mAnimQueue.size() > 1) { if(mAnimation->isPlaying(mAnimQueue.front().first) == false) { mAnimation->disable(mAnimQueue.front().first); mAnimQueue.pop_front(); mAnimation->play(mAnimQueue.front().first, Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, mAnimQueue.front().second); } } if (!mSkipAnim) { // bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used. if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr)) forcestateupdate = updateWeaponState() || forcestateupdate; else forcestateupdate = updateCreatureState() || forcestateupdate; refreshCurrentAnims(idlestate, movestate, jumpstate, forcestateupdate); updateIdleStormState(inwater); } if (inJump) mMovementAnimationControlled = false; if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight) { if (duration > 0) mAnimation->adjustSpeedMult(mCurrentMovement, std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI))); } else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed) { float speedmult = speed / mMovementAnimSpeed; mAnimation->adjustSpeedMult(mCurrentMovement, speedmult); } if (!mSkipAnim) { if(mHitState != CharState_KnockDown && mHitState != CharState_KnockOut) { world->rotateObject(mPtr, rot.x(), rot.y(), rot.z(), true); } else //avoid z-rotating for knockdown world->rotateObject(mPtr, rot.x(), rot.y(), 0.0f, true); if (!mMovementAnimationControlled) world->queueMovement(mPtr, vec); } else // We must always queue movement, even if there is none, to apply gravity. world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f)); movement = vec; cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = 0; // Can't reset jump state (mPosition[2]) here; we don't know for sure whether the PhysicSystem will actually handle it in this frame // due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled. if (!mSkipAnim) updateHeadTracking(duration); } else if(cls.getCreatureStats(mPtr).isDead()) { // initial start of death animation for actors that started the game as dead // not done in constructor since we need to give scripts a chance to set the mSkipAnim flag if (!mSkipAnim && mDeathState != CharState_None && mCurrentDeath.empty()) { playDeath(1.f, mDeathState); } world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f)); } osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim ? 0.f : duration); if(duration > 0.0f) moved /= duration; else moved = osg::Vec3f(0.f, 0.f, 0.f); // Ensure we're moving in generally the right direction... if(speed > 0.f) { float l = moved.length(); if((movement.x() < 0.0f && movement.x() < moved.x()*2.0f) || (movement.x() > 0.0f && movement.x() > moved.x()*2.0f)) moved.x() = movement.x(); if((movement.y() < 0.0f && movement.y() < moved.y()*2.0f) || (movement.y() > 0.0f && movement.y() > moved.y()*2.0f)) moved.y() = movement.y(); if((movement.z() < 0.0f && movement.z() < moved.z()*2.0f) || (movement.z() > 0.0f && movement.z() > moved.z()*2.0f)) moved.z() = movement.z(); // but keep the original speed float newLength = moved.length(); if (newLength > 0) moved *= (l / newLength); } if (mSkipAnim) mAnimation->updateEffects(duration); // Update movement if(mMovementAnimationControlled && mPtr.getClass().isActor()) world->queueMovement(mPtr, moved); mSkipAnim = false; mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead()); } bool CharacterController::playGroup(const std::string &groupname, int mode, int count) { if(!mAnimation || !mAnimation->hasAnimation(groupname)) { std::cerr<< "Animation "<disable(mCurrentIdle); mCurrentIdle.clear(); mIdleState = CharState_SpecialIdle; mAnimation->play(groupname, Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); } else if(mode == 0) { if (!mAnimQueue.empty()) mAnimation->stopLooping(mAnimQueue.front().first); mAnimQueue.resize(1); mAnimQueue.push_back(std::make_pair(groupname, count-1)); } } return true; } void CharacterController::skipAnim() { mSkipAnim = true; } bool CharacterController::isAnimPlaying(const std::string &groupName) { if(mAnimation == NULL) return false; return mAnimation->isPlaying(groupName); } void CharacterController::clearAnimQueue() { if(!mAnimQueue.empty()) mAnimation->disable(mAnimQueue.front().first); mAnimQueue.clear(); } void CharacterController::forceStateUpdate() { if(!mAnimation) return; clearAnimQueue(); refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true); if(mDeathState != CharState_None) { playRandomDeath(); } mAnimation->runAnimation(0.f); } bool CharacterController::kill() { if( isDead() ) { if( mPtr == getPlayer() && !isAnimPlaying(mCurrentDeath) ) { //player's death animation is over MWBase::Environment::get().getStateManager()->askLoadRecent(); } return false; } playRandomDeath(); mAnimation->disable(mCurrentIdle); mIdleState = CharState_None; mCurrentIdle.clear(); // Play Death Music if it was the player dying if(mPtr == getPlayer()) MWBase::Environment::get().getSoundManager()->streamMusic("Special/MW_Death.mp3"); return true; } void CharacterController::resurrect() { if(mDeathState == CharState_None) return; if(mAnimation) mAnimation->disable(mCurrentDeath); mCurrentDeath.clear(); mDeathState = CharState_None; } void CharacterController::updateContinuousVfx() { // Keeping track of when to stop a continuous VFX seems to be very difficult to do inside the spells code, // as it's extremely spread out (ActiveSpells, Spells, InventoryStore effects, etc...) so we do it here. // Stop any effects that are no longer active std::vector effects; mAnimation->getLoopingEffects(effects); for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) { if (mPtr.getClass().getCreatureStats(mPtr).isDead() || mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(*it)).getMagnitude() <= 0) mAnimation->removeEffect(*it); } } void CharacterController::updateMagicEffects() { if (!mPtr.getClass().isActor()) return; float alpha = 1.f; if (mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude()) { if (mPtr == getPlayer()) alpha = 0.4f; else alpha = 0.f; } float chameleon = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude(); if (chameleon) { alpha *= std::max(0.2f, (100.f - chameleon)/100.f); } mAnimation->setAlpha(alpha); bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0.0f; mAnimation->setVampire(vampire); float light = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Light).getMagnitude(); mAnimation->setLightEffect(light); } void CharacterController::determineAttackType() { float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition; if (move[1] && !move[0]) // forward-backward mAttackType = "thrust"; else if (move[0] && !move[1]) //sideway mAttackType = "slash"; else mAttackType = "chop"; } bool CharacterController::isReadyToBlock() const { return updateCarriedLeftVisible(mWeaponType); } bool CharacterController::isKnockedOut() const { return mHitState == CharState_KnockOut; } bool CharacterController::isSneaking() const { return mIdleState == CharState_IdleSneak || mMovementState == CharState_SneakForward || mMovementState == CharState_SneakBack || mMovementState == CharState_SneakLeft || mMovementState == CharState_SneakRight; } void CharacterController::setAttackingOrSpell(bool attackingOrSpell) { mAttackingOrSpell = attackingOrSpell; } bool CharacterController::readyToPrepareAttack() const { return (mHitState == CharState_None || mHitState == CharState_Block) && mUpperBodyState <= UpperCharState_WeapEquiped; } bool CharacterController::readyToStartAttack() const { if (mHitState != CharState_None && mHitState != CharState_Block) return false; if (mPtr.getClass().hasInventoryStore(mPtr) || mPtr.getClass().isBipedal(mPtr)) return mUpperBodyState == UpperCharState_WeapEquiped; else return mUpperBodyState == UpperCharState_Nothing; } float CharacterController::getAttackStrength() const { return mAttackStrength; } void CharacterController::setActive(bool active) { mAnimation->setActive(active); } void CharacterController::setHeadTrackTarget(const MWWorld::ConstPtr &target) { mHeadTrackTarget = target; } void CharacterController::updateHeadTracking(float duration) { const osg::Node* head = mAnimation->getNode("Bip01 Head"); if (!head) return; float zAngleRadians = 0.f; float xAngleRadians = 0.f; if (!mHeadTrackTarget.isEmpty()) { osg::MatrixList mats = head->getWorldMatrices(); if (mats.empty()) return; osg::Matrixf mat = mats[0]; osg::Vec3f headPos = mat.getTrans(); osg::Vec3f direction; if (const MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget)) { const osg::Node* node = anim->getNode("Head"); if (node == NULL) node = anim->getNode("Bip01 Head"); if (node != NULL) { osg::MatrixList mats = node->getWorldMatrices(); if (mats.size()) direction = mats[0].getTrans() - headPos; } else // no head node to look at, fall back to look at center of collision box direction = MWBase::Environment::get().getWorld()->aimToTarget(mPtr, mHeadTrackTarget); } direction.normalize(); if (!mPtr.getRefData().getBaseNode()) return; const osg::Vec3f actorDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0); zAngleRadians = std::atan2(direction.x(), direction.y()) - std::atan2(actorDirection.x(), actorDirection.y()); xAngleRadians = -std::asin(direction.z()); wrap(zAngleRadians); wrap(xAngleRadians); xAngleRadians = std::min(xAngleRadians, osg::DegreesToRadians(40.f)); xAngleRadians = std::max(xAngleRadians, osg::DegreesToRadians(-40.f)); zAngleRadians = std::min(zAngleRadians, osg::DegreesToRadians(30.f)); zAngleRadians = std::max(zAngleRadians, osg::DegreesToRadians(-30.f)); } float factor = duration*5; factor = std::min(factor, 1.f); xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * (-xAngleRadians); zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * (-zAngleRadians); mAnimation->setHeadPitch(xAngleRadians); mAnimation->setHeadYaw(zAngleRadians); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/character.hpp000066400000000000000000000140401264522266000236310ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_CHARACTER_HPP #define GAME_MWMECHANICS_CHARACTER_HPP #include #include #include "../mwworld/ptr.hpp" #include "../mwrender/animation.hpp" namespace MWWorld { class ContainerStoreIterator; class InventoryStore; } namespace MWRender { class Animation; } namespace MWMechanics { struct Movement; class CreatureStats; enum Priority { Priority_Default, Priority_WeaponLowerBody, Priority_SneakIdleLowerBody, Priority_SwimIdle, Priority_Jump, Priority_Movement, Priority_Hit, Priority_Weapon, Priority_Block, Priority_Knockdown, Priority_Torch, Priority_Storm, Priority_Death, Num_Priorities }; enum CharacterState { CharState_None, CharState_SpecialIdle, CharState_Idle, CharState_Idle2, CharState_Idle3, CharState_Idle4, CharState_Idle5, CharState_Idle6, CharState_Idle7, CharState_Idle8, CharState_Idle9, CharState_IdleSwim, CharState_IdleSneak, CharState_WalkForward, CharState_WalkBack, CharState_WalkLeft, CharState_WalkRight, CharState_SwimWalkForward, CharState_SwimWalkBack, CharState_SwimWalkLeft, CharState_SwimWalkRight, CharState_RunForward, CharState_RunBack, CharState_RunLeft, CharState_RunRight, CharState_SwimRunForward, CharState_SwimRunBack, CharState_SwimRunLeft, CharState_SwimRunRight, CharState_SneakForward, CharState_SneakBack, CharState_SneakLeft, CharState_SneakRight, CharState_TurnLeft, CharState_TurnRight, CharState_Jump, CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5, CharState_SwimDeath, CharState_DeathKnockDown, CharState_DeathKnockOut, CharState_Hit, CharState_KnockDown, CharState_KnockOut, CharState_Block }; enum WeaponType { WeapType_None, WeapType_HandToHand, WeapType_OneHand, WeapType_TwoHand, WeapType_TwoWide, WeapType_BowAndArrow, WeapType_Crossbow, WeapType_Thrown, WeapType_PickProbe, WeapType_Spell }; enum UpperBodyCharacterState { UpperCharState_Nothing, UpperCharState_EquipingWeap, UpperCharState_UnEquipingWeap, UpperCharState_WeapEquiped, UpperCharState_StartToMinAttack, UpperCharState_MinAttackToMaxAttack, UpperCharState_MaxAttackToMinHit, UpperCharState_MinHitToHit, UpperCharState_FollowStartToFollowStop, UpperCharState_CastingSpell }; enum JumpingState { JumpState_None, JumpState_InAir, JumpState_Landing }; class CharacterController : public MWRender::Animation::TextKeyListener { MWWorld::Ptr mPtr; MWRender::Animation *mAnimation; typedef std::deque > AnimationQueue; AnimationQueue mAnimQueue; CharacterState mIdleState; std::string mCurrentIdle; CharacterState mMovementState; std::string mCurrentMovement; float mMovementAnimSpeed; bool mAdjustMovementAnimSpeed; bool mHasMovedInXY; bool mMovementAnimationControlled; CharacterState mDeathState; std::string mCurrentDeath; CharacterState mHitState; std::string mCurrentHit; UpperBodyCharacterState mUpperBodyState; JumpingState mJumpState; std::string mCurrentJump; WeaponType mWeaponType; std::string mCurrentWeapon; float mAttackStrength; bool mSkipAnim; // counted for skill increase float mSecondsOfSwimming; float mSecondsOfRunning; MWWorld::ConstPtr mHeadTrackTarget; float mTurnAnimationThreshold; // how long to continue playing turning animation after actor stopped turning std::string mAttackType; // slash, chop or thrust bool mAttackingOrSpell; void determineAttackType(); void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); void clearAnimQueue(); bool updateWeaponState(); bool updateCreatureState(); void updateIdleStormState(bool inwater); void updateHeadTracking(float duration); void castSpell(const std::string& spellid); void updateMagicEffects(); void playDeath(float startpoint, CharacterState death); void playRandomDeath(float startpoint = 0.0f); /// choose a random animation group with \a prefix and numeric suffix /// @param num if non-NULL, the chosen animation number will be written here std::string chooseRandomGroup (const std::string& prefix, int* num = NULL); bool updateCarriedLeftVisible(WeaponType weaptype) const; public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); virtual void handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, const std::multimap& map); // Be careful when to call this, see comment in Actors void updateContinuousVfx(); void updatePtr(const MWWorld::Ptr &ptr); void update(float duration); bool playGroup(const std::string &groupname, int mode, int count); void skipAnim(); bool isAnimPlaying(const std::string &groupName); /// @return false if the character has already been killed before bool kill(); void resurrect(); bool isDead() const { return mDeathState != CharState_None; } void forceStateUpdate(); bool isReadyToBlock() const; bool isKnockedOut() const; bool isSneaking() const; void setAttackingOrSpell(bool attackingOrSpell); bool readyToPrepareAttack() const; bool readyToStartAttack() const; float getAttackStrength() const; /// @see Animation::setActive void setActive(bool active); /// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr. void setHeadTrackTarget(const MWWorld::ConstPtr& target); }; MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype); } #endif /* GAME_MWMECHANICS_CHARACTER_HPP */ openmw-openmw-0.38.0/apps/openmw/mwmechanics/combat.cpp000066400000000000000000000501431264522266000231410ustar00rootroot00000000000000#include "combat.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/difficultyscaling.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/windowmanager.hpp" #include "actorutil.hpp" namespace { float signedAngleRadians (const osg::Vec3f& v1, const osg::Vec3f& v2, const osg::Vec3f& normal) { return std::atan2((normal * (v1 ^ v2)), (v1 * v2)); } bool applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const osg::Vec3f& hitPosition) { std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : ""; if (!enchantmentName.empty()) { const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( enchantmentName); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { MWMechanics::CastSpell cast(attacker, victim); cast.mHitPosition = hitPosition; cast.cast(object); return true; } } return false; } } namespace MWMechanics { bool blockMeleeAttack(const MWWorld::Ptr &attacker, const MWWorld::Ptr &blocker, const MWWorld::Ptr &weapon, float damage, float attackStrength) { if (!blocker.getClass().hasInventoryStore(blocker)) return false; MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker); if (blockerStats.getKnockedDown() // Used for both knockout or knockdown || blockerStats.getHitRecovery() || blockerStats.isParalyzed()) return false; if (!MWBase::Environment::get().getMechanicsManager()->isReadyToBlock(blocker)) return false; MWWorld::InventoryStore& inv = blocker.getClass().getInventoryStore(blocker); MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name()) return false; if (!blocker.getRefData().getBaseNode()) return false; // shouldn't happen float angleDegrees = osg::RadiansToDegrees( signedAngleRadians ( (attacker.getRefData().getPosition().asVec3() - blocker.getRefData().getPosition().asVec3()), blocker.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0), osg::Vec3f(0,0,1))); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); if (angleDegrees < gmst.find("fCombatBlockLeftAngle")->getFloat()) return false; if (angleDegrees > gmst.find("fCombatBlockRightAngle")->getFloat()) return false; MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2f * blockerStats.getAttribute(ESM::Attribute::Agility).getModified() + 0.1f * blockerStats.getAttribute(ESM::Attribute::Luck).getModified(); float enemySwing = attackStrength; float swingTerm = enemySwing * gmst.find("fSwingBlockMult")->getFloat() + gmst.find("fSwingBlockBase")->getFloat(); float blockerTerm = blockTerm * swingTerm; if (blocker.getClass().getMovementSettings(blocker).mPosition[1] <= 0) blockerTerm *= gmst.find("fBlockStillBonus")->getFloat(); blockerTerm *= blockerStats.getFatigueTerm(); int attackerSkill = 0; if (weapon.isEmpty()) attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand); else attackerSkill = attacker.getClass().getSkill(attacker, weapon.getClass().getEquipmentSkill(weapon)); float attackerTerm = attackerSkill + 0.2f * attackerStats.getAttribute(ESM::Attribute::Agility).getModified() + 0.1f * attackerStats.getAttribute(ESM::Attribute::Luck).getModified(); attackerTerm *= attackerStats.getFatigueTerm(); int x = int(blockerTerm - attackerTerm); int iBlockMaxChance = gmst.find("iBlockMaxChance")->getInt(); int iBlockMinChance = gmst.find("iBlockMinChance")->getInt(); x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x)); if (Misc::Rng::roll0to99() < x) { // Reduce shield durability by incoming damage int shieldhealth = shield->getClass().getItemHealth(*shield); shieldhealth -= std::min(shieldhealth, int(damage)); shield->getCellRef().setCharge(shieldhealth); if (shieldhealth == 0) inv.unequipItem(*shield, blocker); // Reduce blocker fatigue const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat(); const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat(); const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat(); MWMechanics::DynamicStat fatigue = blockerStats.getFatigue(); float normalizedEncumbrance = blocker.getClass().getNormalizedEncumbrance(blocker); normalizedEncumbrance = std::min(1.f, normalizedEncumbrance); float fatigueLoss = fFatigueBlockBase + normalizedEncumbrance * fFatigueBlockMult; if (!weapon.isEmpty()) fatigueLoss += weapon.getClass().getWeight(weapon) * attackStrength * fWeaponFatigueBlockMult; fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); blockerStats.setFatigue(fatigue); blockerStats.setBlock(true); if (blocker == getPlayer()) blocker.getClass().skillUsageSucceeded(blocker, ESM::Skill::Block, 0); return true; } return false; } void resistNormalWeapon(const MWWorld::Ptr &actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr &weapon, float &damage) { const MWMechanics::MagicEffects& effects = actor.getClass().getCreatureStats(actor).getMagicEffects(); float resistance = std::min(100.f, effects.get(ESM::MagicEffect::ResistNormalWeapons).getMagnitude() - effects.get(ESM::MagicEffect::WeaknessToNormalWeapons).getMagnitude()); float multiplier = 1.f - resistance / 100.f; if (!(weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver || weapon.get()->mBase->mData.mFlags & ESM::Weapon::Magical)) damage *= multiplier; if ((weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver) && actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) damage *= MWBase::Environment::get().getWorld()->getStore().get().find("fWereWolfSilverWeaponDamageMult")->getFloat(); if (damage == 0 && attacker == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}"); } void projectileHit(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, MWWorld::Ptr weapon, const MWWorld::Ptr &projectile, const osg::Vec3f& hitPosition, float attackStrength) { MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &gmst = world->getStore().get(); if(victim.isEmpty() || !victim.getClass().isActor() || victim.getClass().getCreatureStats(victim).isDead()) // Can't hit non-actors or dead actors { reduceWeaponCondition(0.f, false, weapon, attacker); return; } if(attacker == getPlayer()) MWBase::Environment::get().getWindowManager()->setEnemy(victim); int weapskill = ESM::Skill::Marksman; if(!weapon.isEmpty()) weapskill = weapon.getClass().getEquipmentSkill(weapon); int skillValue = attacker.getClass().getSkill(attacker, weapon.getClass().getEquipmentSkill(weapon)); if (Misc::Rng::roll0to99() >= getHitChance(attacker, victim, skillValue)) { victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false); MWMechanics::reduceWeaponCondition(0.f, false, weapon, attacker); return; } const unsigned char* attack = weapon.get()->mBase->mData.mChop; float damage = attack[0] + ((attack[1]-attack[0])*attackStrength); // Bow/crossbow damage // Arrow/bolt damage // NB in case of thrown weapons, we are applying the damage twice since projectile == weapon attack = projectile.get()->mBase->mData.mChop; damage += attack[0] + ((attack[1]-attack[0])*attackStrength); adjustWeaponDamage(damage, weapon, attacker); reduceWeaponCondition(damage, true, weapon, attacker); if(attacker == getPlayer()) attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0); if (victim.getClass().getCreatureStats(victim).getKnockedDown()) damage *= gmst.find("fCombatKODamageMult")->getFloat(); // Apply "On hit" effect of the weapon bool appliedEnchantment = applyEnchantment(attacker, victim, weapon, hitPosition); if (weapon != projectile) appliedEnchantment = applyEnchantment(attacker, victim, projectile, hitPosition); if (damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); // Non-enchanted arrows shot at enemies have a chance to turn up in their inventory if (victim != getPlayer() && !appliedEnchantment) { float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat(); if (Misc::Rng::rollProbability() < fProjectileThrownStoreChance / 100.f) victim.getClass().getContainerStore(victim).add(projectile, 1, victim); } victim.getClass().onHit(victim, damage, true, projectile, attacker, true); } float getHitChance(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, int skillValue) { MWMechanics::CreatureStats &stats = attacker.getClass().getCreatureStats(attacker); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &gmst = world->getStore().get(); float defenseTerm = 0; MWMechanics::CreatureStats& victimStats = victim.getClass().getCreatureStats(victim); if (victimStats.getFatigue().getCurrent() >= 0) { // Maybe we should keep an aware state for actors updated every so often instead of testing every time bool unaware = (!victimStats.getAiSequence().isInCombat()) && (attacker == getPlayer()) && (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim)); if (!(victimStats.getKnockedDown() || victimStats.isParalyzed() || unaware )) { defenseTerm = victimStats.getEvasion(); } defenseTerm += std::min(100.f, gmst.find("fCombatInvisoMult")->getFloat() * victimStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude()); defenseTerm += std::min(100.f, gmst.find("fCombatInvisoMult")->getFloat() * victimStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude()); } float attackTerm = skillValue + (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); attackTerm *= stats.getFatigueTerm(); attackTerm += mageffects.get(ESM::MagicEffect::FortifyAttack).getMagnitude() - mageffects.get(ESM::MagicEffect::Blind).getMagnitude(); return round(attackTerm - defenseTerm); } void applyElementalShields(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim) { for (int i=0; i<3; ++i) { float magnitude = victim.getClass().getCreatureStats(victim).getMagicEffects().get(ESM::MagicEffect::FireShield+i).getMagnitude(); if (!magnitude) continue; CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); float saveTerm = attacker.getClass().getSkill(attacker, ESM::Skill::Destruction) + 0.2f * attackerStats.getAttribute(ESM::Attribute::Willpower).getModified() + 0.1f * attackerStats.getAttribute(ESM::Attribute::Luck).getModified(); float fatigueMax = attackerStats.getFatigue().getModified(); float fatigueCurrent = attackerStats.getFatigue().getCurrent(); float normalisedFatigue = floor(fatigueMax)==0 ? 1 : std::max (0.0f, (fatigueCurrent/fatigueMax)); saveTerm *= 1.25f * normalisedFatigue; float x = std::max(0.f, saveTerm - Misc::Rng::roll0to99()); int element = ESM::MagicEffect::FireDamage; if (i == 1) element = ESM::MagicEffect::ShockDamage; if (i == 2) element = ESM::MagicEffect::FrostDamage; float elementResistance = MWMechanics::getEffectResistanceAttribute(element, &attackerStats.getMagicEffects()); x = std::min(100.f, x + elementResistance); static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get().find("fElementalShieldMult")->getFloat(); x = fElementalShieldMult * magnitude * (1.f - 0.01f * x); // Note swapped victim and attacker, since the attacker takes the damage here. x = scaleDamage(x, victim, attacker); MWMechanics::DynamicStat health = attackerStats.getHealth(); health.setCurrent(health.getCurrent() - x); attackerStats.setHealth(health); } } void reduceWeaponCondition(float damage, bool hit, MWWorld::Ptr &weapon, const MWWorld::Ptr &attacker) { if (weapon.isEmpty()) return; if (!hit) damage = 0.f; const bool weaphashealth = weapon.getClass().hasItemHealth(weapon); if(weaphashealth) { int weaphealth = weapon.getClass().getItemHealth(weapon); const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get().find("fWeaponDamageMult")->getFloat(); float x = std::max(1.f, fWeaponDamageMult * damage); weaphealth -= std::min(int(x), weaphealth); weapon.getCellRef().setCharge(weaphealth); // Weapon broken? unequip it if (weaphealth == 0) weapon = *attacker.getClass().getInventoryStore(attacker).unequipItem(weapon, attacker); } } void adjustWeaponDamage(float &damage, const MWWorld::Ptr &weapon, const MWWorld::Ptr& attacker) { if (weapon.isEmpty()) return; const bool weaphashealth = weapon.getClass().hasItemHealth(weapon); if(weaphashealth) { int weaphealth = weapon.getClass().getItemHealth(weapon); int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon); damage *= (float(weaphealth) / weapmaxhealth); } static const float fDamageStrengthBase = MWBase::Environment::get().getWorld()->getStore().get() .find("fDamageStrengthBase")->getFloat(); static const float fDamageStrengthMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fDamageStrengthMult")->getFloat(); damage *= fDamageStrengthBase + (attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1f); } void getHandToHandDamage(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, float &damage, bool &healthdmg, float attackStrength) { // Note: MCP contains an option to include Strength in hand-to-hand damage // calculations. Some mods recommend using it, so we may want to include an // option for it. const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); float minstrike = store.get().find("fMinHandToHandMult")->getFloat(); float maxstrike = store.get().find("fMaxHandToHandMult")->getFloat(); damage = static_cast(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand)); damage *= minstrike + ((maxstrike-minstrike)*attackStrength); MWMechanics::CreatureStats& otherstats = victim.getClass().getCreatureStats(victim); healthdmg = otherstats.isParalyzed() || otherstats.getKnockedDown(); bool isWerewolf = (attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()); if(isWerewolf) { healthdmg = true; // GLOB instead of GMST because it gets updated during a quest damage *= MWBase::Environment::get().getWorld()->getGlobalFloat("werewolfclawmult"); } if(healthdmg) damage *= store.get().find("fHandtoHandHealthPer")->getFloat(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(isWerewolf) { const ESM::Sound *sound = store.get().searchRandom("WolfHit"); if(sound) sndMgr->playSound3D(victim, sound->mId, 1.0f, 1.0f); } else sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f); } void applyFatigueLoss(const MWWorld::Ptr &attacker, const MWWorld::Ptr &weapon, float attackStrength) { // somewhat of a guess, but using the weapon weight makes sense const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat(); const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat(); const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat(); CreatureStats& stats = attacker.getClass().getCreatureStats(attacker); MWMechanics::DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = attacker.getClass().getNormalizedEncumbrance(attacker); float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; if (!weapon.isEmpty()) fatigueLoss += weapon.getClass().getWeight(weapon) * attackStrength * fWeaponFatigueMult; fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue); } bool isEnvironmentCompatible(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim) { const MWWorld::Class& attackerClass = attacker.getClass(); MWBase::World* world = MWBase::Environment::get().getWorld(); // If attacker is fish, victim must be in water if (attackerClass.isPureWaterCreature(attacker)) { return world->isWading(victim); } // If attacker can't swim, victim must not be in water if (!attackerClass.canSwim(attacker)) { return !world->isSwimming(victim); } return true; } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/combat.hpp000066400000000000000000000044751264522266000231550ustar00rootroot00000000000000#ifndef OPENMW_MECHANICS_COMBAT_H #define OPENMW_MECHANICS_COMBAT_H #include "../mwworld/ptr.hpp" namespace MWMechanics { /// @return can we block the attack? bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker, const MWWorld::Ptr& weapon, float damage, float attackStrength); void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage); /// @note for a thrown weapon, \a weapon == \a projectile, for bows/crossbows, \a projectile is the arrow/bolt /// @note \a victim may be empty (e.g. for a hit on terrain), a non-actor (environment objects) or an actor void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile, const osg::Vec3f& hitPosition, float attackStrength); /// Get the chance (in percent) for \a attacker to successfully hit \a victim with a given weapon skill value float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, int skillValue); /// Applies damage to attacker based on the victim's elemental shields. void applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); /// @param damage Unmitigated weapon damage of the attack /// @param hit Was the attack successful? /// @param weapon The weapon used. /// @note if the weapon is unequipped as result of condition damage, a new Ptr will be assigned to \a weapon. void reduceWeaponCondition (float damage, bool hit, MWWorld::Ptr& weapon, const MWWorld::Ptr& attacker); /// Adjust weapon damage based on its condition. A used weapon will be less effective. void adjustWeaponDamage (float& damage, const MWWorld::Ptr& weapon, const MWWorld::Ptr& attacker); void getHandToHandDamage (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, float& damage, bool& healthdmg, float attackStrength); /// Apply the fatigue loss incurred by attacking with the given weapon (weapon may be empty = hand-to-hand) void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float attackStrength); /// Can attacker operate in victim's environment? /// e.g. If attacker is a fish, is victim in water? Or, if attacker can't swim, is victim on land? bool isEnvironmentCompatible(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/coordinateconverter.cpp000066400000000000000000000022141264522266000257470ustar00rootroot00000000000000#include "coordinateconverter.hpp" #include #include namespace MWMechanics { CoordinateConverter::CoordinateConverter(const ESM::Cell* cell) : mCellX(0), mCellY(0) { if (cell->isExterior()) { mCellX = cell->mData.mX * ESM::Land::REAL_SIZE; mCellY = cell->mData.mY * ESM::Land::REAL_SIZE; } } void CoordinateConverter::toWorld(ESM::Pathgrid::Point& point) { point.mX += mCellX; point.mY += mCellY; } void CoordinateConverter::toWorld(osg::Vec3f& point) { point.x() += static_cast(mCellX); point.y() += static_cast(mCellY); } void CoordinateConverter::toLocal(osg::Vec3f& point) { point.x() -= static_cast(mCellX); point.y() -= static_cast(mCellY); } osg::Vec3f CoordinateConverter::toLocalVec3(const ESM::Pathgrid::Point& point) { return osg::Vec3f( static_cast(point.mX - mCellX), static_cast(point.mY - mCellY), static_cast(point.mZ)); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/coordinateconverter.hpp000066400000000000000000000015511264522266000257570ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_COORDINATECONVERTER_H #define GAME_MWMECHANICS_COORDINATECONVERTER_H #include #include namespace ESM { struct Cell; } namespace MWMechanics { /// \brief convert coordinates between world and local cell class CoordinateConverter { public: CoordinateConverter(const ESM::Cell* cell); /// in-place conversion from local to world void toWorld(ESM::Pathgrid::Point& point); /// in-place conversion from local to world void toWorld(osg::Vec3f& point); /// in-place conversion from world to local void toLocal(osg::Vec3f& point); osg::Vec3f toLocalVec3(const ESM::Pathgrid::Point& point); private: int mCellX; int mCellY; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/creaturestats.cpp000066400000000000000000000411001264522266000245560ustar00rootroot00000000000000#include "creaturestats.hpp" #include #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" namespace MWMechanics { int CreatureStats::sActorId = 0; CreatureStats::CreatureStats() : mDrawState (DrawState_Nothing), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), mAttacked (false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mDeathAnimation(0), mLevel (0) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; } const AiSequence& CreatureStats::getAiSequence() const { return mAiSequence; } AiSequence& CreatureStats::getAiSequence() { return mAiSequence; } float CreatureStats::getFatigueTerm() const { float max = getFatigue().getModified(); float current = getFatigue().getCurrent(); float normalised = floor(max) == 0 ? 1 : std::max (0.0f, current / max); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); static const float fFatigueBase = gmst.find("fFatigueBase")->getFloat(); static const float fFatigueMult = gmst.find("fFatigueMult")->getFloat(); return fFatigueBase - fFatigueMult * (1-normalised); } const AttributeValue &CreatureStats::getAttribute(int index) const { if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } return mAttributes[index]; } const DynamicStat &CreatureStats::getHealth() const { return mDynamic[0]; } const DynamicStat &CreatureStats::getMagicka() const { return mDynamic[1]; } const DynamicStat &CreatureStats::getFatigue() const { return mDynamic[2]; } const Spells &CreatureStats::getSpells() const { return mSpells; } const ActiveSpells &CreatureStats::getActiveSpells() const { return mActiveSpells; } const MagicEffects &CreatureStats::getMagicEffects() const { return mMagicEffects; } int CreatureStats::getLevel() const { return mLevel; } Stat CreatureStats::getAiSetting (AiSetting index) const { return mAiSettings[index]; } const DynamicStat &CreatureStats::getDynamic(int index) const { if (index < 0 || index > 2) { throw std::runtime_error("dynamic stat index is out of range"); } return mDynamic[index]; } Spells &CreatureStats::getSpells() { return mSpells; } ActiveSpells &CreatureStats::getActiveSpells() { return mActiveSpells; } MagicEffects &CreatureStats::getMagicEffects() { return mMagicEffects; } void CreatureStats::setAttribute(int index, int base) { AttributeValue current = getAttribute(index); current.setBase(base); setAttribute(index, current); } void CreatureStats::setAttribute(int index, const AttributeValue &value) { if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } const AttributeValue& currentValue = mAttributes[index]; if (value != currentValue) { mAttributes[index] = value; if (index == ESM::Attribute::Intelligence) mRecalcMagicka = true; else if (index == ESM::Attribute::Strength || index == ESM::Attribute::Willpower || index == ESM::Attribute::Agility || index == ESM::Attribute::Endurance) { int strength = getAttribute(ESM::Attribute::Strength).getModified(); int willpower = getAttribute(ESM::Attribute::Willpower).getModified(); int agility = getAttribute(ESM::Attribute::Agility).getModified(); int endurance = getAttribute(ESM::Attribute::Endurance).getModified(); DynamicStat fatigue = getFatigue(); float diff = (strength+willpower+agility+endurance) - fatigue.getBase(); fatigue.modify(diff); setFatigue(fatigue); } } } void CreatureStats::setHealth(const DynamicStat &value) { setDynamic (0, value); } void CreatureStats::setMagicka(const DynamicStat &value) { setDynamic (1, value); } void CreatureStats::setFatigue(const DynamicStat &value) { setDynamic (2, value); } void CreatureStats::setDynamic (int index, const DynamicStat &value) { if (index < 0 || index > 2) throw std::runtime_error("dynamic stat index is out of range"); mDynamic[index] = value; if (index==0 && mDynamic[index].getCurrent()<1) { mDead = true; mDynamic[index].setModifier(0); mDynamic[index].setCurrent(0); if (MWBase::Environment::get().getWorld()->getGodModeState()) MWBase::Environment::get().getMechanicsManager()->keepPlayerAlive(); } } void CreatureStats::setLevel(int level) { mLevel = level; } void CreatureStats::modifyMagicEffects(const MagicEffects &effects) { if (effects.get(ESM::MagicEffect::FortifyMaximumMagicka).getModifier() != mMagicEffects.get(ESM::MagicEffect::FortifyMaximumMagicka).getModifier()) mRecalcMagicka = true; mMagicEffects.setModifiers(effects); } void CreatureStats::setAiSetting (AiSetting index, Stat value) { mAiSettings[index] = value; } void CreatureStats::setAiSetting (AiSetting index, int base) { Stat stat = getAiSetting(index); stat.setBase(base); setAiSetting(index, stat); } bool CreatureStats::isParalyzed() const { return mMagicEffects.get(ESM::MagicEffect::Paralyze).getMagnitude() > 0; } bool CreatureStats::isDead() const { return mDead; } void CreatureStats::notifyDied() { mDied = true; } bool CreatureStats::hasDied() const { return mDied; } void CreatureStats::clearHasDied() { mDied = false; } bool CreatureStats::hasBeenMurdered() const { return mMurdered; } void CreatureStats::notifyMurder() { mMurdered = true; } void CreatureStats::clearHasBeenMurdered() { mMurdered = false; } void CreatureStats::resurrect() { if (mDead) { if (mDynamic[0].getModified() < 1) mDynamic[0].setModified(1, 0); mDynamic[0].setCurrent(mDynamic[0].getModified()); mDead = false; } } bool CreatureStats::hasCommonDisease() const { return mSpells.hasCommonDisease(); } bool CreatureStats::hasBlightDisease() const { return mSpells.hasBlightDisease(); } int CreatureStats::getFriendlyHits() const { return mFriendlyHits; } void CreatureStats::friendlyHit() { ++mFriendlyHits; } bool CreatureStats::hasTalkedToPlayer() const { return mTalkedTo; } void CreatureStats::talkedToPlayer() { mTalkedTo = true; } bool CreatureStats::isAlarmed() const { return mAlarmed; } void CreatureStats::setAlarmed (bool alarmed) { mAlarmed = alarmed; } bool CreatureStats::getAttacked() const { return mAttacked; } void CreatureStats::setAttacked (bool attacked) { mAttacked = attacked; } float CreatureStats::getEvasion() const { float evasion = (getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + (getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); evasion *= getFatigueTerm(); evasion += std::min(100.f, mMagicEffects.get(ESM::MagicEffect::Sanctuary).getMagnitude()); return evasion; } void CreatureStats::setLastHitObject(const std::string& objectid) { mLastHitObject = objectid; } const std::string &CreatureStats::getLastHitObject() const { return mLastHitObject; } void CreatureStats::setLastHitAttemptObject(const std::string& objectid) { mLastHitAttemptObject = objectid; } const std::string &CreatureStats::getLastHitAttemptObject() const { return mLastHitAttemptObject; } void CreatureStats::addToFallHeight(float height) { mFallHeight += height; } float CreatureStats::land() { float height = mFallHeight; mFallHeight = 0; return height; } bool CreatureStats::needToRecalcDynamicStats() { if (mRecalcMagicka) { mRecalcMagicka = false; return true; } return false; } void CreatureStats::setNeedRecalcDynamicStats(bool val) { mRecalcMagicka = val; } void CreatureStats::setKnockedDown(bool value) { mKnockdown = value; if(!value) //Resets the "OverOneFrame" flag setKnockedDownOverOneFrame(false); } bool CreatureStats::getKnockedDown() const { return mKnockdown; } void CreatureStats::setKnockedDownOneFrame(bool value) { mKnockdownOneFrame = value; } bool CreatureStats::getKnockedDownOneFrame() const { return mKnockdownOneFrame; } void CreatureStats::setKnockedDownOverOneFrame(bool value) { mKnockdownOverOneFrame = value; } bool CreatureStats::getKnockedDownOverOneFrame() const { return mKnockdownOverOneFrame; } void CreatureStats::setHitRecovery(bool value) { mHitRecovery = value; } bool CreatureStats::getHitRecovery() const { return mHitRecovery; } void CreatureStats::setBlock(bool value) { mBlock = value; } bool CreatureStats::getBlock() const { return mBlock; } bool CreatureStats::getMovementFlag (Flag flag) const { return (mMovementFlags & flag) != 0; } void CreatureStats::setMovementFlag (Flag flag, bool state) { if (state) mMovementFlags |= flag; else mMovementFlags &= ~flag; } bool CreatureStats::getStance(Stance flag) const { switch (flag) { case Stance_Run: return getMovementFlag (Flag_Run) || getMovementFlag (Flag_ForceRun); case Stance_Sneak: return getMovementFlag (Flag_Sneak) || getMovementFlag (Flag_ForceSneak); default: return false; } } DrawState_ CreatureStats::getDrawState() const { return mDrawState; } void CreatureStats::setDrawState(DrawState_ state) { mDrawState = state; } void CreatureStats::writeState (ESM::CreatureStats& state) const { for (int i=0; i& CreatureStats::getSummonedCreatureMap() { return mSummonedCreatures; } std::vector& CreatureStats::getSummonedCreatureGraveyard() { return mSummonGraveyard; } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/creaturestats.hpp000066400000000000000000000174751264522266000246050ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_CREATURESTATS_H #define GAME_MWMECHANICS_CREATURESTATS_H #include #include #include #include "stat.hpp" #include "magiceffects.hpp" #include "spells.hpp" #include "activespells.hpp" #include "aisequence.hpp" #include "drawstate.hpp" namespace ESM { struct CreatureStats; } namespace MWMechanics { /// \brief Common creature stats /// /// class CreatureStats { static int sActorId; DrawState_ mDrawState; AttributeValue mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue Spells mSpells; ActiveSpells mActiveSpells; MagicEffects mMagicEffects; Stat mAiSettings[4]; AiSequence mAiSequence; bool mDead; bool mDied; bool mMurdered; int mFriendlyHits; bool mTalkedTo; bool mAlarmed; bool mAttacked; bool mKnockdown; bool mKnockdownOneFrame; bool mKnockdownOverOneFrame; bool mHitRecovery; bool mBlock; unsigned int mMovementFlags; float mFallHeight; std::string mLastHitObject; // The last object to hit this actor std::string mLastHitAttemptObject; // The last object to attempt to hit this actor bool mRecalcMagicka; // For merchants: the last time items were restocked and gold pool refilled. MWWorld::TimeStamp mLastRestock; // The pool of merchant gold (not in inventory) int mGoldPool; int mActorId; // The index of the death animation that was played unsigned char mDeathAnimation; public: typedef std::pair SummonKey; // private: std::map mSummonedCreatures; // // Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet. // This may be necessary when the creature is in an inactive cell. std::vector mSummonGraveyard; protected: int mLevel; public: CreatureStats(); DrawState_ getDrawState() const; void setDrawState(DrawState_ state); bool needToRecalcDynamicStats(); void setNeedRecalcDynamicStats(bool val); void addToFallHeight(float height); /// Reset the fall height /// @return total fall height float land(); const AttributeValue & getAttribute(int index) const; const DynamicStat & getHealth() const; const DynamicStat & getMagicka() const; const DynamicStat & getFatigue() const; const DynamicStat & getDynamic (int index) const; const Spells & getSpells() const; const ActiveSpells & getActiveSpells() const; const MagicEffects & getMagicEffects() const; bool getAttackingOrSpell() const; int getLevel() const; Spells & getSpells(); ActiveSpells & getActiveSpells(); MagicEffects & getMagicEffects(); void setAttribute(int index, const AttributeValue &value); // Shortcut to set only the base void setAttribute(int index, int base); void setHealth(const DynamicStat &value); void setMagicka(const DynamicStat &value); void setFatigue(const DynamicStat &value); void setDynamic (int index, const DynamicStat &value); /// Set Modifier for each magic effect according to \a effects. Does not touch Base values. void modifyMagicEffects(const MagicEffects &effects); void setAttackingOrSpell(bool attackingOrSpell); void setLevel(int level); enum AiSetting { AI_Hello = 0, AI_Fight = 1, AI_Flee = 2, AI_Alarm = 3 }; void setAiSetting (AiSetting index, Stat value); void setAiSetting (AiSetting index, int base); Stat getAiSetting (AiSetting index) const; const AiSequence& getAiSequence() const; AiSequence& getAiSequence(); float getFatigueTerm() const; ///< Return effective fatigue bool isParalyzed() const; bool isDead() const; void notifyDied(); bool hasDied() const; void clearHasDied(); bool hasBeenMurdered() const; void clearHasBeenMurdered(); void notifyMurder(); void resurrect(); bool hasCommonDisease() const; bool hasBlightDisease() const; int getFriendlyHits() const; ///< Number of friendly hits received. void friendlyHit(); ///< Increase number of friendly hits by one. bool hasTalkedToPlayer() const; ///< Has this creature talked with the player before? void talkedToPlayer(); bool isAlarmed() const; void setAlarmed (bool alarmed); bool getAttacked() const; void setAttacked (bool attacked); float getEvasion() const; void setKnockedDown(bool value); /// Returns true for the entire duration of the actor being knocked down or knocked out, /// including transition animations (falling down & standing up) bool getKnockedDown() const; void setKnockedDownOneFrame(bool value); ///Returns true only for the first frame of the actor being knocked out; used for "onKnockedOut" command bool getKnockedDownOneFrame() const; void setKnockedDownOverOneFrame(bool value); ///Returns true for all but the first frame of being knocked out; used to know to not reset mKnockedDownOneFrame bool getKnockedDownOverOneFrame() const; void setHitRecovery(bool value); bool getHitRecovery() const; void setBlock(bool value); bool getBlock() const; std::map& getSummonedCreatureMap(); // std::vector& getSummonedCreatureGraveyard(); // ActorIds enum Flag { Flag_ForceRun = 1, Flag_ForceSneak = 2, Flag_Run = 4, Flag_Sneak = 8, Flag_ForceJump = 16, Flag_ForceMoveJump = 32 }; enum Stance { Stance_Run, Stance_Sneak }; bool getMovementFlag (Flag flag) const; void setMovementFlag (Flag flag, bool state); /// Like getMovementFlag, but also takes into account if the flag is Forced bool getStance (Stance flag) const; void setLastHitObject(const std::string &objectid); void setLastHitAttemptObject(const std::string &objectid); const std::string &getLastHitObject() const; const std::string &getLastHitAttemptObject() const; // Note, this is just a cache to avoid checking the whole container store every frame. We don't need to store it in saves. // TODO: Put it somewhere else? std::set mBoundItems; void writeState (ESM::CreatureStats& state) const; void readState (const ESM::CreatureStats& state); static void writeActorIdCounter (ESM::ESMWriter& esm); static void readActorIdCounter (ESM::ESMReader& esm); void setLastRestockTime(MWWorld::TimeStamp tradeTime); MWWorld::TimeStamp getLastRestockTime() const; void setGoldPool(int pool); int getGoldPool() const; unsigned char getDeathAnimation() const; void setDeathAnimation(unsigned char index); int getActorId(); ///< Will generate an actor ID, if the actor does not have one yet. bool matchesActorId (int id) const; ///< Check if \a id matches the actor ID of *this (if the actor does not have an ID /// assigned this function will return false). static void cleanup(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/difficultyscaling.cpp000066400000000000000000000021171264522266000253750ustar00rootroot00000000000000#include "difficultyscaling.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" #include #include "actorutil.hpp" float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim) { const MWWorld::Ptr& player = MWMechanics::getPlayer(); // [-100, 100] int difficultySetting = Settings::Manager::getInt("difficulty", "Game"); static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get().find("fDifficultyMult")->getFloat(); float difficultyTerm = 0.01f * difficultySetting; float x = 0; if (victim == player) { if (difficultyTerm > 0) x = fDifficultyMult * difficultyTerm; else x = difficultyTerm / fDifficultyMult; } else if (attacker == player) { if (difficultyTerm > 0) x = -difficultyTerm / fDifficultyMult; else x = fDifficultyMult * (-difficultyTerm); } damage *= 1 + x; return damage; } openmw-openmw-0.38.0/apps/openmw/mwmechanics/difficultyscaling.hpp000066400000000000000000000004501264522266000254000ustar00rootroot00000000000000#ifndef OPENMW_MWMECHANICS_DIFFICULTYSCALING_H #define OPENMW_MWMECHANICS_DIFFICULTYSCALING_H namespace MWWorld { class Ptr; } /// Scales damage dealt to an actor based on difficulty setting float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/disease.hpp000066400000000000000000000060221264522266000233130ustar00rootroot00000000000000#ifndef OPENMW_MECHANICS_DISEASE_H #define OPENMW_MECHANICS_DISEASE_H #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "spells.hpp" #include "creaturestats.hpp" #include "actorutil.hpp" namespace MWMechanics { /// Call when \a actor has got in contact with \a carrier (e.g. hit by him, or loots him) /// @param actor The actor that will potentially catch diseases. Currently only the player can catch diseases. /// @param carrier The disease carrier. inline void diseaseContact (MWWorld::Ptr actor, MWWorld::Ptr carrier) { if (!carrier.getClass().isActor() || actor != getPlayer()) return; float fDiseaseXferChance = MWBase::Environment::get().getWorld()->getStore().get().find( "fDiseaseXferChance")->getFloat(); MagicEffects& actorEffects = actor.getClass().getCreatureStats(actor).getMagicEffects(); Spells& spells = carrier.getClass().getCreatureStats(carrier).getSpells(); for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ESM::Spell* spell = it->first; if (actor.getClass().getCreatureStats(actor).getSpells().hasSpell(spell->mId)) continue; float resist = 0.f; if (spells.hasCorprusEffect(spell)) resist = 1.f - 0.01f * (actorEffects.get(ESM::MagicEffect::ResistCorprusDisease).getMagnitude() - actorEffects.get(ESM::MagicEffect::WeaknessToCorprusDisease).getMagnitude()); else if (spell->mData.mType == ESM::Spell::ST_Disease) resist = 1.f - 0.01f * (actorEffects.get(ESM::MagicEffect::ResistCommonDisease).getMagnitude() - actorEffects.get(ESM::MagicEffect::WeaknessToCommonDisease).getMagnitude()); else if (spell->mData.mType == ESM::Spell::ST_Blight) resist = 1.f - 0.01f * (actorEffects.get(ESM::MagicEffect::ResistBlightDisease).getMagnitude() - actorEffects.get(ESM::MagicEffect::WeaknessToBlightDisease).getMagnitude()); else continue; int x = static_cast(fDiseaseXferChance * 100 * resist); if (Misc::Rng::rollDice(10000) < x) { // Contracted disease! actor.getClass().getCreatureStats(actor).getSpells().add(it->first); std::string msg = "sMagicContractDisease"; msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->getString(); if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, spell->mName); MWBase::Environment::get().getWindowManager()->messageBox(msg); } } } } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/drawstate.hpp000066400000000000000000000005161264522266000236760ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_DRAWSTATE_H #define GAME_MWMECHANICS_DRAWSTATE_H namespace MWMechanics { /// \note The _ suffix is required to avoid a collision with a Windoze macro. Die, Microsoft! Die! enum DrawState_ { DrawState_Nothing = 0, DrawState_Weapon = 1, DrawState_Spell = 2 }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/enchanting.cpp000066400000000000000000000242541264522266000240160ustar00rootroot00000000000000#include "enchanting.hpp" #include #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" #include "spellcasting.hpp" #include "actorutil.hpp" namespace MWMechanics { Enchanting::Enchanting() : mCastStyle(ESM::Enchantment::CastOnce) , mSelfEnchanting(false) {} void Enchanting::setOldItem(MWWorld::Ptr oldItem) { mOldItemPtr=oldItem; if(!itemEmpty()) { mObjectType = mOldItemPtr.getTypeName(); } else { mObjectType=""; } } void Enchanting::setNewItemName(const std::string& s) { mNewItemName=s; } void Enchanting::setEffect(ESM::EffectList effectList) { mEffectList=effectList; } int Enchanting::getCastStyle() const { return mCastStyle; } void Enchanting::setSoulGem(MWWorld::Ptr soulGem) { mSoulGemPtr=soulGem; } bool Enchanting::create() { const MWWorld::Ptr& player = getPlayer(); MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); ESM::Enchantment enchantment; enchantment.mData.mCharge = getGemCharge(); enchantment.mData.mAutocalc = 0; enchantment.mData.mType = mCastStyle; enchantment.mData.mCost = getBaseCastCost(); store.remove(mSoulGemPtr, 1, player); //Exception for Azura Star, new one will be added after enchanting if(Misc::StringUtils::ciEqual(mSoulGemPtr.get()->mBase->mId, "Misc_SoulGem_Azura")) store.add("Misc_SoulGem_Azura", 1, player); if(mSelfEnchanting) { if(getEnchantChance() <= (Misc::Rng::roll0to99())) return false; mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2); } if(mCastStyle==ESM::Enchantment::ConstantEffect) { enchantment.mData.mCharge=0; } enchantment.mEffects = mEffectList; // Apply the enchantment const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); std::string newItemId = mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName); // Add the new item to player inventory and remove the old one store.remove(mOldItemPtr, 1, player); store.add(newItemId, 1, player); if(!mSelfEnchanting) payForEnchantment(); return true; } void Enchanting::nextCastStyle() { if (itemEmpty()) { mCastStyle = ESM::Enchantment::WhenUsed; return; } const bool powerfulSoul = getGemCharge() >= \ MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->getInt(); if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name())) { // Armor or Clothing switch(mCastStyle) { case ESM::Enchantment::WhenUsed: if (powerfulSoul) mCastStyle = ESM::Enchantment::ConstantEffect; return; default: // takes care of Constant effect too mCastStyle = ESM::Enchantment::WhenUsed; return; } } else if(mObjectType == typeid(ESM::Weapon).name()) { // Weapon switch(mCastStyle) { case ESM::Enchantment::WhenStrikes: mCastStyle = ESM::Enchantment::WhenUsed; return; case ESM::Enchantment::WhenUsed: if (powerfulSoul) mCastStyle = ESM::Enchantment::ConstantEffect; else mCastStyle = ESM::Enchantment::WhenStrikes; return; default: // takes care of Constant effect too mCastStyle = ESM::Enchantment::WhenStrikes; return; } } else if(mObjectType == typeid(ESM::Book).name()) { // Scroll or Book mCastStyle = ESM::Enchantment::CastOnce; return; } // Fail case mCastStyle = ESM::Enchantment::CastOnce; } /* * Vanilla enchant cost formula: * * Touch/Self: (min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025 * Target: 1.5 * ((min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025) * Constant eff: (min + max) * baseCost * 2.5 + area * baseCost * 0.025 * * For multiple effects - cost of each effect is multiplied by number of effects that follows +1. * * Note: Minimal value inside formula for 'min' and 'max' is 1. So in vanilla: * (0 + 0) == (1 + 0) == (1 + 1) => 2 or (2 + 0) == (1 + 2) => 3 * * Formula on UESPWiki is not entirely correct. */ int Enchanting::getEnchantPoints() const { if (mEffectList.mList.empty()) // No effects added, cost = 0 return 0; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); std::vector mEffects = mEffectList.mList; float enchantmentCost = 0; int effectsLeftCnt = mEffects.size(); for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { float baseCost = (store.get().find(it->mEffectID))->mData.mBaseCost; int magMin = (it->mMagnMin == 0) ? 1 : it->mMagnMin; int magMax = (it->mMagnMax == 0) ? 1 : it->mMagnMax; int area = (it->mArea == 0) ? 1 : it->mArea; float magnitudeCost = (magMin + magMax) * baseCost * 0.05f; if (mCastStyle == ESM::Enchantment::ConstantEffect) { magnitudeCost *= store.get().find("fEnchantmentConstantDurationMult")->getFloat(); } else { magnitudeCost *= it->mDuration; } float areaCost = area * 0.05f * baseCost; const float fEffectCostMult = store.get().find("fEffectCostMult")->getFloat(); float cost = (magnitudeCost + areaCost) * fEffectCostMult; if (it->mRange == ESM::RT_Target) cost *= 1.5; enchantmentCost += cost * effectsLeftCnt; enchantmentCost = std::max(1.f, enchantmentCost); --effectsLeftCnt; } return static_cast(enchantmentCost); } int Enchanting::getBaseCastCost() const { if (mCastStyle == ESM::Enchantment::ConstantEffect) return 0; return getEnchantPoints(); } int Enchanting::getEffectiveCastCost() const { int baseCost = getBaseCastCost(); MWWorld::Ptr player = getPlayer(); return getEffectiveEnchantmentCastCost(static_cast(baseCost), player); } int Enchanting::getEnchantPrice() const { if(mEnchanter.isEmpty()) return 0; float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentValueMult")->getFloat(); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast(getEnchantPoints() * priceMultipler), true); return price; } int Enchanting::getGemCharge() const { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); if(soulEmpty()) return 0; if(mSoulGemPtr.getCellRef().getSoul()=="") return 0; const ESM::Creature* soul = store.get().find(mSoulGemPtr.getCellRef().getSoul()); return soul->mData.mSoul; } int Enchanting::getMaxEnchantValue() const { if (itemEmpty()) return 0; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); return static_cast(mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get().find("fEnchantmentMult")->getFloat()); } bool Enchanting::soulEmpty() const { return mSoulGemPtr.isEmpty(); } bool Enchanting::itemEmpty() const { return mOldItemPtr.isEmpty(); } void Enchanting::setSelfEnchanting(bool selfEnchanting) { mSelfEnchanting = selfEnchanting; } void Enchanting::setEnchanter(MWWorld::Ptr enchanter) { mEnchanter = enchanter; } float Enchanting::getEnchantChance() const { const NpcStats& npcStats = mEnchanter.getClass().getNpcStats (mEnchanter); float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() + (0.25f * npcStats.getAttribute (ESM::Attribute::Intelligence).getModified()) + (0.125f * npcStats.getAttribute (ESM::Attribute::Luck).getModified())); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); float chance2 = 7.5f / (gmst.find("fEnchantmentChanceMult")->getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ? gmst.find("fEnchantmentConstantChanceMult")->getFloat() : 1.0f )) * getEnchantPoints(); return (chance1-chance2); } void Enchanting::payForEnchantment() const { const MWWorld::Ptr& player = getPlayer(); MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice(), player); // add gold to NPC trading gold pool CreatureStats& enchanterStats = mEnchanter.getClass().getCreatureStats(mEnchanter); enchanterStats.setGoldPool(enchanterStats.getGoldPool() + getEnchantPrice()); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/enchanting.hpp000066400000000000000000000035401264522266000240160ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_ENCHANTING_H #define GAME_MWMECHANICS_ENCHANTING_H #include #include "../mwworld/ptr.hpp" #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" namespace MWMechanics { class Enchanting { MWWorld::Ptr mOldItemPtr; MWWorld::Ptr mSoulGemPtr; MWWorld::Ptr mEnchanter; int mCastStyle; bool mSelfEnchanting; ESM::EffectList mEffectList; std::string mNewItemName; std::string mObjectType; public: Enchanting(); void setEnchanter(MWWorld::Ptr enchanter); void setSelfEnchanting(bool selfEnchanting); void setOldItem(MWWorld::Ptr oldItem); MWWorld::Ptr getOldItem() { return mOldItemPtr; } MWWorld::Ptr getGem() { return mSoulGemPtr; } void setNewItemName(const std::string& s); void setEffect(ESM::EffectList effectList); void setSoulGem(MWWorld::Ptr soulGem); bool create(); //Return true if created, false if failed. void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object) int getCastStyle() const; int getEnchantPoints() const; int getBaseCastCost() const; // To be saved in the enchantment's record int getEffectiveCastCost() const; // Effective cost taking player Enchant skill into account, used for preview purposes in the UI int getEnchantPrice() const; int getMaxEnchantValue() const; int getGemCharge() const; float getEnchantChance() const; bool soulEmpty() const; //Return true if empty bool itemEmpty() const; //Return true if empty void payForEnchantment() const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/levelledlist.hpp000066400000000000000000000063321264522266000243720ustar00rootroot00000000000000#ifndef OPENMW_MECHANICS_LEVELLEDLIST_H #define OPENMW_MECHANICS_LEVELLEDLIST_H #include #include #include "../mwworld/ptr.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "creaturestats.hpp" #include "actorutil.hpp" namespace MWMechanics { /// @return ID of resulting item, or empty if none inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature, unsigned char failChance=0) { const std::vector& items = levItem->mList; const MWWorld::Ptr& player = getPlayer(); int playerLevel = player.getClass().getCreatureStats(player).getLevel(); failChance += levItem->mChanceNone; if (Misc::Rng::roll0to99() < failChance) return std::string(); std::vector candidates; int highestLevel = 0; for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { if (it->mLevel > highestLevel && it->mLevel <= playerLevel) highestLevel = it->mLevel; } // For levelled creatures, the flags are swapped. This file format just makes so much sense. bool allLevels = (levItem->mFlags & ESM::ItemLevList::AllLevels) != 0; if (creature) allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels; std::pair highest = std::make_pair(-1, ""); for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { if (playerLevel >= it->mLevel && (allLevels || it->mLevel == highestLevel)) { candidates.push_back(it->mId); if (it->mLevel >= highest.first) highest = std::make_pair(it->mLevel, it->mId); } } if (candidates.empty()) return std::string(); std::string item = candidates[Misc::Rng::rollDice(candidates.size())]; // Vanilla doesn't fail on nonexistent items in levelled lists if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item))) { std::cerr << "Warning: ignoring nonexistent item '" << item << "' in levelled list '" << levItem->mId << "'" << std::endl; return std::string(); } // Is this another levelled item or a real item? MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item, 1); if (ref.getPtr().getTypeName() != typeid(ESM::ItemLevList).name() && ref.getPtr().getTypeName() != typeid(ESM::CreatureLevList).name()) { return item; } else { if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name()) return getLevelledItem(ref.getPtr().get()->mBase, false, failChance); else return getLevelledItem(ref.getPtr().get()->mBase, true, failChance); } } } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/magiceffects.cpp000066400000000000000000000125561264522266000243220ustar00rootroot00000000000000#include "magiceffects.hpp" #include #include #include #include namespace MWMechanics { EffectKey::EffectKey() : mId (0), mArg (-1) {} EffectKey::EffectKey (const ESM::ENAMstruct& effect) { mId = effect.mEffectID; mArg = -1; if (effect.mSkill!=-1) mArg = effect.mSkill; if (effect.mAttribute!=-1) { if (mArg!=-1) throw std::runtime_error ( "magic effect can't have both a skill and an attribute argument"); mArg = effect.mAttribute; } } bool operator< (const EffectKey& left, const EffectKey& right) { if (left.mIdright.mId) return false; return left.mArgsecond += param; } } void MagicEffects::modifyBase(const EffectKey &key, int diff) { mCollection[key].modifyBase(diff); } void MagicEffects::setModifiers(const MagicEffects &effects) { for (Collection::iterator it = mCollection.begin(); it != mCollection.end(); ++it) { it->second.setModifier(effects.get(it->first).getModifier()); } for (Collection::const_iterator it = effects.begin(); it != effects.end(); ++it) { mCollection[it->first].setModifier(it->second.getModifier()); } } MagicEffects& MagicEffects::operator+= (const MagicEffects& effects) { if (this==&effects) { MagicEffects temp (effects); *this += temp; return *this; } for (Collection::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) { Collection::iterator result = mCollection.find (iter->first); if (result!=mCollection.end()) result->second += iter->second; else mCollection.insert (*iter); } return *this; } EffectParam MagicEffects::get (const EffectKey& key) const { Collection::const_iterator iter = mCollection.find (key); if (iter==mCollection.end()) { return EffectParam(); } else { return iter->second; } } MagicEffects MagicEffects::diff (const MagicEffects& prev, const MagicEffects& now) { MagicEffects result; // adding/changing for (Collection::const_iterator iter (now.begin()); iter!=now.end(); ++iter) { Collection::const_iterator other = prev.mCollection.find (iter->first); if (other==prev.end()) { // adding result.add (iter->first, iter->second); } else { // changing result.add (iter->first, iter->second - other->second); } } // removing for (Collection::const_iterator iter (prev.begin()); iter!=prev.end(); ++iter) { Collection::const_iterator other = now.mCollection.find (iter->first); if (other==now.end()) { result.add (iter->first, EffectParam() - iter->second); } } return result; } void MagicEffects::writeState(ESM::MagicEffects &state) const { // Don't need to save Modifiers, they are recalculated every frame anyway. for (Collection::const_iterator iter (begin()); iter!=end(); ++iter) { if (iter->second.getBase() != 0) { // Don't worry about mArg, never used by magic effect script instructions state.mEffects.insert(std::make_pair(iter->first.mId, iter->second.getBase())); } } } void MagicEffects::readState(const ESM::MagicEffects &state) { for (std::map::const_iterator it = state.mEffects.begin(); it != state.mEffects.end(); ++it) { mCollection[EffectKey(it->first)].setBase(it->second); } } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/magiceffects.hpp000066400000000000000000000063701264522266000243240ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_MAGICEFFECTS_H #define GAME_MWMECHANICS_MAGICEFFECTS_H #include #include namespace ESM { struct ENAMstruct; struct EffectList; struct MagicEffects; } namespace MWMechanics { struct EffectKey { int mId; int mArg; // skill or ability EffectKey(); EffectKey (int id, int arg = -1) : mId (id), mArg (arg) {} EffectKey (const ESM::ENAMstruct& effect); }; bool operator< (const EffectKey& left, const EffectKey& right); struct EffectParam { private: // Note usually this would be int, but applying partial resistance might introduce a decimal point. float mModifier; int mBase; public: /// Get the total magnitude including base and modifier. float getMagnitude() const; void setModifier(float mod); float getModifier() const; /// Change mBase by \a diff void modifyBase(int diff); void setBase(int base); int getBase() const; EffectParam(); EffectParam(float magnitude) : mModifier(magnitude), mBase(0) {} EffectParam& operator+= (const EffectParam& param); EffectParam& operator-= (const EffectParam& param); }; inline EffectParam operator+ (const EffectParam& left, const EffectParam& right) { EffectParam param (left); return param += right; } inline EffectParam operator- (const EffectParam& left, const EffectParam& right) { EffectParam param (left); return param -= right; } // Used by effect management classes (ActiveSpells, InventoryStore, Spells) to list active effect sources for GUI display struct EffectSourceVisitor { virtual ~EffectSourceVisitor() { } virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) = 0; }; /// \brief Effects currently affecting a NPC or creature class MagicEffects { public: typedef std::map Collection; private: Collection mCollection; public: Collection::const_iterator begin() const { return mCollection.begin(); } Collection::const_iterator end() const { return mCollection.end(); } void readState (const ESM::MagicEffects& state); void writeState (ESM::MagicEffects& state) const; void add (const EffectKey& key, const EffectParam& param); void remove (const EffectKey& key); void modifyBase (const EffectKey& key, int diff); /// Copy Modifier values from \a effects, but keep original mBase values. void setModifiers(const MagicEffects& effects); MagicEffects& operator+= (const MagicEffects& effects); EffectParam get (const EffectKey& key) const; ///< This function can safely be used for keys that are not present. static MagicEffects diff (const MagicEffects& prev, const MagicEffects& now); ///< Return changes from \a prev to \a now. }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp000066400000000000000000002046321264522266000256730ustar00rootroot00000000000000#include "mechanicsmanagerimp.hpp" #include #include #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwmechanics/aicombat.hpp" #include "../mwmechanics/aipursue.hpp" #include "spellcasting.hpp" #include "autocalcspell.hpp" #include "npcstats.hpp" #include "actorutil.hpp" namespace { float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2) { osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3()); osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3()); float d = (pos1 - pos2).length(); static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( "iFightDistanceBase")->getInt(); static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get().find( "fFightDistanceMultiplier")->getFloat(); return (iFightDistanceBase - fFightDistanceMultiplier * d); } float getFightDispositionBias(float disposition) { static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get().find( "fFightDispMult")->getFloat(); return ((50.f - disposition) * fFightDispMult); } void getPersuasionRatings(const MWMechanics::NpcStats& stats, float& rating1, float& rating2, float& rating3, bool player) { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); float persTerm = stats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find("fPersonalityMod")->getFloat(); float luckTerm = stats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find("fLuckMod")->getFloat(); float repTerm = stats.getReputation() * gmst.find("fReputationMod")->getFloat(); float fatigueTerm = stats.getFatigueTerm(); float levelTerm = stats.getLevel() * gmst.find("fLevelMod")->getFloat(); rating1 = (repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; if (player) { rating2 = rating1 + levelTerm; rating3 = (stats.getSkill(ESM::Skill::Mercantile).getModified() + luckTerm + persTerm) * fatigueTerm; } else { rating2 = (levelTerm + repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; rating3 = (stats.getSkill(ESM::Skill::Mercantile).getModified() + repTerm + luckTerm + persTerm) * fatigueTerm; } } } namespace MWMechanics { void MechanicsManager::buildPlayer() { MWWorld::Ptr ptr = getPlayer(); MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats (ptr); const ESM::NPC *player = ptr.get()->mBase; // reset creatureStats.setLevel(player->mNpdt52.mLevel); creatureStats.getSpells().clear(); creatureStats.modifyMagicEffects(MagicEffects()); for (int i=0; i<27; ++i) npcStats.getSkill (i).setBase (player->mNpdt52.mSkills[i]); creatureStats.setAttribute(ESM::Attribute::Strength, player->mNpdt52.mStrength); creatureStats.setAttribute(ESM::Attribute::Intelligence, player->mNpdt52.mIntelligence); creatureStats.setAttribute(ESM::Attribute::Willpower, player->mNpdt52.mWillpower); creatureStats.setAttribute(ESM::Attribute::Agility, player->mNpdt52.mAgility); creatureStats.setAttribute(ESM::Attribute::Speed, player->mNpdt52.mSpeed); creatureStats.setAttribute(ESM::Attribute::Endurance, player->mNpdt52.mEndurance); creatureStats.setAttribute(ESM::Attribute::Personality, player->mNpdt52.mPersonality); creatureStats.setAttribute(ESM::Attribute::Luck, player->mNpdt52.mLuck); const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); // race if (mRaceSelected) { const ESM::Race *race = esmStore.get().find(player->mRace); bool male = (player->mFlags & ESM::NPC::Female) == 0; for (int i=0; i<8; ++i) { const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i]; creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); } for (int i=0; i<27; ++i) { int bonus = 0; for (int i2=0; i2<7; ++i2) if (race->mData.mBonus[i2].mSkill==i) { bonus = race->mData.mBonus[i2].mBonus; break; } npcStats.getSkill (i).setBase (5 + bonus); } for (std::vector::const_iterator iter (race->mPowers.mList.begin()); iter!=race->mPowers.mList.end(); ++iter) { creatureStats.getSpells().add (*iter); } } // birthsign const std::string &signId = MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); if (!signId.empty()) { const ESM::BirthSign *sign = esmStore.get().find(signId); for (std::vector::const_iterator iter (sign->mPowers.mList.begin()); iter!=sign->mPowers.mList.end(); ++iter) { creatureStats.getSpells().add (*iter); } } // class if (mClassSelected) { const ESM::Class *class_ = esmStore.get().find(player->mClass); for (int i=0; i<2; ++i) { int attribute = class_->mData.mAttribute[i]; if (attribute>=0 && attribute<8) { creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); } } for (int i=0; i<2; ++i) { int bonus = i==0 ? 10 : 25; for (int i2=0; i2<5; ++i2) { int index = class_->mData.mSkills[i2][i]; if (index>=0 && index<27) { npcStats.getSkill (index).setBase ( npcStats.getSkill (index).getBase() + bonus); } } } const MWWorld::Store &skills = esmStore.get(); MWWorld::Store::iterator iter = skills.begin(); for (; iter != skills.end(); ++iter) { if (iter->second.mData.mSpecialization==class_->mData.mSpecialization) { int index = iter->first; if (index>=0 && index<27) { npcStats.getSkill (index).setBase ( npcStats.getSkill (index).getBase() + 5); } } } } // F_PCStart spells static const float fPCbaseMagickaMult = esmStore.get().find("fPCbaseMagickaMult")->getFloat(); float baseMagicka = fPCbaseMagickaMult * creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase(); bool reachedLimit = false; const ESM::Spell* weakestSpell = NULL; int minCost = INT_MAX; std::vector selectedSpells; const ESM::Race* race = NULL; if (mRaceSelected) race = esmStore.get().find(player->mRace); int skills[ESM::Skill::Length]; for (int i=0; i &spells = esmStore.get(); for (MWWorld::Store::iterator iter = spells.begin(); iter != spells.end(); ++iter) { const ESM::Spell* spell = &*iter; if (spell->mData.mType != ESM::Spell::ST_Spell) continue; if (!(spell->mData.mFlags & ESM::Spell::F_PCStart)) continue; if (reachedLimit && spell->mData.mCost <= minCost) continue; if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end()) continue; if (baseMagicka < spell->mData.mCost) continue; static const float fAutoPCSpellChance = esmStore.get().find("fAutoPCSpellChance")->getFloat(); if (calcAutoCastChance(spell, skills, attributes, -1) < fAutoPCSpellChance) continue; if (!attrSkillCheck(spell, skills, attributes)) continue; selectedSpells.push_back(spell->mId); if (reachedLimit) { std::vector::iterator it = std::find(selectedSpells.begin(), selectedSpells.end(), weakestSpell->mId); if (it != selectedSpells.end()) selectedSpells.erase(it); minCost = INT_MAX; for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = esmStore.get().find(*weakIt); if (testSpell->mData.mCost < minCost) { minCost = testSpell->mData.mCost; weakestSpell = testSpell; } } } else { if (spell->mData.mCost < minCost) { weakestSpell = spell; minCost = weakestSpell->mData.mCost; } static const unsigned int iAutoPCSpellMax = esmStore.get().find("iAutoPCSpellMax")->getInt(); if (selectedSpells.size() == iAutoPCSpellMax) reachedLimit = true; } } for (std::vector::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it) creatureStats.getSpells().add(*it); // forced update and current value adjustments mActors.updateActor (ptr, 0); for (int i=0; i<3; ++i) { DynamicStat stat = creatureStats.getDynamic (i); stat.setCurrent (stat.getModified()); creatureStats.setDynamic (i, stat); } // auto-equip again. we need this for when the race is changed to a beast race and shoes are no longer equippable MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); for (int i=0; igetTimeScaleFactor(); MWWorld::Ptr player = getPlayer(); player.getClass().getInventoryStore(player).rechargeItems(duration); } void MechanicsManager::update(float duration, bool paused) { if(!mWatched.isEmpty()) { MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); const MWMechanics::NpcStats &stats = mWatched.getClass().getNpcStats(mWatched); for(int i = 0;i < ESM::Attribute::Length;++i) { if(stats.getAttribute(i) != mWatchedStats.getAttribute(i) || mWatchedStatsEmpty) { std::stringstream attrname; attrname << "AttribVal"<<(i+1); mWatchedStats.setAttribute(i, stats.getAttribute(i)); winMgr->setValue(attrname.str(), stats.getAttribute(i)); } } if(stats.getHealth() != mWatchedStats.getHealth() || mWatchedStatsEmpty) { static const std::string hbar("HBar"); mWatchedStats.setHealth(stats.getHealth()); winMgr->setValue(hbar, stats.getHealth()); } if(stats.getMagicka() != mWatchedStats.getMagicka() || mWatchedStatsEmpty) { static const std::string mbar("MBar"); mWatchedStats.setMagicka(stats.getMagicka()); winMgr->setValue(mbar, stats.getMagicka()); } if(stats.getFatigue() != mWatchedStats.getFatigue() || mWatchedStatsEmpty) { static const std::string fbar("FBar"); mWatchedStats.setFatigue(stats.getFatigue()); winMgr->setValue(fbar, stats.getFatigue()); } if(stats.getTimeToStartDrowning() != mWatchedStats.getTimeToStartDrowning()) { const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get() .find("fHoldBreathTime")->getFloat(); mWatchedStats.setTimeToStartDrowning(stats.getTimeToStartDrowning()); if(stats.getTimeToStartDrowning() >= fHoldBreathTime) winMgr->setDrowningBarVisibility(false); else { winMgr->setDrowningBarVisibility(true); winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning(), fHoldBreathTime); } } bool update = false; //Loop over ESM::Skill::SkillEnum for(int i = 0; i < ESM::Skill::Length; ++i) { if(stats.getSkill(i) != mWatchedStats.getSkill(i) || mWatchedStatsEmpty) { update = true; mWatchedStats.getSkill(i) = stats.getSkill(i); winMgr->setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i)); } } if(update) winMgr->updateSkillArea(); winMgr->setValue("level", stats.getLevel()); mWatchedStatsEmpty = false; // Update the equipped weapon icon MWWorld::InventoryStore& inv = mWatched.getClass().getInventoryStore(mWatched); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weapon == inv.end()) winMgr->unsetSelectedWeapon(); else winMgr->setSelectedWeapon(*weapon); // Update the selected spell icon MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem(); if (enchantItem != inv.end()) winMgr->setSelectedEnchantItem(*enchantItem); else if (!winMgr->getSelectedSpell().empty()) winMgr->setSelectedSpell(winMgr->getSelectedSpell(), int(MWMechanics::getSpellSuccessChance(winMgr->getSelectedSpell(), mWatched))); else winMgr->unsetSelectedSpell(); } if (mUpdatePlayer) { MWBase::World *world = MWBase::Environment::get().getWorld(); // basic player profile; should not change anymore after the creation phase is finished. MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); const ESM::NPC *player = world->getPlayerPtr().get()->mBase; const ESM::Race *race = world->getStore().get().find(player->mRace); const ESM::Class *cls = world->getStore().get().find(player->mClass); winMgr->setValue ("name", player->mName); winMgr->setValue ("race", race->mName); winMgr->setValue ("class", cls->mName); mUpdatePlayer = false; MWBase::WindowManager::SkillList majorSkills (5); MWBase::WindowManager::SkillList minorSkills (5); for (int i=0; i<5; ++i) { minorSkills[i] = cls->mData.mSkills[i][0]; majorSkills[i] = cls->mData.mSkills[i][1]; } winMgr->configureSkills (majorSkills, minorSkills); // HACK? The player has been changed, so a new Animation object may // have been made for them. Make sure they're properly updated. MWWorld::Ptr ptr = getPlayer(); mActors.removeActor(ptr); mActors.addActor(ptr, true); } mActors.update(duration, paused); mObjects.update(duration, paused); } void MechanicsManager::rest(bool sleep) { mActors.restoreDynamicStats (sleep); mActors.fastForwardAi(); } int MechanicsManager::getHoursToRest() const { return mActors.getHoursToRest(mWatched); } void MechanicsManager::setPlayerName (const std::string& name) { MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = *world->getPlayerPtr().get()->mBase; player.mName = name; world->createRecord(player); mUpdatePlayer = true; } void MechanicsManager::setPlayerRace (const std::string& race, bool male, const std::string &head, const std::string &hair) { MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = *world->getPlayerPtr().get()->mBase; player.mRace = race; player.mHead = head; player.mHair = hair; player.setIsMale(male); world->createRecord(player); mRaceSelected = true; buildPlayer(); mUpdatePlayer = true; } void MechanicsManager::setPlayerBirthsign (const std::string& id) { MWBase::Environment::get().getWorld()->getPlayer().setBirthSign(id); buildPlayer(); mUpdatePlayer = true; } void MechanicsManager::setPlayerClass (const std::string& id) { MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = *world->getPlayerPtr().get()->mBase; player.mClass = id; world->createRecord(player); mClassSelected = true; buildPlayer(); mUpdatePlayer = true; } void MechanicsManager::setPlayerClass (const ESM::Class &cls) { MWBase::World *world = MWBase::Environment::get().getWorld(); const ESM::Class *ptr = world->createRecord(cls); ESM::NPC player = *world->getPlayerPtr().get()->mBase; player.mClass = ptr->mId; world->createRecord(player); mClassSelected = true; buildPlayer(); mUpdatePlayer = true; } int MechanicsManager::getDerivedDisposition(const MWWorld::Ptr& ptr) { const MWMechanics::NpcStats& npcSkill = ptr.getClass().getNpcStats(ptr); float x = static_cast(npcSkill.getBaseDisposition()); MWWorld::LiveCellRef* npc = ptr.get(); MWWorld::Ptr playerPtr = getPlayer(); MWWorld::LiveCellRef* player = playerPtr.get(); const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); static const float fDispRaceMod = gmst.find("fDispRaceMod")->getFloat(); if (Misc::StringUtils::ciEqual(npc->mBase->mRace, player->mBase->mRace)) x += fDispRaceMod; static const float fDispPersonalityMult = gmst.find("fDispPersonalityMult")->getFloat(); static const float fDispPersonalityBase = gmst.find("fDispPersonalityBase")->getFloat(); x += fDispPersonalityMult * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - fDispPersonalityBase); float reaction = 0; int rank = 0; std::string npcFaction = ptr.getClass().getPrimaryFaction(ptr); Misc::StringUtils::lowerCaseInPlace(npcFaction); if (playerStats.getFactionRanks().find(npcFaction) != playerStats.getFactionRanks().end()) { if (!playerStats.getExpelled(npcFaction)) { // faction reaction towards itself. yes, that exists reaction = static_cast(MWBase::Environment::get().getDialogueManager()->getFactionReaction(npcFaction, npcFaction)); rank = playerStats.getFactionRanks().find(npcFaction)->second; } } else if (!npcFaction.empty()) { std::map::const_iterator playerFactionIt = playerStats.getFactionRanks().begin(); for (; playerFactionIt != playerStats.getFactionRanks().end(); ++playerFactionIt) { std::string itFaction = playerFactionIt->first; int itReaction = MWBase::Environment::get().getDialogueManager()->getFactionReaction(npcFaction, itFaction); if (playerFactionIt == playerStats.getFactionRanks().begin() || itReaction < reaction) reaction = static_cast(itReaction); } } else { reaction = 0; rank = 0; } static const float fDispFactionRankMult = gmst.find("fDispFactionRankMult")->getFloat(); static const float fDispFactionRankBase = gmst.find("fDispFactionRankBase")->getFloat(); static const float fDispFactionMod = gmst.find("fDispFactionMod")->getFloat(); x += (fDispFactionRankMult * rank + fDispFactionRankBase) * fDispFactionMod * reaction; static const float fDispCrimeMod = gmst.find("fDispCrimeMod")->getFloat(); static const float fDispDiseaseMod = gmst.find("fDispDiseaseMod")->getFloat(); x -= fDispCrimeMod * playerStats.getBounty(); if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease()) x += fDispDiseaseMod; static const float fDispWeaponDrawn = gmst.find("fDispWeaponDrawn")->getFloat(); if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon) x += fDispWeaponDrawn; x += ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Charm).getMagnitude(); int effective_disposition = std::max(0,std::min(int(x),100));//, normally clamped to [0..100] when used return effective_disposition; } int MechanicsManager::getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) { if (ptr.getTypeName() == typeid(ESM::Creature).name()) return basePrice; const MWMechanics::NpcStats &sellerStats = ptr.getClass().getNpcStats(ptr); MWWorld::Ptr playerPtr = getPlayer(); const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr); // I suppose the temporary disposition change _has_ to be considered here, // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = std::max(0, std::min(getDerivedDisposition(ptr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(),100)); float a = static_cast(std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100)); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float d = static_cast(std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100)); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); float npcTerm = (d + e + f) * sellerStats.getFatigueTerm(); float buyTerm = 0.01f * (100 - 0.5f * (pcTerm - npcTerm)); float sellTerm = 0.01f * (50 - 0.5f * (npcTerm - pcTerm)); float x; if(buying) x = buyTerm; else x = std::min(buyTerm, sellTerm); int offerPrice; if (x < 1) offerPrice = int(x * basePrice); else offerPrice = basePrice + int((x - 1) * basePrice); offerPrice = std::max(1, offerPrice); return offerPrice; } int MechanicsManager::countDeaths (const std::string& id) const { return mActors.countDeaths (id); } void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); MWMechanics::NpcStats& npcStats = npc.getClass().getNpcStats(npc); MWWorld::Ptr playerPtr = getPlayer(); const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr); float npcRating1, npcRating2, npcRating3; getPersuasionRatings(npcStats, npcRating1, npcRating2, npcRating3, false); float playerRating1, playerRating2, playerRating3; getPersuasionRatings(playerStats, playerRating1, playerRating2, playerRating3, true); int currentDisposition = std::min(100, std::max(0, int(getDerivedDisposition(npc) + currentTemporaryDispositionDelta))); float d = 1 - 0.02f * abs(currentDisposition - 50); float target1 = d * (playerRating1 - npcRating1 + 50); float target2 = d * (playerRating2 - npcRating2 + 50); float bribeMod; if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat(); else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); else bribeMod = gmst.find("fBribe1000Mod")->getFloat(); float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod; float iPerMinChance = floor(gmst.find("iPerMinChance")->getFloat()); float iPerMinChange = floor(gmst.find("iPerMinChange")->getFloat()); float fPerDieRollMult = gmst.find("fPerDieRollMult")->getFloat(); float fPerTempMult = gmst.find("fPerTempMult")->getFloat(); float x = 0; float y = 0; int roll = Misc::Rng::roll0to99(); if (type == PT_Admire) { target1 = std::max(iPerMinChance, target1); success = (roll <= target1); float c = floor(fPerDieRollMult * (target1 - roll)); x = success ? std::max(iPerMinChange, c) : c; } else if (type == PT_Intimidate) { target2 = std::max(iPerMinChance, target2); success = (roll <= target2); float r; if (roll != target2) r = floor(target2 - roll); else r = 1; if (roll <= target2) { float s = floor(r * fPerDieRollMult * fPerTempMult); int flee = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Flee).getBase(); int fight = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Fight).getBase(); npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, std::max(0, std::min(100, flee + int(std::max(iPerMinChange, s))))); npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, std::max(0, std::min(100, fight + int(std::min(-iPerMinChange, -s))))); } float c = -std::abs(floor(r * fPerDieRollMult)); if (success) { if (std::abs(c) < iPerMinChange) { x = 0; y = -iPerMinChange; } else { x = -floor(c * fPerTempMult); y = c; } } else { x = floor(c * fPerTempMult); y = c; } } else if (type == PT_Taunt) { target1 = std::max(iPerMinChance, target1); success = (roll <= target1); float c = std::abs(floor(target1 - roll)); if (success) { float s = c * fPerDieRollMult * fPerTempMult; int flee = npcStats.getAiSetting (CreatureStats::AI_Flee).getBase(); int fight = npcStats.getAiSetting (CreatureStats::AI_Fight).getBase(); npcStats.setAiSetting (CreatureStats::AI_Flee, std::max(0, std::min(100, flee + std::min(-int(iPerMinChange), int(-s))))); npcStats.setAiSetting (CreatureStats::AI_Fight, std::max(0, std::min(100, fight + std::max(int(iPerMinChange), int(s))))); } x = floor(-c * fPerDieRollMult); if (success && std::abs(x) < iPerMinChange) x = -iPerMinChange; } else // Bribe { target3 = std::max(iPerMinChance, target3); success = (roll <= target3); float c = floor((target3 - roll) * fPerDieRollMult); x = success ? std::max(iPerMinChange, c) : c; } tempChange = type == PT_Intimidate ? x : int(x * fPerTempMult); float cappedDispositionChange = tempChange; if (currentDisposition + tempChange > 100.f) cappedDispositionChange = static_cast(100 - currentDisposition); if (currentDisposition + tempChange < 0.f) cappedDispositionChange = static_cast(-currentDisposition); permChange = floor(cappedDispositionChange / fPerTempMult); if (type == PT_Intimidate) { permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : y; } } void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr) { if(ptr.getClass().isActor()) mActors.forceStateUpdate(ptr); } bool MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { if(ptr.getClass().isActor()) return mActors.playAnimationGroup(ptr, groupName, mode, number); else return mObjects.playAnimationGroup(ptr, groupName, mode, number); } void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr) { if(ptr.getClass().isActor()) mActors.skipAnimation(ptr); else mObjects.skipAnimation(ptr); } bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName) { if(ptr.getClass().isActor()) return mActors.checkAnimationPlaying(ptr, groupName); else return false; } void MechanicsManager::updateMagicEffects(const MWWorld::Ptr &ptr) { mActors.updateMagicEffects(ptr); } bool MechanicsManager::toggleAI() { mAI = !mAI; return mAI; } bool MechanicsManager::isAIActive() { return mAI; } void MechanicsManager::playerLoaded() { mUpdatePlayer = true; mClassSelected = true; mRaceSelected = true; mAI = true; } bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim) { const std::string& owner = cellref.getOwner(); bool isOwned = !owner.empty() && owner != "player"; const std::string& faction = cellref.getFaction(); bool isFactionOwned = false; if (!faction.empty() && ptr.getClass().isNpc()) { const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); std::map::const_iterator found = factions.find(Misc::StringUtils::lowerCase(faction)); if (found == factions.end() || found->second < cellref.getFactionRank()) isFactionOwned = true; } const std::string& globalVariable = cellref.getGlobalVariable(); if (!globalVariable.empty() && MWBase::Environment::get().getWorld()->getGlobalInt(Misc::StringUtils::lowerCase(globalVariable)) == 1) { isOwned = false; isFactionOwned = false; } if (!cellref.getOwner().empty()) victim = MWBase::Environment::get().getWorld()->searchPtr(cellref.getOwner(), true); return (!isOwned && !isFactionOwned); } bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed) { if (ptr.getClass().getNpcStats(ptr).isWerewolf()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); return true; } if(MWMechanics::isPlayerInCombat()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); return true; } MWWorld::Ptr victim; if (isAllowedToUse(ptr, bed.getCellRef(), victim)) return false; if(commitCrime(ptr, victim, OT_SleepingInOwnedBed)) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage64}"); return true; } else return false; } void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item) { MWWorld::Ptr victim; if (isAllowedToUse(ptr, item.getCellRef(), victim)) return; commitCrime(ptr, victim, OT_Trespassing); } std::vector > MechanicsManager::getStolenItemOwners(const std::string& itemid) { std::vector > result; StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid)); if (it == mStolenItems.end()) return result; else { const OwnerMap& owners = it->second; for (OwnerMap::const_iterator ownerIt = owners.begin(); ownerIt != owners.end(); ++ownerIt) result.push_back(std::make_pair(ownerIt->first.first, ownerIt->second)); return result; } } bool MechanicsManager::isItemStolenFrom(const std::string &itemid, const std::string &ownerid) { StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid)); if (it == mStolenItems.end()) return false; const OwnerMap& owners = it->second; OwnerMap::const_iterator ownerFound = owners.find(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false)); return ownerFound != owners.end(); } void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer) { MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getCellRef().getRefId())); if (stolenIt == mStolenItems.end()) continue; OwnerMap& owners = stolenIt->second; int itemCount = it->getRefData().getCount(); for (OwnerMap::iterator ownerIt = owners.begin(); ownerIt != owners.end();) { int toRemove = std::min(itemCount, ownerIt->second); itemCount -= toRemove; ownerIt->second -= toRemove; if (ownerIt->second == 0) owners.erase(ownerIt++); else ++ownerIt; } int toMove = it->getRefData().getCount() - itemCount; targetContainer.getClass().getContainerStore(targetContainer).add(*it, toMove, targetContainer); store.remove(*it, toMove, player); } // TODO: unhardcode the locklevel targetContainer.getClass().lock(targetContainer,50); } void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, const MWWorld::Ptr& container, int count) { if (ptr != getPlayer()) return; MWWorld::Ptr victim; const MWWorld::CellRef* ownerCellRef = &item.getCellRef(); if (!container.isEmpty()) { // Inherit the owner of the container ownerCellRef = &container.getCellRef(); } else { if (!item.getCellRef().hasContentFile()) { // this is a manually placed item, which means it was already stolen return; } } if (isAllowedToUse(ptr, *ownerCellRef, victim)) return; Owner owner; owner.first = ownerCellRef->getOwner(); owner.second = false; if (owner.first.empty()) { owner.first = ownerCellRef->getFaction(); owner.second = true; } Misc::StringUtils::lowerCaseInPlace(owner.first); if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId)) mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count; commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } void getFollowers (const MWWorld::Ptr& actor, std::set& out) { std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsSidingWith(actor); for(std::list::iterator it = followers.begin();it != followers.end();++it) { if (out.insert(*it).second) { getFollowers(*it, out); } } } bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg, bool victimAware) { // NOTE: victim may be empty // Only player can commit crime if (player != getPlayer()) return false; // Find all the actors within the alarm radius std::vector neighbors; osg::Vec3f from (player.getRefData().getPosition().asVec3()); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); float radius = esmStore.get().find("fAlarmRadius")->getFloat(); mActors.getObjectsInRange(from, radius, neighbors); // victim should be considered even beyond alarm radius if (!victim.isEmpty() && (from - victim.getRefData().getPosition().asVec3()).length2() > radius*radius) neighbors.push_back(victim); // get the player's followers / allies (works recursively) that will not report crimes std::set playerFollowers; getFollowers(player, playerFollowers); // Did anyone see it? bool crimeSeen = false; for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if (*it == player) continue; // skip player if (it->getClass().getCreatureStats(*it).isDead()) continue; if ((*it == victim && victimAware) || (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) // Murder crime can be reported even if no one saw it (hearing is enough, I guess). // TODO: Add mod support for stealth executions! || (type == OT_Murder && *it != victim)) { // Crime reporting only applies to NPCs if (!it->getClass().isNpc()) continue; if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) continue; if (playerFollowers.find(*it) != playerFollowers.end()) continue; if (type == OT_Theft || type == OT_Pickpocket) MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); else if (type == OT_Trespassing) MWBase::Environment::get().getDialogueManager()->say(*it, "intruder"); crimeSeen = true; } } if (crimeSeen) reportCrime(player, victim, type, arg); else if (type == OT_Assault && !victim.isEmpty()) startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee? return crimeSeen; } void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); if (type == OT_Murder && !victim.isEmpty()) victim.getClass().getCreatureStats(victim).notifyMurder(); // Bounty and disposition penalty for each type of crime float disp = 0.f, dispVictim = 0.f; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) { arg = store.find("iCrimeTresspass")->getInt(); disp = dispVictim = store.find("iDispTresspass")->getFloat(); } else if (type == OT_Pickpocket) { arg = store.find("iCrimePickPocket")->getInt(); disp = dispVictim = store.find("fDispPickPocketMod")->getFloat(); } else if (type == OT_Assault) { arg = store.find("iCrimeAttack")->getInt(); disp = store.find("iDispAttackMod")->getFloat(); dispVictim = store.find("fDispAttacking")->getFloat(); } else if (type == OT_Murder) { arg = store.find("iCrimeKilling")->getInt(); disp = dispVictim = store.find("iDispKilling")->getFloat(); } else if (type == OT_Theft) { disp = dispVictim = store.find("fDispStealing")->getFloat() * arg; arg = static_cast(arg * store.find("fCrimeStealing")->getFloat()); arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen } // Make surrounding actors within alarm distance respond to the crime std::vector neighbors; const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); osg::Vec3f from (player.getRefData().getPosition().asVec3()); float radius = esmStore.get().find("fAlarmRadius")->getFloat(); mActors.getObjectsInRange(from, radius, neighbors); // victim should be considered even beyond alarm radius if (!victim.isEmpty() && (from - victim.getRefData().getPosition().asVec3()).length2() > radius*radius) neighbors.push_back(victim); int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); // What amount of provocation did this crime generate? // Controls whether witnesses will engage combat with the criminal. int fight = 0, fightVictim = 0; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) fight = fightVictim = esmStore.get().find("iFightTrespass")->getInt(); else if (type == OT_Pickpocket) { fight = esmStore.get().find("iFightPickpocket")->getInt(); fightVictim = esmStore.get().find("iFightPickpocket")->getInt() * 4; // *4 according to research wiki } else if (type == OT_Assault) { fight = esmStore.get().find("iFightAttacking")->getInt(); fightVictim = esmStore.get().find("iFightAttack")->getInt(); } else if (type == OT_Murder) fight = fightVictim = esmStore.get().find("iFightKilling")->getInt(); else if (type == OT_Theft) fight = fightVictim = esmStore.get().find("fFightStealing")->getInt(); bool reported = false; // Tell everyone (including the original reporter) in alarm range for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if ( *it == player || !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue; if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) continue; // Will the witness report the crime? if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100) { reported = true; } if (it->getClass().isClass(*it, "guard")) { // Mark as Alarmed for dialogue it->getClass().getCreatureStats(*it).setAlarmed(true); // Set the crime ID, which we will use to calm down participants // once the bounty has been paid. it->getClass().getNpcStats(*it).setCrimeId(id); it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it); } else { float dispTerm = (*it == victim) ? dispVictim : disp; float alarmTerm = 0.01f * it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase(); if (type == OT_Pickpocket && alarmTerm <= 0) alarmTerm = 1.0; if (*it != victim) dispTerm *= alarmTerm; float fightTerm = static_cast((*it == victim) ? fightVictim : fight); fightTerm += getFightDispositionBias(dispTerm); fightTerm += getFightDistanceBias(*it, player); fightTerm *= alarmTerm; int observerFightRating = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase(); if (observerFightRating + fightTerm > 100) fightTerm = static_cast(100 - observerFightRating); fightTerm = std::max(0.f, fightTerm); if (observerFightRating + fightTerm >= 100) { startCombat(*it, player); NpcStats& observerStats = it->getClass().getNpcStats(*it); // Apply aggression value to the base Fight rating, so that the actor can continue fighting // after a Calm spell wears off observerStats.setAiSetting(CreatureStats::AI_Fight, observerFightRating + static_cast(fightTerm)); observerStats.setBaseDisposition(observerStats.getBaseDisposition() + static_cast(dispTerm)); // Set the crime ID, which we will use to calm down participants // once the bounty has been paid. observerStats.setCrimeId(id); // Mark as Alarmed for dialogue observerStats.setAlarmed(true); } } } if (reported) { MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty() + arg); // If committing a crime against a faction member, expell from the faction if (!victim.isEmpty() && victim.getClass().isNpc()) { std::string factionID = victim.getClass().getPrimaryFaction(victim); const std::map& playerRanks = player.getClass().getNpcStats(player).getFactionRanks(); if (playerRanks.find(Misc::StringUtils::lowerCase(factionID)) != playerRanks.end()) { player.getClass().getNpcStats(player).expell(factionID); } } if (type == OT_Assault && !victim.isEmpty() && !victim.getClass().getCreatureStats(victim).getAiSequence().isInCombat(player) && victim.getClass().isNpc()) { // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. // Note: accidental or collateral damage attacks are ignored. startCombat(victim, player); // Set the crime ID, which we will use to calm down participants // once the bounty has been paid. victim.getClass().getNpcStats(victim).setCrimeId(id); } } } bool MechanicsManager::actorAttacked(const MWWorld::Ptr &ptr, const MWWorld::Ptr &attacker) { if (ptr == getPlayer()) return false; std::list followers = getActorsSidingWith(attacker); MWMechanics::CreatureStats& targetStats = ptr.getClass().getCreatureStats(ptr); if (std::find(followers.begin(), followers.end(), ptr) != followers.end()) { targetStats.friendlyHit(); if (targetStats.getFriendlyHits() < 4) { MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); return false; } } // Attacking an NPC that is already in combat with any other NPC is not a crime AiSequence& seq = targetStats.getAiSequence(); bool isFightingNpc = false; for (std::list::const_iterator it = seq.begin(); it != seq.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { MWWorld::Ptr target = static_cast(*it)->getTarget(); if (!target.isEmpty() && target.getClass().isNpc()) isFightingNpc = true; } } if (ptr.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) && !isAggressive(ptr, attacker) && !isFightingNpc) commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr) || attacker == getPlayer()) && !seq.isInCombat(attacker)) { // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. // Note: accidental or collateral damage attacks are ignored. startCombat(ptr, attacker); } return true; } void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker) { if (attacker.isEmpty() || attacker != getPlayer()) return; if (victim == attacker) return; // known to happen if (!victim.getClass().isNpc()) return; // TODO: implement animal rights const MWMechanics::NpcStats& victimStats = victim.getClass().getNpcStats(victim); // Simple check for who attacked first: if the player attacked first, a crimeId should be set // Doesn't handle possible edge case where no one reported the assault, but in such a case, // for bystanders it is not possible to tell who attacked first, anyway. if (victimStats.getCrimeId() != -1) commitCrime(attacker, victim, MWBase::MechanicsManager::OT_Murder); } bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) { if (observer.getClass().getCreatureStats(observer).isDead() || !observer.getRefData().isEnabled()) return false; const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); float invisibility = stats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude(); if (invisibility > 0) return false; float sneakTerm = 0; if (ptr.getClass().getCreatureStats(ptr).getStance(CreatureStats::Stance_Sneak) && !MWBase::Environment::get().getWorld()->isSwimming(ptr) && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float bootWeight = 0; if (ptr.getClass().isNpc()) { MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); MWWorld::ContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); if (it != inv.end()) bootWeight = it->getClass().getWeight(*it); } sneakTerm = fSneakSkillMult * sneak + 0.2f * agility + 0.1f * luck + bootWeight * fSneakBootMult; } static float fSneakDistBase = store.find("fSneakDistanceBase")->getFloat(); static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->getFloat(); osg::Vec3f pos1 (ptr.getRefData().getPosition().asVec3()); osg::Vec3f pos2 (observer.getRefData().getPosition().asVec3()); float distTerm = fSneakDistBase + fSneakDistMult * (pos1 - pos2).length(); float chameleon = stats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude(); float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility; CreatureStats& observerStats = observer.getClass().getCreatureStats(observer); int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude(); int obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind; // is ptr behind the observer? static float fSneakNoViewMult = store.find("fSneakNoViewMult")->getFloat(); static float fSneakViewMult = store.find("fSneakViewMult")->getFloat(); float y = 0; osg::Vec3f vec = pos1 - pos2; if (observer.getRefData().getBaseNode()) { osg::Vec3f observerDir = (observer.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0)); float angleRadians = std::acos(observerDir * vec / (observerDir.length() * vec.length())); if (angleRadians > osg::DegreesToRadians(90.f)) y = obsTerm * observerStats.getFatigueTerm() * fSneakNoViewMult; else y = obsTerm * observerStats.getFatigueTerm() * fSneakViewMult; } float target = x - y; return (Misc::Rng::roll0to99() >= target); } void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) { if (ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(target)) return; ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr); if (target == getPlayer()) { // if guard starts combat with player, guards pursuing player should do the same if (ptr.getClass().isClass(ptr, "Guard")) { for (Actors::PtrActorMap::const_iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { if (iter->first.getClass().isClass(iter->first, "Guard")) { MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence(); if (aiSeq.getTypeId() == MWMechanics::AiPackage::TypeIdPursue) { aiSeq.stopPursuit(); aiSeq.stack(MWMechanics::AiCombat(target), ptr); } } } } } // Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly if (ptr.getClass().isNpc() && !ptr.getClass().getCreatureStats(ptr).isDead()) MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); } void MechanicsManager::getObjectsInRange(const osg::Vec3f &position, float radius, std::vector &objects) { mActors.getObjectsInRange(position, radius, objects); mObjects.getObjectsInRange(position, radius, objects); } void MechanicsManager::getActorsInRange(const osg::Vec3f &position, float radius, std::vector &objects) { mActors.getObjectsInRange(position, radius, objects); } std::list MechanicsManager::getActorsSidingWith(const MWWorld::Ptr& actor) { return mActors.getActorsSidingWith(actor); } std::list MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor) { return mActors.getActorsFollowing(actor); } std::list MechanicsManager::getActorsFollowingIndices(const MWWorld::Ptr& actor) { return mActors.getActorsFollowingIndices(actor); } std::list MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) { return mActors.getActorsFighting(actor); } int MechanicsManager::countSavedGameRecords() const { return 1 // Death counter +1; // Stolen items } void MechanicsManager::write(ESM::ESMWriter &writer, Loading::Listener &listener) const { mActors.write(writer, listener); ESM::StolenItems items; items.mStolenItems = mStolenItems; writer.startRecord(ESM::REC_STLN); items.write(writer); writer.endRecord(ESM::REC_STLN); } void MechanicsManager::readRecord(ESM::ESMReader &reader, uint32_t type) { if (type == ESM::REC_STLN) { ESM::StolenItems items; items.load(reader); mStolenItems = items.mStolenItems; } else mActors.readRecord(reader, type); } void MechanicsManager::clear() { mActors.clear(); mStolenItems.clear(); } bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) { int disposition = 50; if (ptr.getClass().isNpc()) disposition = getDerivedDisposition(ptr); int fight = std::max(0, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() + static_cast(getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast(disposition)))); if (ptr.getClass().isNpc() && target.getClass().isNpc()) { if (target.getClass().getNpcStats(target).isWerewolf() || (target == getPlayer() && MWBase::Environment::get().getWorld()->getGlobalInt("pcknownwerewolf"))) { const ESM::GameSetting * iWerewolfFightMod = MWBase::Environment::get().getWorld()->getStore().get().search("iWerewolfFightMod"); fight += iWerewolfFightMod->getInt(); } } return (fight >= 100); } void MechanicsManager::keepPlayerAlive() { MWWorld::Ptr player = getPlayer(); CreatureStats& stats = player.getClass().getCreatureStats(player); if (stats.isDead()) { MWMechanics::DynamicStat stat (stats.getHealth()); if (stat.getModified()<1) { stat.setModified(1, 0); stats.setHealth(stat); } stats.resurrect(); } } bool MechanicsManager::isReadyToBlock(const MWWorld::Ptr &ptr) const { return mActors.isReadyToBlock(ptr); } void MechanicsManager::setWerewolf(const MWWorld::Ptr& actor, bool werewolf) { MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor); // The actor does not have to change state if (npcStats.isWerewolf() == werewolf) return; MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer(); if (actor == player->getPlayer()) { if (werewolf) { player->saveSkillsAttributes(); player->setWerewolfSkillsAttributes(); } else player->restoreSkillsAttributes(); } // Equipped items other than WerewolfRobe may reference bones that do not even // exist with the werewolf object root, so make sure to unequip all items // *before* we become a werewolf. MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); invStore.unequipAll(actor); // Werewolfs can not cast spells, so we need to unset the prepared spell if there is one. if (npcStats.getDrawState() == MWMechanics::DrawState_Spell) npcStats.setDrawState(MWMechanics::DrawState_Nothing); npcStats.setWerewolf(werewolf); if(werewolf) { MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor); inv.equip(MWWorld::InventoryStore::Slot_Robe, inv.ContainerStore::add("werewolfrobe", 1, actor), actor); } else { actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); } if(actor == player->getPlayer()) { MWBase::Environment::get().getWorld()->reattachPlayerCamera(); // Update the GUI only when called on the player MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); if (werewolf) { windowManager->forceHide(MWGui::GW_Inventory); windowManager->forceHide(MWGui::GW_Magic); } else { windowManager->unsetForceHide(MWGui::GW_Inventory); windowManager->unsetForceHide(MWGui::GW_Magic); } windowManager->setWerewolfOverlay(werewolf); // Witnesses of the player's transformation will make them a globally known werewolf std::vector closeActors; const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->getFloat(), closeActors); bool detected = false, reported = false; for (std::vector::const_iterator it = closeActors.begin(); it != closeActors.end(); ++it) { if (*it == actor) continue; if (!it->getClass().isNpc()) continue; if (MWBase::Environment::get().getWorld()->getLOS(*it, actor) && awarenessCheck(actor, *it)) detected = true; if (it->getClass().getCreatureStats(*it).getAiSetting(MWMechanics::CreatureStats::AI_Alarm).getModified() > 0) reported = true; } if (detected) { windowManager->messageBox("#{sWerewolfAlarmMessage}"); MWBase::Environment::get().getWorld()->setGlobalInt("pcknownwerewolf", 1); if (reported) { npcStats.setBounty(npcStats.getBounty()+ gmst.find("iWereWolfBounty")->getInt()); windowManager->messageBox("#{sCrimeMessage}"); } } } } void MechanicsManager::applyWerewolfAcrobatics(const MWWorld::Ptr &actor) { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); MWMechanics::NpcStats &stats = actor.getClass().getNpcStats(actor); stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->getInt()); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp000066400000000000000000000221561264522266000256770ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_MECHANICSMANAGERIMP_H #define GAME_MWMECHANICS_MECHANICSMANAGERIMP_H #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" #include "objects.hpp" #include "actors.hpp" namespace MWWorld { class CellStore; } namespace MWMechanics { class MechanicsManager : public MWBase::MechanicsManager { MWWorld::Ptr mWatched; NpcStats mWatchedStats; bool mWatchedStatsEmpty; bool mUpdatePlayer; bool mClassSelected; bool mRaceSelected; bool mAI;///< is AI active? Objects mObjects; Actors mActors; typedef std::pair Owner; // < Owner id, bool isFaction > typedef std::map OwnerMap; // < Owner, number of stolen items with this id from this owner > typedef std::map StolenItemsMap; StolenItemsMap mStolenItems; public: void buildPlayer(); ///< build player according to stored class/race/birthsign information. Will /// default to the values of the ESM::NPC object, if no explicit information is given. MechanicsManager(); virtual void add (const MWWorld::Ptr& ptr); ///< Register an object for management virtual void remove (const MWWorld::Ptr& ptr); ///< Deregister an object for management virtual void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr); ///< Moves an object to a new cell virtual void drop(const MWWorld::CellStore *cellStore); ///< Deregister all objects in the given cell. virtual void watchActor(const MWWorld::Ptr& ptr); ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. virtual void update (float duration, bool paused); ///< Update objects /// /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). virtual void advanceTime (float duration); virtual void setPlayerName (const std::string& name); ///< Set player name. virtual void setPlayerRace (const std::string& id, bool male, const std::string &head, const std::string &hair); ///< Set player race. virtual void setPlayerBirthsign (const std::string& id); ///< Set player birthsign. virtual void setPlayerClass (const std::string& id); ///< Set player class to stock class. virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. virtual void rest(bool sleep); ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? virtual int getHoursToRest() const; ///< Calculate how many hours the player needs to rest in order to be fully healed virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying); ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. virtual int getDerivedDisposition(const MWWorld::Ptr& ptr); ///< Calculate the diposition of an NPC toward the player. virtual int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange); void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer); /// Makes \a ptr fight \a target. Also shouts a combat taunt. virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target); /** * @note victim may be empty * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @param victimAware Is the victim already aware of the crime? * If this parameter is false, it will be determined by a line-of-sight and awareness check. * @return was the crime seen? */ virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0, bool victimAware=false); /// @return false if the attack was considered a "friendly hit" and forgiven virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); /// Notify that actor was killed, add a murder bounty if applicable /// @note No-op for non-player attackers virtual void actorKilled (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); /// Utility to check if taking this item is illegal and calling commitCrime if so /// @param container The container the item is in; may be empty for an item in the world virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container, int count); /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item); /// Attempt sleeping in a bed. If this is illegal, call commitCrime. /// @return was it illegal, and someone saw you doing it? Also returns fail when enemies are nearby virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed); virtual void forceStateUpdate(const MWWorld::Ptr &ptr); /// Attempt to play an animation group /// @return Success or error virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); virtual void skipAnimation(const MWWorld::Ptr& ptr); virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName); /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) virtual void updateMagicEffects (const MWWorld::Ptr& ptr); virtual void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector& objects); virtual void getActorsInRange(const osg::Vec3f &position, float radius, std::vector &objects); virtual std::list getActorsSidingWith(const MWWorld::Ptr& actor); virtual std::list getActorsFollowing(const MWWorld::Ptr& actor); virtual std::list getActorsFollowingIndices(const MWWorld::Ptr& actor); virtual std::list getActorsFighting(const MWWorld::Ptr& actor); virtual bool toggleAI(); virtual bool isAIActive(); virtual void playerLoaded(); virtual int countSavedGameRecords() const; virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const; virtual void readRecord (ESM::ESMReader& reader, uint32_t type); virtual void clear(); virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target); virtual void keepPlayerAlive(); virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const; virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer); /// List the owners that the player has stolen this item from (the owner can be an NPC or a faction). /// virtual std::vector > getStolenItemOwners(const std::string& itemid); /// Has the player stolen this item from the given owner? virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid); /// @return is \a ptr allowed to take/use \a cellref or is it a crime? virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim); virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf); virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor); private: void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/movement.hpp000066400000000000000000000010401264522266000235230ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_MOVEMENT_H #define GAME_MWMECHANICS_MOVEMENT_H #include namespace MWMechanics { /// Desired movement for an actor struct Movement { float mPosition[3]; float mRotation[3]; Movement() { mPosition[0] = mPosition[1] = mPosition[2] = 0.0f; mRotation[0] = mRotation[1] = mRotation[2] = 0.0f; } osg::Vec3f asVec3() { return osg::Vec3f(mPosition[0], mPosition[1], mPosition[2]); } }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/npcstats.cpp000066400000000000000000000357111264522266000235370ustar00rootroot00000000000000#include "npcstats.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" MWMechanics::NpcStats::NpcStats() : mDisposition (0) , mReputation(0) , mCrimeId(-1) , mBounty(0) , mWerewolfKills (0) , mLevelProgress(0) , mTimeToStartDrowning(20.0) , mIsWerewolf(false) { mSkillIncreases.resize (ESM::Attribute::Length, 0); } int MWMechanics::NpcStats::getBaseDisposition() const { return mDisposition; } void MWMechanics::NpcStats::setBaseDisposition(int disposition) { mDisposition = disposition; } const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) const { if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); return mSkill[index]; } MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) { if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); return mSkill[index]; } void MWMechanics::NpcStats::setSkill(int index, const MWMechanics::SkillValue &value) { if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); mSkill[index] = value; } const std::map& MWMechanics::NpcStats::getFactionRanks() const { return mFactionRank; } void MWMechanics::NpcStats::raiseRank(const std::string &faction) { const std::string lower = Misc::StringUtils::lowerCase(faction); std::map::iterator it = mFactionRank.find(lower); if (it != mFactionRank.end()) { // Does the next rank exist? const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get().find(lower); if (it->second+1 < 10 && !faction->mRanks[it->second+1].empty()) it->second += 1; } } void MWMechanics::NpcStats::lowerRank(const std::string &faction) { const std::string lower = Misc::StringUtils::lowerCase(faction); std::map::iterator it = mFactionRank.find(lower); if (it != mFactionRank.end()) { it->second = std::max(0, it->second-1); } } void MWMechanics::NpcStats::joinFaction(const std::string& faction) { const std::string lower = Misc::StringUtils::lowerCase(faction); std::map::iterator it = mFactionRank.find(lower); if (it == mFactionRank.end()) mFactionRank[lower] = 0; } bool MWMechanics::NpcStats::getExpelled(const std::string& factionID) const { return mExpelled.find(Misc::StringUtils::lowerCase(factionID)) != mExpelled.end(); } void MWMechanics::NpcStats::expell(const std::string& factionID) { std::string lower = Misc::StringUtils::lowerCase(factionID); if (mExpelled.find(lower) == mExpelled.end()) { std::string message = "#{sExpelledMessage}"; message += MWBase::Environment::get().getWorld()->getStore().get().find(factionID)->mName; MWBase::Environment::get().getWindowManager()->messageBox(message); mExpelled.insert(lower); } } void MWMechanics::NpcStats::clearExpelled(const std::string& factionID) { mExpelled.erase(Misc::StringUtils::lowerCase(factionID)); } bool MWMechanics::NpcStats::isInFaction (const std::string& faction) const { return (mFactionRank.find(Misc::StringUtils::lowerCase(faction)) != mFactionRank.end()); } int MWMechanics::NpcStats::getFactionReputation (const std::string& faction) const { std::map::const_iterator iter = mFactionReputation.find (Misc::StringUtils::lowerCase(faction)); if (iter==mFactionReputation.end()) return 0; return iter->second; } void MWMechanics::NpcStats::setFactionReputation (const std::string& faction, int value) { mFactionReputation[Misc::StringUtils::lowerCase(faction)] = value; } float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const ESM::Class& class_) const { float progressRequirement = static_cast(1 + getSkill(skillIndex).getBase()); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); float typeFactor = gmst.find ("fMiscSkillBonus")->getFloat(); for (int i=0; i<5; ++i) if (class_.mData.mSkills[i][0]==skillIndex) { typeFactor = gmst.find ("fMinorSkillBonus")->getFloat(); break; } for (int i=0; i<5; ++i) if (class_.mData.mSkills[i][1]==skillIndex) { typeFactor = gmst.find ("fMajorSkillBonus")->getFloat(); break; } progressRequirement *= typeFactor; if (typeFactor<=0) throw std::runtime_error ("invalid skill type factor"); float specialisationFactor = 1; const ESM::Skill *skill = MWBase::Environment::get().getWorld()->getStore().get().find (skillIndex); if (skill->mData.mSpecialization==class_.mData.mSpecialization) { specialisationFactor = gmst.find ("fSpecialSkillBonus")->getFloat(); if (specialisationFactor<=0) throw std::runtime_error ("invalid skill specialisation factor"); } progressRequirement *= specialisationFactor; return progressRequirement; } void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType, float extraFactor) { const ESM::Skill *skill = MWBase::Environment::get().getWorld()->getStore().get().find (skillIndex); float skillGain = 1; if (usageType>=4) throw std::runtime_error ("skill usage type out of range"); if (usageType>=0) { skillGain = skill->mData.mUseValue[usageType]; if (skillGain<0) throw std::runtime_error ("invalid skill gain factor"); } skillGain *= extraFactor; MWMechanics::SkillValue& value = getSkill (skillIndex); value.setProgress(value.getProgress() + skillGain); if (int(value.getProgress())>=int(getSkillProgressRequirement(skillIndex, class_))) { // skill levelled up increaseSkill(skillIndex, class_, false); } } void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress) { int base = getSkill (skillIndex).getBase(); if (base >= 100) return; base += 1; const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); // is this a minor or major skill? int increase = gmst.find("iLevelupMiscMultAttriubte")->getInt(); // Note: GMST has a typo for (int k=0; k<5; ++k) { if (class_.mData.mSkills[k][0] == skillIndex) { mLevelProgress += gmst.find("iLevelUpMinorMult")->getInt(); increase = gmst.find("iLevelUpMajorMultAttribute")->getInt(); } } for (int k=0; k<5; ++k) { if (class_.mData.mSkills[k][1] == skillIndex) { mLevelProgress += gmst.find("iLevelUpMajorMult")->getInt(); increase = gmst.find("iLevelUpMinorMultAttribute")->getInt(); } } const ESM::Skill* skill = MWBase::Environment::get().getWorld ()->getStore ().get().find(skillIndex); mSkillIncreases[skill->mData.mAttribute] += increase; // Play sound & skill progress notification /// \todo check if character is the player, if levelling is ever implemented for NPCs MWBase::Environment::get().getSoundManager ()->playSound ("skillraise", 1, 1); std::stringstream message; message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") % static_cast (base); MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), MWGui::ShowInDialogueMode_Never); if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) { // levelup is possible now MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never); } getSkill(skillIndex).setBase (base); if (!preserveProgress) getSkill(skillIndex).setProgress(0); } int MWMechanics::NpcStats::getLevelProgress () const { return mLevelProgress; } void MWMechanics::NpcStats::levelUp() { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); mLevelProgress -= gmst.find("iLevelUpTotal")->getInt(); mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console for (int i=0; igetFloat()); setLevel(getLevel()+1); } void MWMechanics::NpcStats::updateHealth() { const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); const int strength = getAttribute(ESM::Attribute::Strength).getBase(); setHealth(floor(0.5f * (strength + endurance))); } int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const { int num = mSkillIncreases[attribute]; if (num == 0) return 1; num = std::min(10, num); // iLevelUp01Mult - iLevelUp10Mult std::stringstream gmst; gmst << "iLevelUp" << std::setfill('0') << std::setw(2) << num << "Mult"; return MWBase::Environment::get().getWorld()->getStore().get().find(gmst.str())->getInt(); } void MWMechanics::NpcStats::flagAsUsed (const std::string& id) { mUsedIds.insert (id); } bool MWMechanics::NpcStats::hasBeenUsed (const std::string& id) const { return mUsedIds.find (id)!=mUsedIds.end(); } int MWMechanics::NpcStats::getBounty() const { return mBounty; } void MWMechanics::NpcStats::setBounty (int bounty) { mBounty = bounty; } int MWMechanics::NpcStats::getReputation() const { return mReputation; } void MWMechanics::NpcStats::setReputation(int reputation) { mReputation = reputation; } int MWMechanics::NpcStats::getCrimeId() const { return mCrimeId; } void MWMechanics::NpcStats::setCrimeId(int id) { mCrimeId = id; } bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int rank) const { if (rank<0 || rank>=10) throw std::runtime_error ("rank index out of range"); const ESM::Faction& faction = *MWBase::Environment::get().getWorld()->getStore().get().find (factionId); std::vector skills; for (int i=0; i<7; ++i) { if (faction.mData.mSkills[i] != -1) skills.push_back (static_cast (getSkill (faction.mData.mSkills[i]).getModified())); } if (skills.empty()) return true; std::sort (skills.begin(), skills.end()); std::vector::const_reverse_iterator iter = skills.rbegin(); const ESM::RankData& rankData = faction.mData.mRankData[rank]; if (*iter=rankData.mSkill2; } bool MWMechanics::NpcStats::isWerewolf() const { return mIsWerewolf; } void MWMechanics::NpcStats::setWerewolf (bool set) { if (mIsWerewolf == set) return; if(set != false) { mWerewolfKills = 0; } mIsWerewolf = set; } int MWMechanics::NpcStats::getWerewolfKills() const { return mWerewolfKills; } void MWMechanics::NpcStats::addWerewolfKill() { ++mWerewolfKills; } float MWMechanics::NpcStats::getTimeToStartDrowning() const { return mTimeToStartDrowning; } void MWMechanics::NpcStats::setTimeToStartDrowning(float time) { mTimeToStartDrowning=time; } void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const { for (std::map::const_iterator iter (mFactionRank.begin()); iter!=mFactionRank.end(); ++iter) state.mFactions[iter->first].mRank = iter->second; state.mDisposition = mDisposition; for (int i=0; i::const_iterator iter (mExpelled.begin()); iter!=mExpelled.end(); ++iter) state.mFactions[*iter].mExpelled = true; for (std::map::const_iterator iter (mFactionReputation.begin()); iter!=mFactionReputation.end(); ++iter) state.mFactions[iter->first].mReputation = iter->second; state.mReputation = mReputation; state.mWerewolfKills = mWerewolfKills; state.mLevelProgress = mLevelProgress; for (int i=0; igetStore(); for (std::map::const_iterator iter (state.mFactions.begin()); iter!=state.mFactions.end(); ++iter) if (store.get().search (iter->first)) { if (iter->second.mExpelled) mExpelled.insert (iter->first); if (iter->second.mRank >= 0) mFactionRank[iter->first] = iter->second.mRank; if (iter->second.mReputation) mFactionReputation[Misc::StringUtils::lowerCase(iter->first)] = iter->second.mReputation; } mDisposition = state.mDisposition; for (int i=0; i::const_iterator iter (state.mUsedIds.begin()); iter!=state.mUsedIds.end(); ++iter) if (store.find (*iter)) mUsedIds.insert (*iter); mTimeToStartDrowning = state.mTimeToStartDrowning; } openmw-openmw-0.38.0/apps/openmw/mwmechanics/npcstats.hpp000066400000000000000000000103561264522266000235420ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_NPCSTATS_H #define GAME_MWMECHANICS_NPCSTATS_H #include #include #include #include #include "creaturestats.hpp" namespace ESM { struct Class; struct NpcStats; } namespace MWMechanics { /// \brief Additional stats for NPCs class NpcStats : public CreatureStats { int mDisposition; SkillValue mSkill[ESM::Skill::Length]; // SkillValue.mProgress used by the player only int mReputation; int mCrimeId; // ----- used by the player only, maybe should be moved at some point ------- int mBounty; int mWerewolfKills; /// Used for the player only; NPCs have maximum one faction defined in their NPC record std::map mFactionRank; std::set mExpelled; std::map mFactionReputation; int mLevelProgress; // 0-10 std::vector mSkillIncreases; // number of skill increases for each attribute std::set mUsedIds; // --------------------------------------------------------------------------- /// Countdown to getting damage while underwater float mTimeToStartDrowning; bool mIsWerewolf; public: NpcStats(); int getBaseDisposition() const; void setBaseDisposition(int disposition); int getReputation() const; void setReputation(int reputation); int getCrimeId() const; void setCrimeId(int id); const SkillValue& getSkill (int index) const; SkillValue& getSkill (int index); void setSkill(int index, const SkillValue& value); const std::map& getFactionRanks() const; /// Increase the rank in this faction by 1, if such a rank exists. void raiseRank(const std::string& faction); /// Lower the rank in this faction by 1, if such a rank exists. void lowerRank(const std::string& faction); /// Join this faction, setting the initial rank to 0. void joinFaction(const std::string& faction); const std::set& getExpelled() const { return mExpelled; } bool getExpelled(const std::string& factionID) const; void expell(const std::string& factionID); void clearExpelled(const std::string& factionID); bool isInFaction (const std::string& faction) const; float getSkillProgressRequirement (int skillIndex, const ESM::Class& class_) const; void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1, float extraFactor=1.f); ///< Increase skill by usage. void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress); int getLevelProgress() const; int getLevelupAttributeMultiplier(int attribute) const; void levelUp(); void updateHealth(); ///< Calculate health based on endurance and strength. /// Called at character creation. void flagAsUsed (const std::string& id); ///< @note Id must be lower-case bool hasBeenUsed (const std::string& id) const; ///< @note Id must be lower-case int getBounty() const; void setBounty (int bounty); int getFactionReputation (const std::string& faction) const; void setFactionReputation (const std::string& faction, int value); bool hasSkillsForRank (const std::string& factionId, int rank) const; bool isWerewolf() const; void setWerewolf(bool set); int getWerewolfKills() const; /// Increments mWerewolfKills by 1. void addWerewolfKill(); float getTimeToStartDrowning() const; /// Sets time left for the creature to drown if it stays underwater. /// @param time value from [0,20] void setTimeToStartDrowning(float time); void writeState (ESM::NpcStats& state) const; void readState (const ESM::NpcStats& state); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/objects.cpp000066400000000000000000000052331264522266000233250ustar00rootroot00000000000000#include "objects.hpp" #include #include "movement.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" namespace MWMechanics { Objects::Objects() { } Objects::~Objects() { PtrControllerMap::iterator it(mObjects.begin()); for (; it != mObjects.end();++it) { delete it->second; it->second = NULL; } } void Objects::addObject(const MWWorld::Ptr& ptr) { removeObject(ptr); MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); if(anim) mObjects.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); } void Objects::removeObject(const MWWorld::Ptr& ptr) { PtrControllerMap::iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) { delete iter->second; mObjects.erase(iter); } } void Objects::updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) { PtrControllerMap::iterator iter = mObjects.find(old); if(iter != mObjects.end()) { CharacterController *ctrl = iter->second; mObjects.erase(iter); ctrl->updatePtr(ptr); mObjects.insert(std::make_pair(ptr, ctrl)); } } void Objects::dropObjects (const MWWorld::CellStore *cellStore) { PtrControllerMap::iterator iter = mObjects.begin(); while(iter != mObjects.end()) { if(iter->first.getCell()==cellStore) { delete iter->second; mObjects.erase(iter++); } else ++iter; } } void Objects::update(float duration, bool paused) { if(!paused) { for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter) iter->second->update(duration); } } bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { PtrControllerMap::iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) { return iter->second->playGroup(groupName, mode, number); } else { std::cerr<< "Error in Objects::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId() << std::endl; return false; } } void Objects::skipAnimation(const MWWorld::Ptr& ptr) { PtrControllerMap::iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) iter->second->skipAnim(); } void Objects::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector& out) { for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter) { if ((position - iter->first.getRefData().getPosition().asVec3()).length2() <= radius*radius) out.push_back(iter->first); } } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/objects.hpp000066400000000000000000000022721264522266000233320ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_ACTIVATORS_H #define GAME_MWMECHANICS_ACTIVATORS_H #include #include #include "character.hpp" namespace MWWorld { class Ptr; class CellStore; } namespace MWMechanics { class Objects { typedef std::map PtrControllerMap; PtrControllerMap mObjects; public: Objects(); ~Objects(); void addObject (const MWWorld::Ptr& ptr); ///< Register an animated object void removeObject (const MWWorld::Ptr& ptr); ///< Deregister an object void updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); ///< Updates an object with a new Ptr void dropObjects(const MWWorld::CellStore *cellStore); ///< Deregister all objects in the given cell. void update(float duration, bool paused); ///< Update object animations bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector& out); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/obstacle.cpp000066400000000000000000000157701264522266000234770ustar00rootroot00000000000000#include "obstacle.hpp" #include #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "movement.hpp" namespace MWMechanics { // NOTE: determined empirically but probably need further tweaking static const float DIST_SAME_SPOT = 0.5f; static const float DURATION_SAME_SPOT = 1.5f; static const float DURATION_TO_EVADE = 0.4f; const float ObstacleCheck::evadeDirections[NUM_EVADE_DIRECTIONS][2] = { { 1.0f, 0.0f }, // move to side { 1.0f, -1.0f }, // move to side and backwards { -1.0f, 0.0f }, // move to other side { -1.0f, -1.0f } // move to side and backwards }; // Proximity check function for interior doors. Given that most interior cells // do not have many doors performance shouldn't be too much of an issue. // // Limitation: there can be false detections, and does not test whether the // actor is facing the door. bool proximityToDoor(const MWWorld::Ptr& actor, float minSqr) { if(getNearbyDoor(actor, minSqr)!=MWWorld::Ptr()) return true; else return false; } MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minSqr) { MWWorld::CellStore *cell = actor.getCell(); if(cell->getCell()->isExterior()) return MWWorld::Ptr(); // check interior cells only // Check all the doors in this cell const MWWorld::CellRefList& doors = cell->getReadOnlyDoors(); const MWWorld::CellRefList::List& refList = doors.mList; MWWorld::CellRefList::List::const_iterator it = refList.begin(); osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); /// TODO: How to check whether the actor is facing a door? Below code is for /// the player, perhaps it can be adapted. //MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject(); //if(!ptr.isEmpty()) //std::cout << "faced door " << ptr.getClass().getName(ptr) << std::endl; /// TODO: The in-game observation of rot[2] value seems to be the /// opposite of the code in World::activateDoor() ::confused:: for (; it != refList.end(); ++it) { const MWWorld::LiveCellRef& ref = *it; if((pos - ref.mData.getPosition().asVec3()).length2() < minSqr && ref.mData.getPosition().rot[2] == ref.mRef.getPosition().rot[2]) { // FIXME cast return MWWorld::Ptr(&const_cast &>(ref), actor.getCell()); // found, stop searching } } return MWWorld::Ptr(); // none found } ObstacleCheck::ObstacleCheck(): mPrevX(0) // to see if the moved since last time , mPrevY(0) , mWalkState(State_Norm) , mStuckDuration(0) , mEvadeDuration(0) , mDistSameSpot(-1) // avoid calculating it each time , mEvadeDirectionIndex(0) { } void ObstacleCheck::clear() { mWalkState = State_Norm; mStuckDuration = 0; mEvadeDuration = 0; } bool ObstacleCheck::isNormalState() const { return mWalkState == State_Norm; } /* * input - actor, duration (time since last check) * output - true if evasive action needs to be taken * * Walking state transitions (player greeting check not shown): * * MoveNow <------------------------------------+ * | d| * | | * +-> State_Norm <---> State_CheckStuck --> State_Evade * ^ ^ | f ^ | t ^ | | * | | | | | | | | * | +---+ +---+ +---+ | u * | any < t < u | * +--------------------------------------------+ * * f = one reaction time * d = proximity to a closed door * t = how long before considered stuck * u = how long to move sideways * */ bool ObstacleCheck::check(const MWWorld::Ptr& actor, float duration) { const MWWorld::Class& cls = actor.getClass(); ESM::Position pos = actor.getRefData().getPosition(); // actors can move at most 60 fps (the physics framerate). // the max() can be removed if we implement physics interpolation. float movementDuration = std::max(1/60.f, duration); if(mDistSameSpot == -1) mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor); float distSameSpot = mDistSameSpot * movementDuration; bool samePosition = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2() < distSameSpot * distSameSpot; // update position mPrevX = pos.pos[0]; mPrevY = pos.pos[1]; switch(mWalkState) { case State_Norm: { if(!samePosition) break; else mWalkState = State_CheckStuck; } /* FALL THROUGH */ case State_CheckStuck: { if(!samePosition) { mWalkState = State_Norm; mStuckDuration = 0; break; } else { mStuckDuration += duration; // consider stuck only if position unchanges for a period if(mStuckDuration < DURATION_SAME_SPOT) break; // still checking, note duration added to timer else { mStuckDuration = 0; mWalkState = State_Evade; chooseEvasionDirection(); } } } /* FALL THROUGH */ case State_Evade: { mEvadeDuration += duration; if(mEvadeDuration < DURATION_TO_EVADE) return true; else { // tried to evade, assume all is ok and start again mWalkState = State_Norm; mEvadeDuration = 0; } } /* NO DEFAULT CASE */ } return false; // no obstacles to evade (yet) } void ObstacleCheck::takeEvasiveAction(MWMechanics::Movement& actorMovement) { actorMovement.mPosition[0] = evadeDirections[mEvadeDirectionIndex][0]; actorMovement.mPosition[1] = evadeDirections[mEvadeDirectionIndex][1]; } void ObstacleCheck::chooseEvasionDirection() { // change direction if attempt didn't work ++mEvadeDirectionIndex; if (mEvadeDirectionIndex == NUM_EVADE_DIRECTIONS) { mEvadeDirectionIndex = 0; } } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/obstacle.hpp000066400000000000000000000037471264522266000235050ustar00rootroot00000000000000#ifndef OPENMW_MECHANICS_OBSTACLE_H #define OPENMW_MECHANICS_OBSTACLE_H namespace MWWorld { class Ptr; } namespace MWMechanics { struct Movement; /// NOTE: determined empirically based on in-game behaviour static const float MIN_DIST_TO_DOOR_SQUARED = 128*128; static const int NUM_EVADE_DIRECTIONS = 4; /// tests actor's proximity to a closed door by default bool proximityToDoor(const MWWorld::Ptr& actor, float minSqr = MIN_DIST_TO_DOOR_SQUARED); /// Returns door pointer within range. No guarentee is given as too which one /** \return Pointer to the door, or NULL if none exists **/ MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minSqr = MIN_DIST_TO_DOOR_SQUARED); class ObstacleCheck { public: ObstacleCheck(); // Clear the timers and set the state machine to default void clear(); bool isNormalState() const; // Returns true if there is an obstacle and an evasive action // should be taken bool check(const MWWorld::Ptr& actor, float duration); // change direction to try to fix "stuck" actor void takeEvasiveAction(MWMechanics::Movement& actorMovement); private: // for checking if we're stuck (ignoring Z axis) float mPrevX; float mPrevY; // directions to try moving in when get stuck static const float evadeDirections[NUM_EVADE_DIRECTIONS][2]; enum WalkState { State_Norm, State_CheckStuck, State_Evade }; WalkState mWalkState; float mStuckDuration; // accumulate time here while in same spot float mEvadeDuration; float mDistSameSpot; // take account of actor's speed int mEvadeDirectionIndex; void chooseEvasionDirection(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/pathfinding.cpp000066400000000000000000000302651264522266000241720ustar00rootroot00000000000000#include "pathfinding.hpp" #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" #include "coordinateconverter.hpp" namespace { // Slightly cheaper version for comparisons. // Caller needs to be careful for very short distances (i.e. less than 1) // or when accumuating the results i.e. (a + b)^2 != a^2 + b^2 // float distanceSquared(ESM::Pathgrid::Point point, const osg::Vec3f& pos) { return (MWMechanics::PathFinder::MakeOsgVec3(point) - pos).length2(); } // Return the closest pathgrid point index from the specified position co // -ordinates. NOTE: Does not check if there is a sensible way to get there // (e.g. a cliff in front). // // NOTE: pos is expected to be in local co-ordinates, as is grid->mPoints // int getClosestPoint(const ESM::Pathgrid* grid, const osg::Vec3f& pos) { assert(grid && !grid->mPoints.empty()); float distanceBetween = distanceSquared(grid->mPoints[0], pos); int closestIndex = 0; // TODO: if this full scan causes performance problems mapping pathgrid // points to a quadtree may help for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++) { float potentialDistBetween = distanceSquared(grid->mPoints[counter], pos); if(potentialDistBetween < distanceBetween) { distanceBetween = potentialDistBetween; closestIndex = counter; } } return closestIndex; } // Chooses a reachable end pathgrid point. start is assumed reachable. std::pair getClosestReachablePoint(const ESM::Pathgrid* grid, const MWWorld::CellStore *cell, const osg::Vec3f pos, int start) { assert(grid && !grid->mPoints.empty()); float closestDistanceBetween = std::numeric_limits::max(); float closestDistanceReachable = std::numeric_limits::max(); int closestIndex = 0; int closestReachableIndex = 0; // TODO: if this full scan causes performance problems mapping pathgrid // points to a quadtree may help for(unsigned int counter = 0; counter < grid->mPoints.size(); counter++) { float potentialDistBetween = distanceSquared(grid->mPoints[counter], pos); if (potentialDistBetween < closestDistanceReachable) { // found a closer one if (cell->isPointConnected(start, counter)) { closestDistanceReachable = potentialDistBetween; closestReachableIndex = counter; } if (potentialDistBetween < closestDistanceBetween) { closestDistanceBetween = potentialDistBetween; closestIndex = counter; } } } // post-condition: start and endpoint must be connected assert(cell->isPointConnected(start, closestReachableIndex)); // AiWander has logic that depends on whether a path was created, deleting // allowed nodes if not. Hence a path needs to be created even if the start // and the end points are the same. return std::pair (closestReachableIndex, closestReachableIndex == closestIndex); } } namespace MWMechanics { float sqrDistanceIgnoreZ(const ESM::Pathgrid::Point& point, float x, float y) { x -= point.mX; y -= point.mY; return (x * x + y * y); } float distance(const ESM::Pathgrid::Point& point, float x, float y, float z) { x -= point.mX; y -= point.mY; z -= point.mZ; return sqrt(x * x + y * y + z * z); } float distance(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b) { float x = static_cast(a.mX - b.mX); float y = static_cast(a.mY - b.mY); float z = static_cast(a.mZ - b.mZ); return sqrt(x * x + y * y + z * z); } PathFinder::PathFinder() : mPathgrid(NULL), mCell(NULL) { } void PathFinder::clearPath() { if(!mPath.empty()) mPath.clear(); } /* * NOTE: This method may fail to find a path. The caller must check the * result before using it. If there is no path the AI routies need to * implement some other heuristics to reach the target. * * NOTE: It may be desirable to simply go directly to the endPoint if for * example there are no pathgrids in this cell. * * NOTE: startPoint & endPoint are in world co-ordinates * * Updates mPath using aStarSearch() or ray test (if shortcut allowed). * mPath consists of pathgrid points, except the last element which is * endPoint. This may be useful where the endPoint is not on a pathgrid * point (e.g. combat). However, if the caller has already chosen a * pathgrid point (e.g. wander) then it may be worth while to call * pop_back() to remove the redundant entry. * * NOTE: co-ordinates must be converted prior to calling getClosestPoint() * * | * | cell * | +-----------+ * | | | * | | | * | | @ | * | i | j | * |<--->|<---->| | * | +-----------+ * | k * |<---------->| world * +----------------------------- * * i = x value of cell itself (multiply by ESM::Land::REAL_SIZE to convert) * j = @.x in local co-ordinates (i.e. within the cell) * k = @.x in world co-ordinates */ void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts) { mPath.clear(); if(allowShortcuts) { // if there's a ray cast hit, can't take a direct path if (!MWBase::Environment::get().getWorld()->castRay( static_cast(startPoint.mX), static_cast(startPoint.mY), static_cast(startPoint.mZ), static_cast(endPoint.mX), static_cast(endPoint.mY), static_cast(endPoint.mZ))) { mPath.push_back(endPoint); return; } } if(mCell != cell || !mPathgrid) { mCell = cell; mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); } // Refer to AiWander reseach topic on openmw forums for some background. // Maybe there is no pathgrid for this cell. Just go to destination and let // physics take care of any blockages. if(!mPathgrid || mPathgrid->mPoints.empty()) { mPath.push_back(endPoint); return; } // NOTE: getClosestPoint expects local co-ordinates CoordinateConverter converter(mCell->getCell()); // NOTE: It is possible that getClosestPoint returns a pathgrind point index // that is unreachable in some situations. e.g. actor is standing // outside an area enclosed by walls, but there is a pathgrid // point right behind the wall that is closer than any pathgrid // point outside the wall osg::Vec3f startPointInLocalCoords(converter.toLocalVec3(startPoint)); int startNode = getClosestPoint(mPathgrid, startPointInLocalCoords); osg::Vec3f endPointInLocalCoords(converter.toLocalVec3(endPoint)); std::pair endNode = getClosestReachablePoint(mPathgrid, cell, endPointInLocalCoords, startNode); // if it's shorter for actor to travel from start to end, than to travel from either // start or end to nearest pathgrid point, just travel from start to end. float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2(); float endTolastNodeLength2 = distanceSquared(mPathgrid->mPoints[endNode.first], endPointInLocalCoords); float startTo1stNodeLength2 = distanceSquared(mPathgrid->mPoints[startNode], startPointInLocalCoords); if ((startToEndLength2 < startTo1stNodeLength2) || (startToEndLength2 < endTolastNodeLength2)) { mPath.push_back(endPoint); return; } // AiWander has logic that depends on whether a path was created, // deleting allowed nodes if not. Hence a path needs to be created // even if the start and the end points are the same. // NOTE: aStarSearch will return an empty path if the start and end // nodes are the same if(startNode == endNode.first) { ESM::Pathgrid::Point temp(mPathgrid->mPoints[startNode]); converter.toWorld(temp); mPath.push_back(temp); } else { mPath = mCell->aStarSearch(startNode, endNode.first); // convert supplied path to world co-ordinates for (std::list::iterator iter(mPath.begin()); iter != mPath.end(); ++iter) { converter.toWorld(*iter); } } // If endNode found is NOT the closest PathGrid point to the endPoint, // assume endPoint is not reachable from endNode. In which case, // path ends at endNode. // // So only add the destination (which may be different to the closest // pathgrid point) when endNode was the closest point to endPoint. // // This logic can fail in the opposite situate, e.g. endPoint may // have been reachable but happened to be very close to an // unreachable pathgrid point. // // The AI routines will have to deal with such situations. if(endNode.second) mPath.push_back(endPoint); } float PathFinder::getZAngleToNext(float x, float y) const { // This should never happen (programmers should have an if statement checking // isPathConstructed that prevents this call if otherwise). if(mPath.empty()) return 0.; const ESM::Pathgrid::Point &nextPoint = *mPath.begin(); float directionX = nextPoint.mX - x; float directionY = nextPoint.mY - y; return std::atan2(directionX, directionY); } bool PathFinder::checkPathCompleted(float x, float y, float tolerance) { if(mPath.empty()) return true; const ESM::Pathgrid::Point& nextPoint = *mPath.begin(); if (sqrDistanceIgnoreZ(nextPoint, x, y) < tolerance*tolerance) { mPath.pop_front(); if(mPath.empty()) { return true; } } return false; } // see header for the rationale void PathFinder::buildSyncedPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts) { if (mPath.size() < 2) { // if path has one point, then it's the destination. // don't need to worry about bad path for this case buildPath(startPoint, endPoint, cell, allowShortcuts); } else { const ESM::Pathgrid::Point oldStart(*getPath().begin()); buildPath(startPoint, endPoint, cell, allowShortcuts); if (mPath.size() >= 2) { // if 2nd waypoint of new path == 1st waypoint of old, // delete 1st waypoint of new path. std::list::iterator iter = ++mPath.begin(); if (iter->mX == oldStart.mX && iter->mY == oldStart.mY && iter->mZ == oldStart.mZ) { mPath.pop_front(); } } } } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/pathfinding.hpp000066400000000000000000000065701264522266000242010ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_PATHFINDING_H #define GAME_MWMECHANICS_PATHFINDING_H #include #include #include namespace MWWorld { class CellStore; } namespace MWMechanics { float distance(const ESM::Pathgrid::Point& point, float x, float y, float); float distance(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b); class PathFinder { public: PathFinder(); static const int PathTolerance = 32; static float sgn(float val) { if(val > 0) return 1.0; return -1.0; } static int sgn(int a) { if(a > 0) return 1; return -1; } void clearPath(); bool checkPathCompleted(float x, float y, float tolerance = PathTolerance); ///< \Returns true if we are within \a tolerance units of the last path point. /// In radians float getZAngleToNext(float x, float y) const; bool isPathConstructed() const { return !mPath.empty(); } int getPathSize() const { return mPath.size(); } const std::list& getPath() const { return mPath; } /** Synchronize new path with old one to avoid visiting 1 waypoint 2 times @note BuildPath() takes closest PathGrid point to NPC as first point of path. This is undesireable if NPC has just passed a Pathgrid point, as this makes the 2nd point of the new path == the 1st point of old path. Which results in NPC "running in a circle" back to the just passed waypoint. */ void buildSyncedPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts = true); void addPointToPath(ESM::Pathgrid::Point &point) { mPath.push_back(point); } /// utility function to convert a osg::Vec3f to a Pathgrid::Point static ESM::Pathgrid::Point MakePathgridPoint(const osg::Vec3f& v) { return ESM::Pathgrid::Point(static_cast(v[0]), static_cast(v[1]), static_cast(v[2])); } /// utility function to convert an ESM::Position to a Pathgrid::Point static ESM::Pathgrid::Point MakePathgridPoint(const ESM::Position& p) { return ESM::Pathgrid::Point(static_cast(p.pos[0]), static_cast(p.pos[1]), static_cast(p.pos[2])); } static osg::Vec3f MakeOsgVec3(const ESM::Pathgrid::Point& p) { return osg::Vec3f(static_cast(p.mX), static_cast(p.mY), static_cast(p.mZ)); } private: void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts = true); std::list mPath; const ESM::Pathgrid *mPathgrid; const MWWorld::CellStore* mCell; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/pathgrid.cpp000066400000000000000000000270761264522266000235070ustar00rootroot00000000000000#include "pathgrid.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" namespace { // See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html // // One of the smallest cost in Seyda Neen is between points 77 & 78: // pt x y // 77 = 8026, 4480 // 78 = 7986, 4218 // // Euclidean distance is about 262 (ignoring z) and Manhattan distance is 300 // (again ignoring z). Using a value of about 300 for D seems like a reasonable // starting point for experiments. If in doubt, just use value 1. // // The distance between 3 & 4 are pretty small, too. // 3 = 5435, 223 // 4 = 5948, 193 // // Approx. 514 Euclidean distance and 533 Manhattan distance. // float manhattan(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b) { return 300.0f * (abs(a.mX - b.mX) + abs(a.mY - b.mY) + abs(a.mZ - b.mZ)); } // Choose a heuristics - Note that these may not be the best for directed // graphs with non-uniform edge costs. // // distance: // - sqrt((curr.x - goal.x)^2 + (curr.y - goal.y)^2 + (curr.z - goal.z)^2) // - slower but more accurate // // Manhattan: // - |curr.x - goal.x| + |curr.y - goal.y| + |curr.z - goal.z| // - faster but not the shortest path float costAStar(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b) { //return distance(a, b); return manhattan(a, b); } } namespace MWMechanics { PathgridGraph::PathgridGraph() : mCell(NULL) , mPathgrid(NULL) , mIsExterior(0) , mGraph(0) , mIsGraphConstructed(false) , mSCCId(0) , mSCCIndex(0) { } /* * mGraph is populated with the cost of each allowed edge. * * The data structure is based on the code in buildPath2() but modified. * Please check git history if interested. * * mGraph[v].edges[i].index = w * * v = point index of location "from" * i = index of edges from point v * w = point index of location "to" * * * Example: (notice from p(0) to p(2) is not allowed in this example) * * mGraph[0].edges[0].index = 1 * .edges[1].index = 3 * * mGraph[1].edges[0].index = 0 * .edges[1].index = 2 * .edges[2].index = 3 * * mGraph[2].edges[0].index = 1 * * (etc, etc) * * * low * cost * p(0) <---> p(1) <------------> p(2) * ^ ^ * | | * | +-----> p(3) * +----------------> * high cost */ bool PathgridGraph::load(const MWWorld::CellStore *cell) { if(!cell) return false; if(mIsGraphConstructed) return true; mCell = cell->getCell(); mIsExterior = cell->getCell()->isExterior(); mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell->getCell()); if(!mPathgrid) return false; mGraph.resize(mPathgrid->mPoints.size()); for(int i = 0; i < static_cast (mPathgrid->mEdges.size()); i++) { ConnectedPoint neighbour; neighbour.cost = costAStar(mPathgrid->mPoints[mPathgrid->mEdges[i].mV0], mPathgrid->mPoints[mPathgrid->mEdges[i].mV1]); // forward path of the edge neighbour.index = mPathgrid->mEdges[i].mV1; mGraph[mPathgrid->mEdges[i].mV0].edges.push_back(neighbour); // reverse path of the edge // NOTE: These are redundant, ESM already contains the required reverse paths //neighbour.index = mPathgrid->mEdges[i].mV0; //mGraph[mPathgrid->mEdges[i].mV1].edges.push_back(neighbour); } buildConnectedPoints(); mIsGraphConstructed = true; return true; } // v is the pathgrid point index (some call them vertices) void PathgridGraph::recursiveStrongConnect(int v) { mSCCPoint[v].first = mSCCIndex; // index mSCCPoint[v].second = mSCCIndex; // lowlink mSCCIndex++; mSCCStack.push_back(v); int w; for(int i = 0; i < static_cast (mGraph[v].edges.size()); i++) { w = mGraph[v].edges[i].index; if(mSCCPoint[w].first == -1) // not visited { recursiveStrongConnect(w); // recurse mSCCPoint[v].second = std::min(mSCCPoint[v].second, mSCCPoint[w].second); } else { if(find(mSCCStack.begin(), mSCCStack.end(), w) != mSCCStack.end()) mSCCPoint[v].second = std::min(mSCCPoint[v].second, mSCCPoint[w].first); } } if(mSCCPoint[v].second == mSCCPoint[v].first) { // new component do { w = mSCCStack.back(); mSCCStack.pop_back(); mGraph[w].componentId = mSCCId; } while(w != v); mSCCId++; } return; } /* * mGraph contains the strongly connected component group id's along * with pre-calculated edge costs. * * A cell can have disjointed pathgrids, e.g. Seyda Neen has 3 * * mGraph for Seyda Neen will therefore have 3 different values. When * selecting a random pathgrid point for AiWander, mGraph can be checked * for quickly finding whether the destination is reachable. * * Otherwise, buildPath can automatically select a closest reachable end * pathgrid point (reachable from the closest start point). * * Using Tarjan's algorithm: * * mGraph | graph G | * mSCCPoint | V | derived from mPoints * mGraph[v].edges | E (for v) | * mSCCIndex | index | tracking smallest unused index * mSCCStack | S | * mGraph[v].edges[i].index | w | * */ void PathgridGraph::buildConnectedPoints() { // both of these are set to zero in the constructor //mSCCId = 0; // how many strongly connected components in this cell //mSCCIndex = 0; int pointsSize = static_cast (mPathgrid->mPoints.size()); mSCCPoint.resize(pointsSize, std::pair (-1, -1)); mSCCStack.reserve(pointsSize); for(int v = 0; v < pointsSize; v++) { if(mSCCPoint[v].first == -1) // undefined (haven't visited) recursiveStrongConnect(v); } } bool PathgridGraph::isPointConnected(const int start, const int end) const { return (mGraph[start].componentId == mGraph[end].componentId); } /* * NOTE: Based on buildPath2(), please check git history if interested * Should consider using a 3rd party library version (e.g. boost) * * Find the shortest path to the target goal using a well known algorithm. * Uses mGraph which has pre-computed costs for allowed edges. It is assumed * that mGraph is already constructed. * * Should be possible to make this MT safe. * * Returns path which may be empty. path contains pathgrid points in local * cell co-ordinates (indoors) or world co-ordinates (external). * * Input params: * start, goal - pathgrid point indexes (for this cell) * * Variables: * openset - point indexes to be traversed, lowest cost at the front * closedset - point indexes already traversed * gScore - past accumulated costs vector indexed by point index * fScore - future estimated costs vector indexed by point index * * TODO: An intersting exercise might be to cache the paths created for a * start/goal pair. To cache the results the paths need to be in * pathgrid points form (currently they are converted to world * co-ordinates). Essentially trading speed w/ memory. */ std::list PathgridGraph::aStarSearch(const int start, const int goal) const { std::list path; if(!isPointConnected(start, goal)) { return path; // there is no path, return an empty path } int graphSize = static_cast (mGraph.size()); std::vector gScore (graphSize, -1); std::vector fScore (graphSize, -1); std::vector graphParent (graphSize, -1); // gScore & fScore keep costs for each pathgrid point in mPoints gScore[start] = 0; fScore[start] = costAStar(mPathgrid->mPoints[start], mPathgrid->mPoints[goal]); std::list openset; std::list closedset; openset.push_back(start); int current = -1; while(!openset.empty()) { current = openset.front(); // front has the lowest cost openset.pop_front(); if(current == goal) break; closedset.push_back(current); // remember we've been here // check all edges for the current point index for(int j = 0; j < static_cast (mGraph[current].edges.size()); j++) { if(std::find(closedset.begin(), closedset.end(), mGraph[current].edges[j].index) == closedset.end()) { // not in closedset - i.e. have not traversed this edge destination int dest = mGraph[current].edges[j].index; float tentative_g = gScore[current] + mGraph[current].edges[j].cost; bool isInOpenSet = std::find(openset.begin(), openset.end(), dest) != openset.end(); if(!isInOpenSet || tentative_g < gScore[dest]) { graphParent[dest] = current; gScore[dest] = tentative_g; fScore[dest] = tentative_g + costAStar(mPathgrid->mPoints[dest], mPathgrid->mPoints[goal]); if(!isInOpenSet) { // add this edge to openset, lowest cost goes to the front // TODO: if this causes performance problems a hash table may help std::list::iterator it = openset.begin(); for(it = openset.begin(); it!= openset.end(); ++it) { if(fScore[*it] > fScore[dest]) break; } openset.insert(it, dest); } } } // if in closedset, i.e. traversed this edge already, try the next edge } } if(current != goal) return path; // for some reason couldn't build a path // reconstruct path to return, using local co-ordinates while(graphParent[current] != -1) { path.push_front(mPathgrid->mPoints[current]); current = graphParent[current]; } // add first node to path explicitly path.push_front(mPathgrid->mPoints[start]); return path; } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/pathgrid.hpp000066400000000000000000000045711264522266000235070ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_PATHGRID_H #define GAME_MWMECHANICS_PATHGRID_H #include #include namespace ESM { struct Cell; } namespace MWWorld { class CellStore; } namespace MWMechanics { class PathgridGraph { public: PathgridGraph(); bool load(const MWWorld::CellStore *cell); // returns true if end point is strongly connected (i.e. reachable // from start point) both start and end are pathgrid point indexes bool isPointConnected(const int start, const int end) const; // the input parameters are pathgrid point indexes // the output list is in local (internal cells) or world (external // cells) co-ordinates // // NOTE: if start equals end an empty path is returned std::list aStarSearch(const int start, const int end) const; private: const ESM::Cell *mCell; const ESM::Pathgrid *mPathgrid; bool mIsExterior; struct ConnectedPoint // edge { int index; // pathgrid point index of neighbour float cost; }; struct Node // point { int componentId; std::vector edges; // neighbours }; // componentId is an integer indicating the groups of connected // pathgrid points (all connected points will have the same value) // // In Seyda Neen there are 3: // // 52, 53 and 54 are one set (enclosed yard) // 48, 49, 50, 51, 84, 85, 86, 87, 88, 89, 90 (ship & office) // all other pathgrid points are the third set // std::vector mGraph; bool mIsGraphConstructed; // variables used to calculate connected components int mSCCId; int mSCCIndex; std::vector mSCCStack; typedef std::pair VPair; // first is index, second is lowlink std::vector mSCCPoint; // methods used to calculate connected components void recursiveStrongConnect(int v); void buildConnectedPoints(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/pickpocket.cpp000066400000000000000000000043731264522266000240340ustar00rootroot00000000000000#include "pickpocket.hpp" #include #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "npcstats.hpp" namespace MWMechanics { Pickpocket::Pickpocket(const MWWorld::Ptr &thief, const MWWorld::Ptr &victim) : mThief(thief) , mVictim(victim) { } float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add) { NpcStats& stats = ptr.getClass().getNpcStats(ptr); float agility = static_cast(stats.getAttribute(ESM::Attribute::Agility).getModified()); float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm(); } bool Pickpocket::getDetected(float valueTerm) { float x = getChanceModifier(mThief); float y = getChanceModifier(mVictim, valueTerm); float t = 2*x - y; float pcSneak = static_cast(mThief.getClass().getSkill(mThief, ESM::Skill::Sneak)); int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get() .find("iPickMinChance")->getInt(); int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get() .find("iPickMaxChance")->getInt(); int roll = Misc::Rng::roll0to99(); if (t < pcSneak / iPickMinChance) { return (roll > int(pcSneak / iPickMinChance)); } else { t = std::min(float(iPickMaxChance), t); return (roll > int(t)); } } bool Pickpocket::pick(MWWorld::Ptr item, int count) { float stackValue = static_cast(item.getClass().getValue(item) * count); float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get() .find("fPickPocketMod")->getFloat(); float valueTerm = 10 * fPickPocketMod * stackValue; return getDetected(valueTerm); } bool Pickpocket::finish() { return getDetected(0.f); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/pickpocket.hpp000066400000000000000000000012571264522266000240370ustar00rootroot00000000000000#ifndef OPENMW_MECHANICS_PICKPOCKET_H #define OPENMW_MECHANICS_PICKPOCKET_H #include "../mwworld/ptr.hpp" namespace MWMechanics { class Pickpocket { public: Pickpocket (const MWWorld::Ptr& thief, const MWWorld::Ptr& victim); /// Steal some items /// @return Was the thief detected? bool pick (MWWorld::Ptr item, int count); /// End the pickpocketing process /// @return Was the thief detected? bool finish (); private: bool getDetected(float valueTerm); float getChanceModifier(const MWWorld::Ptr& ptr, float add=0); MWWorld::Ptr mThief; MWWorld::Ptr mVictim; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/repair.cpp000066400000000000000000000074351264522266000231640ustar00rootroot00000000000000#include "repair.hpp" #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" #include "actorutil.hpp" namespace MWMechanics { void Repair::repair(const MWWorld::Ptr &itemToRepair) { MWWorld::Ptr player = getPlayer(); MWWorld::LiveCellRef *ref = mTool.get(); // unstack tool if required player.getClass().getContainerStore(player).unstack(mTool, player); // reduce number of uses left int uses = mTool.getClass().getItemHealth(mTool); mTool.getCellRef().setCharge(uses-1); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); float fatigueTerm = stats.getFatigueTerm(); int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); int armorerSkill = npcStats.getSkill(ESM::Skill::Armorer).getModified(); float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fRepairAmountMult")->getFloat(); float toolQuality = ref->mBase->mData.mQuality; float x = (0.1f * pcStrength + 0.1f * pcLuck + armorerSkill) * fatigueTerm; int roll = Misc::Rng::roll0to99(); if (roll <= x) { int y = static_cast(fRepairAmountMult * toolQuality * roll); y = std::max(1, y); // repair by 'y' points int charge = itemToRepair.getClass().getItemHealth(itemToRepair); charge = std::min(charge + y, itemToRepair.getClass().getItemMaxHealth(itemToRepair)); itemToRepair.getCellRef().setCharge(charge); // attempt to re-stack item, in case it was fully repaired MWWorld::ContainerStoreIterator stacked = player.getClass().getContainerStore(player).restack(itemToRepair); // set the OnPCRepair variable on the item's script std::string script = stacked->getClass().getScript(itemToRepair); if(script != "") stacked->getRefData().getLocals().setVarByInt(script, "onpcrepair", 1); // increase skill player.getClass().skillUsageSucceeded(player, ESM::Skill::Armorer, 0); MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1); MWBase::Environment::get().getWindowManager()->messageBox("#{sRepairSuccess}"); } else { MWBase::Environment::get().getSoundManager()->playSound("Repair Fail",1,1); MWBase::Environment::get().getWindowManager()->messageBox("#{sRepairFailed}"); } // tool used up? if (mTool.getCellRef().getCharge() == 0) { MWWorld::Ptr player = getPlayer(); MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); store.remove(mTool, 1, player); std::string message = MWBase::Environment::get().getWorld()->getStore().get() .find("sNotifyMessage51")->getString(); MWBase::Environment::get().getWindowManager()->messageBox((boost::format(message) % mTool.getClass().getName(mTool)).str()); // try to find a new tool of the same ID for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), mTool.getCellRef().getRefId())) { mTool = *iter; break; } } } } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/repair.hpp000066400000000000000000000006141264522266000231610ustar00rootroot00000000000000#ifndef OPENMW_MWMECHANICS_REPAIR_H #define OPENMW_MWMECHANICS_REPAIR_H #include "../mwworld/ptr.hpp" namespace MWMechanics { class Repair { public: void setTool (const MWWorld::Ptr& tool) { mTool = tool; } MWWorld::Ptr getTool() { return mTool; } void repair (const MWWorld::Ptr& itemToRepair); private: MWWorld::Ptr mTool; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/security.cpp000066400000000000000000000101521264522266000235370ustar00rootroot00000000000000#include "security.hpp" #include #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" namespace MWMechanics { Security::Security(const MWWorld::Ptr &actor) : mActor(actor) { CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); NpcStats& npcStats = actor.getClass().getNpcStats(actor); mAgility = static_cast(creatureStats.getAttribute(ESM::Attribute::Agility).getModified()); mLuck = static_cast(creatureStats.getAttribute(ESM::Attribute::Luck).getModified()); mSecuritySkill = static_cast(npcStats.getSkill(ESM::Skill::Security).getModified()); mFatigueTerm = creatureStats.getFatigueTerm(); } void Security::pickLock(const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick, std::string& resultMessage, std::string& resultSound) { if (!(lock.getCellRef().getLockLevel() > 0) || !lock.getClass().canLock(lock)) //If it's unlocked back out immediately return; int lockStrength = lock.getCellRef().getLockLevel(); float pickQuality = lockpick.get()->mBase->mData.mQuality; float fPickLockMult = MWBase::Environment::get().getWorld()->getStore().get().find("fPickLockMult")->getFloat(); float x = 0.2f * mAgility + 0.1f * mLuck + mSecuritySkill; x *= pickQuality * mFatigueTerm; x += fPickLockMult * lockStrength; resultSound = "Open Lock Fail"; if (x <= 0) resultMessage = "#{sLockImpossible}"; else { MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, lock); if (Misc::Rng::roll0to99() <= x) { lock.getClass().unlock(lock); resultMessage = "#{sLockSuccess}"; resultSound = "Open Lock"; mActor.getClass().skillUsageSucceeded(mActor, ESM::Skill::Security, 1); } else resultMessage = "#{sLockFail}"; } int uses = lockpick.getClass().getItemHealth(lockpick); --uses; lockpick.getCellRef().setCharge(uses); if (!uses) lockpick.getContainerStore()->remove(lockpick, 1, mActor); } void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe, std::string& resultMessage, std::string& resultSound) { if (trap.getCellRef().getTrap() == "") return; float probeQuality = probe.get()->mBase->mData.mQuality; const ESM::Spell* trapSpell = MWBase::Environment::get().getWorld()->getStore().get().find(trap.getCellRef().getTrap()); int trapSpellPoints = trapSpell->mData.mCost; float fTrapCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fTrapCostMult")->getFloat(); float x = 0.2f * mAgility + 0.1f * mLuck + mSecuritySkill; x += fTrapCostMult * trapSpellPoints; x *= probeQuality * mFatigueTerm; resultSound = "Disarm Trap Fail"; if (x <= 0) resultMessage = "#{sTrapImpossible}"; else { MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, trap); if (Misc::Rng::roll0to99() <= x) { trap.getCellRef().setTrap(""); resultSound = "Disarm Trap"; resultMessage = "#{sTrapSuccess}"; mActor.getClass().skillUsageSucceeded(mActor, ESM::Skill::Security, 0); } else resultMessage = "#{sTrapFail}"; } int uses = probe.getClass().getItemHealth(probe); --uses; probe.getCellRef().setCharge(uses); if (!uses) probe.getContainerStore()->remove(probe, 1, mActor); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/security.hpp000066400000000000000000000012741264522266000235510ustar00rootroot00000000000000#ifndef MWMECHANICS_SECURITY_H #define MWMECHANICS_SECURITY_H #include "../mwworld/ptr.hpp" namespace MWMechanics { /// @brief implementation of Security skill class Security { public: Security (const MWWorld::Ptr& actor); void pickLock (const MWWorld::Ptr& lock, const MWWorld::Ptr& lockpick, std::string& resultMessage, std::string& resultSound); void probeTrap (const MWWorld::Ptr& trap, const MWWorld::Ptr& probe, std::string& resultMessage, std::string& resultSound); private: float mAgility, mLuck, mSecuritySkill, mFatigueTerm; MWWorld::Ptr mActor; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/spellcasting.cpp000066400000000000000000001375651264522266000244020ustar00rootroot00000000000000#include "spellcasting.hpp" #include #include #include #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/actionteleport.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwrender/animation.hpp" #include "magiceffects.hpp" #include "npcstats.hpp" #include "summoning.hpp" #include "actorutil.hpp" namespace { /// Get projectile properties (model, sound and speed) for a spell with the given effects /// If \a model is empty, the spell has no ranged effects and should not spawn a projectile. void getProjectileInfo (const ESM::EffectList& effects, std::string& model, std::string& sound, float& speed) { for (std::vector::const_iterator iter (effects.mList.begin()); iter!=effects.mList.end(); ++iter) { if (iter->mRange != ESM::RT_Target) continue; const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( iter->mEffectID); model = magicEffect->mBolt; if (model.empty()) model = "VFX_DefaultBolt"; static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; if (!magicEffect->mBoltSound.empty()) sound = magicEffect->mBoltSound; else sound = schools[magicEffect->mData.mSchool] + " bolt"; speed = magicEffect->mData.mSpeed; break; } } void applyDynamicStatsEffect(int attribute, const MWWorld::Ptr& target, float magnitude) { MWMechanics::DynamicStat value = target.getClass().getCreatureStats(target).getDynamic(attribute); value.setCurrent(value.getCurrent()+magnitude, attribute == 2); target.getClass().getCreatureStats(target).setDynamic(attribute, value); } } namespace MWMechanics { ESM::Skill::SkillEnum spellSchoolToSkill(int school) { std::map schoolSkillMap; // maps spell school to skill id schoolSkillMap[0] = ESM::Skill::Alteration; schoolSkillMap[1] = ESM::Skill::Conjuration; schoolSkillMap[3] = ESM::Skill::Illusion; schoolSkillMap[2] = ESM::Skill::Destruction; schoolSkillMap[4] = ESM::Skill::Mysticism; schoolSkillMap[5] = ESM::Skill::Restoration; assert(schoolSkillMap.find(school) != schoolSkillMap.end()); return schoolSkillMap[school]; } float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap) { CreatureStats& stats = actor.getClass().getCreatureStats(actor); if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude()) return 0; float y = std::numeric_limits::max(); float lowestSkill = 0; for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it) { float x = static_cast(it->mDuration); const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( it->mEffectID); if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) x = std::max(1.f, x); x *= 0.1f * magicEffect->mData.mBaseCost; x *= 0.5f * (it->mMagnMin + it->mMagnMax); x *= it->mArea * 0.05f * magicEffect->mData.mBaseCost; if (it->mRange == ESM::RT_Target) x *= 1.5f; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( "fEffectCostMult")->getFloat(); x *= fEffectCostMult; float s = 2.0f * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); if (s - x < y) { y = s - x; if (effectiveSchool) *effectiveSchool = magicEffect->mData.mSchool; lowestSkill = s; } } if (spell->mData.mType == ESM::Spell::ST_Power) return stats.getSpells().canUsePower(spell) ? 100 : 0; if (spell->mData.mType != ESM::Spell::ST_Spell) return 100; if (spell->mData.mFlags & ESM::Spell::F_Always) return 100; float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude(); int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2f * actorWillpower + 0.1f * actorLuck) * stats.getFatigueTerm(); if (MWBase::Environment::get().getWorld()->getGodModeState() && actor == getPlayer()) castChance = 100; if (!cap) return std::max(0.f, castChance); else return std::max(0.f, std::min(100.f, castChance)); } float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellId); return getSpellSuccessChance(spell, actor, effectiveSchool, cap); } int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor) { int school = 0; getSpellSuccessChance(spellId, actor, &school); return school; } int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor) { int school = 0; getSpellSuccessChance(spell, actor, &school); return school; } bool spellIncreasesSkill(const ESM::Spell *spell) { if (spell->mData.mType == ESM::Spell::ST_Spell && !(spell->mData.mFlags & ESM::Spell::F_Always)) return true; return false; } bool spellIncreasesSkill(const std::string &spellId) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellId); return spellIncreasesSkill(spell); } float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects) { short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); float resistance = 0; if (resistanceEffect != -1) resistance += actorEffects->get(resistanceEffect).getMagnitude(); if (weaknessEffect != -1) resistance -= actorEffects->get(weaknessEffect).getMagnitude(); if (effectId == ESM::MagicEffect::FireDamage) resistance += actorEffects->get(ESM::MagicEffect::FireShield).getMagnitude(); if (effectId == ESM::MagicEffect::ShockDamage) resistance += actorEffects->get(ESM::MagicEffect::LightningShield).getMagnitude(); if (effectId == ESM::MagicEffect::FrostDamage) resistance += actorEffects->get(ESM::MagicEffect::FrostShield).getMagnitude(); return resistance; } float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell, const MagicEffects* effects) { const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( effectId); const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects(); if (effects) magicEffects = effects; float resisted = 0; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) { // Effects with no resistance attribute belonging to them can not be resisted if (ESM::MagicEffect::getResistanceEffect(effectId) == -1) return 0.f; float resistance = getEffectResistanceAttribute(effectId, magicEffects); int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); // This makes spells that are easy to cast harder to resist and vice versa float castChance = 100.f; if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor()) { castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance } if (castChance > 0) x *= 50 / castChance; float roll = Misc::Rng::rollClosedProbability() * 100; if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) roll -= resistance; if (x <= roll) x = 0; else { if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) x = 100; else x = roll / std::min(x, 100.f); } x = std::min(x + resistance, 100.f); resisted = x; } return resisted; } float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell, const MagicEffects* effects) { float resistance = getEffectResistance(effectId, actor, caster, spell, effects); return 1 - resistance / 100.f; } /// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure. bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, bool castByPlayer) { switch (effectId) { case ESM::MagicEffect::Levitate: if (!MWBase::Environment::get().getWorld()->isLevitationEnabled()) { if (castByPlayer) MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); return false; } break; case ESM::MagicEffect::Soultrap: if (!target.getClass().isNpc() // no messagebox for NPCs && (target.getTypeName() == typeid(ESM::Creature).name() && target.get()->mBase->mData.mSoul == 0)) { if (castByPlayer) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInvalidTarget}"); return true; // must still apply to get visual effect and have target regard it as attack } break; case ESM::MagicEffect::AlmsiviIntervention: case ESM::MagicEffect::DivineIntervention: case ESM::MagicEffect::Mark: case ESM::MagicEffect::Recall: if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled()) { if (castByPlayer) MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); return false; } break; } return true; } CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target) : mCaster(caster) , mTarget(target) , mStack(false) , mHitPosition(0,0,0) , mAlwaysSucceed(false) { } void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded) { if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead()) return; // If none of the effects need to apply, we can early-out bool found = false; for (std::vector::const_iterator iter (effects.mList.begin()); iter!=effects.mList.end(); ++iter) { if (iter->mRange == range) { found = true; break; } } if (!found) return; const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search (mId); if (spell && (spell->mData.mType == ESM::Spell::ST_Disease || spell->mData.mType == ESM::Spell::ST_Blight)) { int requiredResistance = (spell->mData.mType == ESM::Spell::ST_Disease) ? ESM::MagicEffect::ResistCommonDisease : ESM::MagicEffect::ResistBlightDisease; float x = target.getClass().getCreatureStats(target).getMagicEffects().get(requiredResistance).getMagnitude(); if (Misc::Rng::roll0to99() <= x) { // Fully resisted, show message if (target == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}"); return; } } ESM::EffectList reflectedEffects; std::vector appliedLastingEffects; bool firstAppliedEffect = true; bool anyHarmfulEffect = false; // HACK: cache target's magic effects here, and add any applied effects to it. Use the cached effects for determining resistance. // This is required for Weakness effects in a spell to apply to any subsequent effects in the spell. // Otherwise, they'd only apply after the whole spell was added. MagicEffects targetEffects; if (target.getClass().isActor()) targetEffects += target.getClass().getCreatureStats(target).getMagicEffects(); bool castByPlayer = (!caster.isEmpty() && caster == getPlayer()); for (std::vector::const_iterator effectIt (effects.mList.begin()); effectIt!=effects.mList.end(); ++effectIt) { if (effectIt->mRange != range) continue; const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( effectIt->mEffectID); if (!checkEffectTarget(effectIt->mEffectID, target, castByPlayer)) continue; // If player is healing someone, show the target's HP bar if (castByPlayer && target != caster && effectIt->mEffectID == ESM::MagicEffect::RestoreHealth && target.getClass().isActor()) MWBase::Environment::get().getWindowManager()->setEnemy(target); // Try absorbing if it's a spell // NOTE: Vanilla does this once per spell absorption effect source instead of adding the % from all sources together, not sure // if that is worth replicating. bool absorbed = false; if (spell && caster != target && target.getClass().isActor()) { float absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude(); absorbed = (Misc::Rng::roll0to99() < absorb); if (absorbed) { const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, ""); // Magicka is increased by cost of spell DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); target.getClass().getCreatureStats(target).setMagicka(magicka); } } float magnitudeMult = 1; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor()) { anyHarmfulEffect = true; if (absorbed) // Absorbed, and we know there was a harmful effect (figuring that out is the only reason we are in this loop) break; // If player is attempting to cast a harmful spell, show the target's HP bar if (castByPlayer && target != caster) MWBase::Environment::get().getWindowManager()->setEnemy(target); // Try reflecting if (!reflected && magnitudeMult > 0 && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) { float reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).getMagnitude(); bool isReflected = (Misc::Rng::roll0to99() < reflect); if (isReflected) { const ESM::Static* reflectStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Reflect"); MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( "meshes\\" + reflectStatic->mModel, ESM::MagicEffect::Reflect, false, ""); reflectedEffects.mList.push_back(*effectIt); magnitudeMult = 0; } } // Try resisting if (magnitudeMult > 0 && target.getClass().isActor()) { magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects); if (magnitudeMult == 0) { // Fully resisted, show message if (target == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}"); else if (castByPlayer) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}"); } } } if (magnitudeMult > 0 && !absorbed) { float random = Misc::Rng::rollClosedProbability(); float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; magnitude *= magnitudeMult; if (!target.getClass().isActor()) { // non-actor objects have no list of active magic effects, so have to apply instantly if (!applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude)) continue; } else // target.getClass().isActor() == true { bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); if (hasDuration && effectIt->mDuration == 0) { // duration 0 means apply full magnitude instantly bool wasDead = target.getClass().getCreatureStats(target).isDead(); effectTick(target.getClass().getCreatureStats(target), target, EffectKey(*effectIt), magnitude); bool isDead = target.getClass().getCreatureStats(target).isDead(); if (!wasDead && isDead) MWBase::Environment::get().getMechanicsManager()->actorKilled(target, caster); } else { // add to list of active effects, to apply in next frame ActiveSpells::ActiveEffect effect; effect.mEffectId = effectIt->mEffectID; effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; effect.mDuration = static_cast(effectIt->mDuration); effect.mMagnitude = magnitude; targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); appliedLastingEffects.push_back(effect); // For absorb effects, also apply the effect to the caster - but with a negative // magnitude, since we're transfering stats from the target to the caster if (!caster.isEmpty() && caster.getClass().isActor()) { for (int i=0; i<5; ++i) { if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) { std::vector effects; ActiveSpells::ActiveEffect effect_ = effect; effect_.mMagnitude *= -1; effects.push_back(effect_); // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, effects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); } } } } } // Re-casting a summon effect will remove the creature from previous castings of that effect. if (isSummoningEffect(effectIt->mEffectID) && !target.isEmpty() && target.getClass().isActor()) { CreatureStats& targetStats = target.getClass().getCreatureStats(target); std::map::iterator found = targetStats.getSummonedCreatureMap().find(std::make_pair(effectIt->mEffectID, mId)); if (found != targetStats.getSummonedCreatureMap().end()) { cleanupSummonedCreature(targetStats, found->second); targetStats.getSummonedCreatureMap().erase(found); } } if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) { // Play sound, only for the first effect if (firstAppliedEffect) { static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(!magicEffect->mHitSound.empty()) sndMgr->playSound3D(target, magicEffect->mHitSound, 1.0f, 1.0f); else sndMgr->playSound3D(target, schools[magicEffect->mData.mSchool]+" hit", 1.0f, 1.0f); firstAppliedEffect = false; } // Add VFX const ESM::Static* castStatic; if (!magicEffect->mHit.empty()) castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); else castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_DefaultHit"); // TODO: VFX are no longer active after saving/reloading the game bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0; // Note: in case of non actor, a free effect should be fine as well MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); if (anim) anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); } } } if (!exploded) MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, range, mId, mSourceName); if (!reflectedEffects.mList.empty()) inflict(caster, target, reflectedEffects, range, true, exploded); if (!appliedLastingEffects.empty()) { int casterActorId = -1; if (!caster.isEmpty() && caster.getClass().isActor()) casterActorId = caster.getClass().getCreatureStats(caster).getActorId(); target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName, casterActorId); } // Notify the target actor they've been hit if (anyHarmfulEffect && target.getClass().isActor() && target != caster && !caster.isEmpty() && caster.getClass().isActor()) target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true); } bool CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) { short effectId = effect.mId; if (target.getClass().canLock(target)) { if (effectId == ESM::MagicEffect::Lock) { if (target.getCellRef().getLockLevel() < magnitude) //If the door is not already locked to a higher value, lock it to spell magnitude { if (caster == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}"); target.getClass().lock(target, static_cast(magnitude)); } return true; } else if (effectId == ESM::MagicEffect::Open) { if (target.getCellRef().getLockLevel() <= magnitude) { if (target.getCellRef().getLockLevel() > 0) { MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); if (!caster.isEmpty() && caster.getClass().isActor()) MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); if (caster == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}"); } target.getClass().unlock(target); } else MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); return true; } } else if (target.getClass().isActor()) { switch (effectId) { case ESM::MagicEffect::CurePoison: target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); return true; case ESM::MagicEffect::CureParalyzation: target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); return true; case ESM::MagicEffect::CureCommonDisease: target.getClass().getCreatureStats(target).getSpells().purgeCommonDisease(); return true; case ESM::MagicEffect::CureBlightDisease: target.getClass().getCreatureStats(target).getSpells().purgeBlightDisease(); return true; case ESM::MagicEffect::CureCorprusDisease: target.getClass().getCreatureStats(target).getSpells().purgeCorprusDisease(); return true; case ESM::MagicEffect::Dispel: target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude); return true; case ESM::MagicEffect::RemoveCurse: target.getClass().getCreatureStats(target).getSpells().purgeCurses(); return true; } if (target != getPlayer()) return false; if (effectId == ESM::MagicEffect::DivineIntervention) { MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker"); return true; } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker"); return true; } else if (effectId == ESM::MagicEffect::Mark) { MWBase::Environment::get().getWorld()->getPlayer().markPosition( target.getCell(), target.getRefData().getPosition()); return true; } else if (effectId == ESM::MagicEffect::Recall) { MWWorld::CellStore* markedCell = NULL; ESM::Position markedPosition; MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); if (markedCell) { MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->getCell()->mName, markedPosition, false); action.execute(target); } return true; } } return false; } bool CastSpell::cast(const std::string &id) { if (const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().search (id)) return cast(spell); if (const ESM::Potion *potion = MWBase::Environment::get().getWorld()->getStore().get().search (id)) return cast(potion); if (const ESM::Ingredient *ingredient = MWBase::Environment::get().getWorld()->getStore().get().search (id)) return cast(ingredient); throw std::runtime_error("ID type cannot be casted"); } bool CastSpell::cast(const MWWorld::Ptr &item) { std::string enchantmentName = item.getClass().getEnchantment(item); if (enchantmentName.empty()) throw std::runtime_error("can't cast an item without an enchantment"); mSourceName = item.getClass().getName(item); mId = item.getCellRef().getRefId(); const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(enchantmentName); mStack = (enchantment->mData.mType == ESM::Enchantment::CastOnce); // Check if there's enough charge left if (enchantment->mData.mType == ESM::Enchantment::WhenUsed || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { const int castCost = getEffectiveEnchantmentCastCost(static_cast(enchantment->mData.mCost), mCaster); if (item.getCellRef().getEnchantmentCharge() == -1) item.getCellRef().setEnchantmentCharge(static_cast(enchantment->mData.mCharge)); if (item.getCellRef().getEnchantmentCharge() < castCost) { if (mCaster == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); // Failure sound int school = 0; if (!enchantment->mEffects.mList.empty()) { short effectId = enchantment->mEffects.mList.front().mEffectID; const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectId); school = magicEffect->mData.mSchool; } static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f); return false; } // Reduce charge item.getCellRef().setEnchantmentCharge(item.getCellRef().getEnchantmentCharge() - castCost); } if (enchantment->mData.mType == ESM::Enchantment::WhenUsed) { if (mCaster == getPlayer()) mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1); } if (enchantment->mData.mType == ESM::Enchantment::CastOnce) item.getContainerStore()->remove(item, 1, mCaster); else if (enchantment->mData.mType != ESM::Enchantment::WhenStrikes) { if (mCaster == getPlayer()) { mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3); } } inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self); if (!mTarget.isEmpty()) { inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch); } std::string projectileModel; std::string sound; float speed = 0; getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed); if (!projectileModel.empty()) MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, false, enchantment->mEffects, mCaster, mSourceName, // Not needed, enchantments can only be cast by actors osg::Vec3f(1,0,0)); return true; } bool CastSpell::cast(const ESM::Potion* potion) { mSourceName = potion->mName; mId = potion->mId; mStack = true; inflict(mCaster, mCaster, potion->mEffects, ESM::RT_Self); return true; } bool CastSpell::cast(const ESM::Spell* spell) { mSourceName = spell->mName; mId = spell->mId; mStack = false; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); int school = 0; if (mCaster.getClass().isActor() && !mAlwaysSucceed) { school = getSpellSchool(spell, mCaster); CreatureStats& stats = mCaster.getClass().getCreatureStats(mCaster); // Reduce fatigue (note that in the vanilla game, both GMSTs are 0, and there's no fatigue loss) static const float fFatigueSpellBase = store.get().find("fFatigueSpellBase")->getFloat(); static const float fFatigueSpellMult = store.get().find("fFatigueSpellMult")->getFloat(); DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster); float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult); fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue); bool fail = false; // Check success float successChance = getSpellSuccessChance(spell, mCaster); if (Misc::Rng::roll0to99() >= successChance) { if (mCaster == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}"); fail = true; } if (fail) { // Failure sound static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f); return false; } // A power can be used once per 24h if (spell->mData.mType == ESM::Spell::ST_Power) stats.getSpells().usePower(spell); } if (mCaster == getPlayer() && spellIncreasesSkill(spell)) mCaster.getClass().skillUsageSucceeded(mCaster, spellSchoolToSkill(school), 0); inflict(mCaster, mCaster, spell->mEffects, ESM::RT_Self); if (!mTarget.isEmpty()) { inflict(mTarget, mCaster, spell->mEffects, ESM::RT_Touch); } std::string projectileModel; std::string sound; float speed = 0; getProjectileInfo(spell->mEffects, projectileModel, sound, speed); if (!projectileModel.empty()) { osg::Vec3f fallbackDirection (0,1,0); // Fall back to a "caster to target" direction if we have no other means of determining it // (e.g. when cast by a non-actor) if (!mTarget.isEmpty()) fallbackDirection = osg::Vec3f(mTarget.getRefData().getPosition().asVec3())- osg::Vec3f(mCaster.getRefData().getPosition().asVec3()); MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, false, spell->mEffects, mCaster, mSourceName, fallbackDirection); } return true; } bool CastSpell::cast (const ESM::Ingredient* ingredient) { mId = ingredient->mId; mStack = true; mSourceName = ingredient->mName; ESM::ENAMstruct effect; effect.mEffectID = ingredient->mData.mEffectID[0]; effect.mSkill = ingredient->mData.mSkills[0]; effect.mAttribute = ingredient->mData.mAttributes[0]; effect.mRange = ESM::RT_Self; effect.mArea = 0; const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( effect.mEffectID); const MWMechanics::NpcStats& npcStats = mCaster.getClass().getNpcStats(mCaster); const MWMechanics::CreatureStats& creatureStats = mCaster.getClass().getCreatureStats(mCaster); float x = (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + 0.2f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified() + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()) * creatureStats.getFatigueTerm(); int roll = Misc::Rng::roll0to99(); if (roll > x) { // "X has no effect on you" std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage50")->getString(); message = boost::str(boost::format(message) % ingredient->mName); MWBase::Environment::get().getWindowManager()->messageBox(message); return false; } float magnitude = 0; float y = roll / std::min(x, 100.f); y *= 0.25f * x; if (magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) effect.mDuration = static_cast(y); else effect.mDuration = 1; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) { if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) magnitude = floor((0.05f * y) / (0.1f * magicEffect->mData.mBaseCost)); else magnitude = floor(y / (0.1f * magicEffect->mData.mBaseCost)); magnitude = std::max(1.f, magnitude); } else magnitude = 1; effect.mMagnMax = static_cast(magnitude); effect.mMagnMin = static_cast(magnitude); ESM::EffectList effects; effects.mList.push_back(effect); inflict(mCaster, mCaster, effects, ESM::RT_Self); return true; } int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr &actor) { /* * Each point of enchant skill above/under 10 subtracts/adds * one percent of enchantment cost while minimum is 1. */ int eSkill = actor.getClass().getSkill(actor, ESM::Skill::Enchant); const float result = castCost - (castCost / 100) * (eSkill - 10); return static_cast((result < 1) ? 1 : result); } bool isSummoningEffect(int effectId) { return ((effectId >= ESM::MagicEffect::SummonScamp && effectId <= ESM::MagicEffect::SummonStormAtronach) || effectId == ESM::MagicEffect::SummonCenturionSphere || (effectId >= ESM::MagicEffect::SummonFabricant && effectId <= ESM::MagicEffect::SummonCreature05)); } bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) { if (ptr.getClass().hasInventoryStore(ptr)) { MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); MWWorld::ContainerStoreIterator item = inv.getSlot(slot); if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor || item.getType() == MWWorld::ContainerStore::Type_Weapon)) { if (!item->getClass().hasItemHealth(*item)) return false; int charge = item->getClass().getItemHealth(*item); if (charge == 0) return false; // FIXME: charge should be a float, not int so that damage < 1 per frame can be applied. // This was also a bug in the original engine. charge -= std::min(static_cast(disintegrate), charge); item->getCellRef().setCharge(charge); if (charge == 0) { // Will unequip the broken item and try to find a replacement if (ptr != getPlayer()) inv.autoEquip(ptr); else inv.unequipItem(*item, ptr); } return true; } } return false; } void adjustDynamicStat(CreatureStats& creatureStats, int index, float magnitude) { DynamicStat stat = creatureStats.getDynamic(index); stat.setCurrent(stat.getCurrent() + magnitude, index == 2); creatureStats.setDynamic(index, stat); } void effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey &effectKey, float magnitude) { if (magnitude == 0.f) return; bool receivedMagicDamage = false; switch (effectKey.mId) { case ESM::MagicEffect::DamageAttribute: { AttributeValue attr = creatureStats.getAttribute(effectKey.mArg); attr.damage(magnitude); creatureStats.setAttribute(effectKey.mArg, attr); break; } case ESM::MagicEffect::RestoreAttribute: { AttributeValue attr = creatureStats.getAttribute(effectKey.mArg); attr.restore(magnitude); creatureStats.setAttribute(effectKey.mArg, attr); break; } case ESM::MagicEffect::RestoreHealth: case ESM::MagicEffect::RestoreMagicka: case ESM::MagicEffect::RestoreFatigue: adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::RestoreHealth, magnitude); break; case ESM::MagicEffect::DamageHealth: case ESM::MagicEffect::DamageMagicka: case ESM::MagicEffect::DamageFatigue: receivedMagicDamage = true; adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); break; case ESM::MagicEffect::AbsorbHealth: case ESM::MagicEffect::AbsorbMagicka: case ESM::MagicEffect::AbsorbFatigue: if (magnitude > 0.f) receivedMagicDamage = true; adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); break; case ESM::MagicEffect::DisintegrateArmor: { // According to UESP int priorities[] = { MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet, MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Boots }; for (unsigned int i=0; iisExterior()) break; float time = MWBase::Environment::get().getWorld()->getTimeStamp().getHour(); float timeDiff = std::min(7.f, std::max(0.f, std::abs(time - 13))); float damageScale = 1.f - timeDiff / 7.f; // When cloudy, the sun damage effect is halved static float fMagicSunBlockedMult = MWBase::Environment::get().getWorld()->getStore().get().find( "fMagicSunBlockedMult")->getFloat(); int weather = MWBase::Environment::get().getWorld()->getCurrentWeather(); if (weather > 1) damageScale *= fMagicSunBlockedMult; adjustDynamicStat(creatureStats, 0, -magnitude * damageScale); if (magnitude * damageScale > 0.f) receivedMagicDamage = true; break; } case ESM::MagicEffect::FireDamage: case ESM::MagicEffect::ShockDamage: case ESM::MagicEffect::FrostDamage: case ESM::MagicEffect::Poison: { adjustDynamicStat(creatureStats, 0, -magnitude); receivedMagicDamage = true; break; } case ESM::MagicEffect::DamageSkill: case ESM::MagicEffect::RestoreSkill: { if (!actor.getClass().isNpc()) break; NpcStats &npcStats = actor.getClass().getNpcStats(actor); SkillValue& skill = npcStats.getSkill(effectKey.mArg); if (effectKey.mId == ESM::MagicEffect::RestoreSkill) skill.restore(magnitude); else skill.damage(magnitude); break; } } if (receivedMagicDamage && actor == getPlayer()) MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/spellcasting.hpp000066400000000000000000000113111264522266000243630ustar00rootroot00000000000000#ifndef MWMECHANICS_SPELLSUCCESS_H #define MWMECHANICS_SPELLSUCCESS_H #include "../mwworld/ptr.hpp" #include namespace ESM { struct Spell; struct Ingredient; struct Potion; struct EffectList; } namespace MWMechanics { struct EffectKey; class MagicEffects; class CreatureStats; ESM::Skill::SkillEnum spellSchoolToSkill(int school); bool isSummoningEffect(int effectId); /** * @param spell spell to cast * @param actor calculate spell success chance for this actor (depends on actor's skills) * @param effectiveSchool the spell's effective school (relevant for skill progress) will be written here * @param cap cap the result to 100%? * @note actor can be an NPC or a creature * @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned. */ float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true); float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true); int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); /// Get whether or not the given spell contributes to skill progress. bool spellIncreasesSkill(const ESM::Spell* spell); bool spellIncreasesSkill(const std::string& spellId); /// Get the resistance attribute against an effect for a given actor. This will add together /// ResistX and Weakness to X effects relevant against the given effect. float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects); /// Get the effective resistance against an effect casted by the given actor in the given spell (optional). /// @return >=100 for fully resisted. can also return negative value for damage amplification. /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); /// Get an effect multiplier for applying an effect cast by the given actor in the given spell (optional). /// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness) /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); void effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude); class CastSpell { private: MWWorld::Ptr mCaster; // May be empty MWWorld::Ptr mTarget; // May be empty public: bool mStack; std::string mId; // ID of spell, potion, item etc std::string mSourceName; // Display name for spell, potion, etc osg::Vec3f mHitPosition; // Used for spawning area orb bool mAlwaysSucceed; // Always succeed spells casted by NPCs/creatures regardless of their chance (default: false) public: CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target); bool cast (const ESM::Spell* spell); /// @note mCaster must be an actor bool cast (const MWWorld::Ptr& item); /// @note mCaster must be an NPC bool cast (const ESM::Ingredient* ingredient); bool cast (const ESM::Potion* potion); /// @note Auto detects if spell, ingredient or potion bool cast (const std::string& id); /// @note \a target can be any type of object, not just actors. /// @note \a caster can be any type of object, or even an empty object. void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false); /// @note \a caster can be any type of object, or even an empty object. /// @return was the target suitable for the effect? bool applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/spells.cpp000066400000000000000000000353361264522266000232050ustar00rootroot00000000000000#include "spells.hpp" #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" #include "magiceffects.hpp" namespace MWMechanics { Spells::TIterator Spells::begin() const { return mSpells.begin(); } Spells::TIterator Spells::end() const { return mSpells.end(); } const ESM::Spell* Spells::getSpell(const std::string& id) const { return MWBase::Environment::get().getWorld()->getStore().get().find(id); } bool Spells::hasSpell(const std::string &spell) const { return hasSpell(getSpell(spell)); } bool Spells::hasSpell(const ESM::Spell *spell) const { return mSpells.find(spell) != mSpells.end(); } void Spells::add (const ESM::Spell* spell) { if (mSpells.find (spell)==mSpells.end()) { std::map random; // Determine the random magnitudes (unless this is a castable spell, in which case // they will be determined when the spell is cast) if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell) { for (unsigned int i=0; imEffects.mList.size();++i) { if (spell->mEffects.mList[i].mMagnMin != spell->mEffects.mList[i].mMagnMax) random[i] = Misc::Rng::rollClosedProbability(); } } if (hasCorprusEffect(spell)) { CorprusStats corprus; corprus.mWorsenings = 0; corprus.mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; mCorprusSpells[spell] = corprus; } mSpells.insert (std::make_pair (spell, random)); } } void Spells::add (const std::string& spellId) { add(getSpell(spellId)); } void Spells::remove (const std::string& spellId) { const ESM::Spell* spell = getSpell(spellId); TContainer::iterator iter = mSpells.find (spell); std::map::iterator corprusIt = mCorprusSpells.find(spell); // if it's corprus, remove negative and keep positive effects if (corprusIt != mCorprusSpells.end()) { worsenCorprus(spell); if (mPermanentSpellEffects.find(spell) != mPermanentSpellEffects.end()) { MagicEffects & effects = mPermanentSpellEffects[spell]; for (MagicEffects::Collection::const_iterator effectIt = effects.begin(); effectIt != effects.end();) { const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->first.mId); if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) effects.remove((effectIt++)->first); else ++effectIt; } } mCorprusSpells.erase(corprusIt); } if (iter!=mSpells.end()) mSpells.erase (iter); if (spellId==mSelectedSpell) mSelectedSpell.clear(); } MagicEffects Spells::getMagicEffects() const { // TODO: These are recalculated every frame, no need to do that MagicEffects effects; for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) { const ESM::Spell *spell = iter->first; if (spell->mData.mType==ESM::Spell::ST_Ability || spell->mData.mType==ESM::Spell::ST_Blight || spell->mData.mType==ESM::Spell::ST_Disease || spell->mData.mType==ESM::Spell::ST_Curse) { int i=0; for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it) { float random = 1.f; if (iter->second.find(i) != iter->second.end()) random = iter->second.at(i); effects.add (*it, it->mMagnMin + (it->mMagnMax - it->mMagnMin) * random); ++i; } } } for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) { effects += it->second; } return effects; } void Spells::clear() { mSpells.clear(); } void Spells::setSelectedSpell (const std::string& spellId) { mSelectedSpell = spellId; } const std::string Spells::getSelectedSpell() const { return mSelectedSpell; } bool Spells::isSpellActive(const std::string &id) const { TContainer::const_iterator found = mSpells.find(getSpell(id)); if (found != mSpells.end()) { const ESM::Spell *spell = found->first; return (spell->mData.mType==ESM::Spell::ST_Ability || spell->mData.mType==ESM::Spell::ST_Blight || spell->mData.mType==ESM::Spell::ST_Disease || spell->mData.mType==ESM::Spell::ST_Curse); } return false; } bool Spells::hasCommonDisease() const { for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) { const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Disease) return true; } return false; } bool Spells::hasBlightDisease() const { for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) { const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Blight) return true; } return false; } void Spells::purgeCommonDisease() { for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) { const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Disease) mSpells.erase(iter++); else ++iter; } } void Spells::purgeBlightDisease() { for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) { const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Blight && !hasCorprusEffect(spell)) mSpells.erase(iter++); else ++iter; } } void Spells::purgeCorprusDisease() { for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) { const ESM::Spell *spell = iter->first; if (hasCorprusEffect(spell)) mSpells.erase(iter++); else ++iter; } } void Spells::purgeCurses() { for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) { const ESM::Spell *spell = iter->first; if (spell->mData.mType == ESM::Spell::ST_Curse) mSpells.erase(iter++); else ++iter; } } void Spells::visitEffectSources(EffectSourceVisitor &visitor) const { for (TIterator it = begin(); it != end(); ++it) { const ESM::Spell* spell = it->first; // these are the spell types that are permanently in effect if (!(spell->mData.mType == ESM::Spell::ST_Ability) && !(spell->mData.mType == ESM::Spell::ST_Disease) && !(spell->mData.mType == ESM::Spell::ST_Curse) && !(spell->mData.mType == ESM::Spell::ST_Blight)) continue; const ESM::EffectList& list = spell->mEffects; int i=0; for (std::vector::const_iterator effectIt = list.mList.begin(); effectIt != list.mList.end(); ++effectIt, ++i) { float random = 1.f; if (it->second.find(i) != it->second.end()) random = it->second.at(i); float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, spell->mId, -1, magnitude); } } } void Spells::worsenCorprus(const ESM::Spell* spell) { mCorprusSpells[spell].mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; mCorprusSpells[spell].mWorsenings++; // update worsened effects mPermanentSpellEffects[spell] = MagicEffects(); int i=0; for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt, ++i) { const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); if ((effectIt->mEffectID != ESM::MagicEffect::Corprus) && (magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) // APPLIED_ONCE { float random = 1.f; if (mSpells[spell].find(i) != mSpells[spell].end()) random = mSpells[spell].at(i); float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; magnitude *= std::max(1, mCorprusSpells[spell].mWorsenings); mPermanentSpellEffects[spell].add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(magnitude)); } } } bool Spells::hasCorprusEffect(const ESM::Spell *spell) { for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt) { if (effectIt->mEffectID == ESM::MagicEffect::Corprus) { return true; } } return false; } const std::map &Spells::getCorprusSpells() const { return mCorprusSpells; } bool Spells::canUsePower(const ESM::Spell* spell) const { std::map::const_iterator it = mUsedPowers.find(spell); if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp()) return true; else return false; } void Spells::usePower(const ESM::Spell* spell) { mUsedPowers[spell] = MWBase::Environment::get().getWorld()->getTimeStamp(); } void Spells::readState(const ESM::SpellState &state) { for (ESM::SpellState::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) { // Discard spells that are no longer available due to changed content files const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); if (spell) { mSpells[spell] = it->second; if (it->first == state.mSelectedSpell) mSelectedSpell = it->first; } } for (std::map::const_iterator it = state.mUsedPowers.begin(); it != state.mUsedPowers.end(); ++it) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); if (!spell) continue; mUsedPowers[spell] = MWWorld::TimeStamp(it->second); } for (std::map >::const_iterator it = state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it) { const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); if (!spell) continue; mPermanentSpellEffects[spell] = MagicEffects(); for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) { mPermanentSpellEffects[spell].add(EffectKey(effectIt->mId, effectIt->mArg), effectIt->mMagnitude); } } mCorprusSpells.clear(); for (std::map::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); if (!spell) // Discard unavailable corprus spells continue; mCorprusSpells[spell].mWorsenings = state.mCorprusSpells.at(it->first).mWorsenings; mCorprusSpells[spell].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); } } void Spells::writeState(ESM::SpellState &state) const { for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it) state.mSpells.insert(std::make_pair(it->first->mId, it->second)); state.mSelectedSpell = mSelectedSpell; for (std::map::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it) state.mUsedPowers[it->first->mId] = it->second.toEsm(); for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) { std::vector effectList; for (MagicEffects::Collection::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) { ESM::SpellState::PermanentSpellEffectInfo info; info.mId = effectIt->first.mId; info.mArg = effectIt->first.mArg; info.mMagnitude = effectIt->second.getModifier(); effectList.push_back(info); } state.mPermanentSpellEffects[it->first->mId] = effectList; } for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) { state.mCorprusSpells[it->first->mId].mWorsenings = mCorprusSpells.at(it->first).mWorsenings; state.mCorprusSpells[it->first->mId].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm(); } } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/spells.hpp000066400000000000000000000073301264522266000232030ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_SPELLS_H #define GAME_MWMECHANICS_SPELLS_H #include #include #include #include "../mwworld/ptr.hpp" #include "../mwworld/timestamp.hpp" #include "magiceffects.hpp" namespace ESM { struct Spell; struct SpellState; } namespace MWMechanics { class MagicEffects; /// \brief Spell list /// /// This class manages known spells as well as abilities, powers and permanent negative effects like /// diseases. It also keeps track of used powers (which can only be used every 24h). class Spells { public: typedef const ESM::Spell* SpellKey; typedef std::map > TContainer; // ID, typedef TContainer::const_iterator TIterator; struct CorprusStats { static const int sWorseningPeriod = 24; int mWorsenings; MWWorld::TimeStamp mNextWorsening; }; private: TContainer mSpells; // spell-tied effects that will be applied even after removing the spell (currently used to keep positive effects when corprus is removed) std::map mPermanentSpellEffects; // Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different) std::string mSelectedSpell; std::map mUsedPowers; std::map mCorprusSpells; /// Get spell from ID, throws exception if not found const ESM::Spell* getSpell(const std::string& id) const; public: void worsenCorprus(const ESM::Spell* spell); static bool hasCorprusEffect(const ESM::Spell *spell); const std::map & getCorprusSpells() const; bool canUsePower (const ESM::Spell* spell) const; void usePower (const ESM::Spell* spell); void purgeCommonDisease(); void purgeBlightDisease(); void purgeCorprusDisease(); void purgeCurses(); TIterator begin() const; TIterator end() const; bool hasSpell(const std::string& spell) const; bool hasSpell(const ESM::Spell* spell) const; void add (const std::string& spell); ///< Adding a spell that is already listed in *this is a no-op. void add (const ESM::Spell* spell); ///< Adding a spell that is already listed in *this is a no-op. void remove (const std::string& spell); ///< If the spell to be removed is the selected spell, the selected spell will be changed to /// no spell (empty string). MagicEffects getMagicEffects() const; ///< Return sum of magic effects resulting from abilities, blights, deseases and curses. void clear(); ///< Remove all spells of al types. void setSelectedSpell (const std::string& spellId); ///< This function does not verify, if the spell is available. const std::string getSelectedSpell() const; ///< May return an empty string. bool isSpellActive(const std::string& id) const; ///< Are we under the effects of the given spell ID? bool hasCommonDisease() const; bool hasBlightDisease() const; void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; void readState (const ESM::SpellState& state); void writeState (ESM::SpellState& state) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/stat.cpp000066400000000000000000000153341264522266000226520ustar00rootroot00000000000000#include "stat.hpp" #include namespace MWMechanics { template Stat::Stat() : mBase (0), mModified (0) {} template Stat::Stat(T base) : mBase (base), mModified (base) {} template Stat::Stat(T base, T modified) : mBase (base), mModified (modified) {} template const T& Stat::getBase() const { return mBase; } template T Stat::getModified() const { return std::max(static_cast(0), mModified); } template T Stat::getModifier() const { return mModified-mBase; } template void Stat::set (const T& value) { mBase = mModified = value; } template void Stat::modify(const T& diff) { mBase += diff; if(mBase >= static_cast(0)) mModified += diff; else { mModified += diff - mBase; mBase = static_cast(0); } } template void Stat::setBase (const T& value) { T diff = value - mBase; mBase = value; mModified += diff; } template void Stat::setModified (T value, const T& min, const T& max) { T diff = value - mModified; if (mBase+diffmax) { value = max + (mModified - mBase); diff = value - mModified; } mModified = value; mBase += diff; } template void Stat::setModifier (const T& modifier) { mModified = mBase + modifier; } template void Stat::writeState (ESM::StatState& state) const { state.mBase = mBase; state.mMod = mModified; } template void Stat::readState (const ESM::StatState& state) { mBase = state.mBase; mModified = state.mMod; } template DynamicStat::DynamicStat() : mStatic (0), mCurrent (0) {} template DynamicStat::DynamicStat(T base) : mStatic (base), mCurrent (base) {} template DynamicStat::DynamicStat(T base, T modified, T current) : mStatic(base, modified), mCurrent (current) {} template DynamicStat::DynamicStat(const Stat &stat, T current) : mStatic(stat), mCurrent (current) {} template const T& DynamicStat::getBase() const { return mStatic.getBase(); } template T DynamicStat::getModified() const { return mStatic.getModified(); } template const T& DynamicStat::getCurrent() const { return mCurrent; } template void DynamicStat::set (const T& value) { mStatic.set (value); mCurrent = value; } template void DynamicStat::setBase (const T& value) { mStatic.setBase (value); if (mCurrent>getModified()) mCurrent = getModified(); } template void DynamicStat::setModified (T value, const T& min, const T& max) { mStatic.setModified (value, min, max); if (mCurrent>getModified()) mCurrent = getModified(); } template void DynamicStat::modify (const T& diff, bool allowCurrentDecreaseBelowZero) { mStatic.modify (diff); setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero); } template void DynamicStat::setCurrent (const T& value, bool allowDecreaseBelowZero) { if (value > mCurrent) { // increase mCurrent = value; if (mCurrent > getModified()) mCurrent = getModified(); } else if (value > 0 || allowDecreaseBelowZero) { // allowed decrease mCurrent = value; } else if (mCurrent > 0) { // capped decrease mCurrent = 0; } } template void DynamicStat::setModifier (const T& modifier, bool allowCurrentDecreaseBelowZero) { T diff = modifier - mStatic.getModifier(); mStatic.setModifier (modifier); setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero); } template void DynamicStat::writeState (ESM::StatState& state) const { mStatic.writeState (state); state.mCurrent = mCurrent; } template void DynamicStat::readState (const ESM::StatState& state) { mStatic.readState (state); mCurrent = state.mCurrent; } AttributeValue::AttributeValue() : mBase(0), mModifier(0), mDamage(0) { } int AttributeValue::getModified() const { return std::max(0, mBase - (int) mDamage + mModifier); } int AttributeValue::getBase() const { return mBase; } int AttributeValue::getModifier() const { return mModifier; } void AttributeValue::setBase(int base) { mBase = std::max(0, base); } void AttributeValue::setModifier(int mod) { mModifier = mod; } void AttributeValue::damage(float damage) { mDamage += std::min(damage, (float)getModified()); } void AttributeValue::restore(float amount) { mDamage -= std::min(mDamage, amount); } float AttributeValue::getDamage() const { return mDamage; } void AttributeValue::writeState (ESM::StatState& state) const { state.mBase = mBase; state.mMod = mModifier; state.mDamage = mDamage; } void AttributeValue::readState (const ESM::StatState& state) { mBase = state.mBase; mModifier = state.mMod; mDamage = state.mDamage; } SkillValue::SkillValue() : mProgress(0) { } float SkillValue::getProgress() const { return mProgress; } void SkillValue::setProgress(float progress) { mProgress = progress; } void SkillValue::writeState (ESM::StatState& state) const { AttributeValue::writeState (state); state.mProgress = mProgress; } void SkillValue::readState (const ESM::StatState& state) { AttributeValue::readState (state); mProgress = state.mProgress; } } template class MWMechanics::Stat; template class MWMechanics::Stat; template class MWMechanics::DynamicStat; template class MWMechanics::DynamicStat; openmw-openmw-0.38.0/apps/openmw/mwmechanics/stat.hpp000066400000000000000000000120141264522266000226470ustar00rootroot00000000000000#ifndef GAME_MWMECHANICS_STAT_H #define GAME_MWMECHANICS_STAT_H #include #include namespace ESM { template struct StatState; } namespace MWMechanics { template class Stat { T mBase; T mModified; public: typedef T Type; Stat(); Stat(T base); Stat(T base, T modified); const T& getBase() const; T getModified() const; T getModifier() const; /// Set base and modified to \a value. void set (const T& value); void modify(const T& diff); /// Set base and adjust modified accordingly. void setBase (const T& value); /// Set modified value an adjust base accordingly. void setModified (T value, const T& min, const T& max = std::numeric_limits::max()); void setModifier (const T& modifier); void writeState (ESM::StatState& state) const; void readState (const ESM::StatState& state); }; template inline bool operator== (const Stat& left, const Stat& right) { return left.getBase()==right.getBase() && left.getModified()==right.getModified(); } template inline bool operator!= (const Stat& left, const Stat& right) { return !(left==right); } template class DynamicStat { Stat mStatic; T mCurrent; public: typedef T Type; DynamicStat(); DynamicStat(T base); DynamicStat(T base, T modified, T current); DynamicStat(const Stat &stat, T current); const T& getBase() const; T getModified() const; const T& getCurrent() const; /// Set base, modified and current to \a value. void set (const T& value); /// Set base and adjust modified accordingly. void setBase (const T& value); /// Set modified value an adjust base accordingly. void setModified (T value, const T& min, const T& max = std::numeric_limits::max()); /// Change modified relatively. void modify (const T& diff, bool allowCurrentDecreaseBelowZero=false); void setCurrent (const T& value, bool allowDecreaseBelowZero = false); void setModifier (const T& modifier, bool allowCurrentDecreaseBelowZero=false); void writeState (ESM::StatState& state) const; void readState (const ESM::StatState& state); }; template inline bool operator== (const DynamicStat& left, const DynamicStat& right) { return left.getBase()==right.getBase() && left.getModified()==right.getModified() && left.getCurrent()==right.getCurrent(); } template inline bool operator!= (const DynamicStat& left, const DynamicStat& right) { return !(left==right); } class AttributeValue { int mBase; int mModifier; float mDamage; // needs to be float to allow continuous damage public: AttributeValue(); int getModified() const; int getBase() const; int getModifier() const; void setBase(int base); void setModifier(int mod); // Maximum attribute damage is limited to the modified value. // Note: I think MW applies damage directly to mModified, since you can also // "restore" drained attributes. We need to rewrite the magic effect system to support this. void damage(float damage); void restore(float amount); float getDamage() const; void writeState (ESM::StatState& state) const; void readState (const ESM::StatState& state); }; class SkillValue : public AttributeValue { float mProgress; public: SkillValue(); float getProgress() const; void setProgress(float progress); void writeState (ESM::StatState& state) const; void readState (const ESM::StatState& state); }; inline bool operator== (const AttributeValue& left, const AttributeValue& right) { return left.getBase() == right.getBase() && left.getModifier() == right.getModifier() && left.getDamage() == right.getDamage(); } inline bool operator!= (const AttributeValue& left, const AttributeValue& right) { return !(left == right); } inline bool operator== (const SkillValue& left, const SkillValue& right) { return left.getBase() == right.getBase() && left.getModifier() == right.getModifier() && left.getDamage() == right.getDamage() && left.getProgress() == right.getProgress(); } inline bool operator!= (const SkillValue& left, const SkillValue& right) { return !(left == right); } } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/steering.cpp000066400000000000000000000024161264522266000235140ustar00rootroot00000000000000#include "steering.hpp" #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" #include "../mwbase/environment.hpp" #include "movement.hpp" namespace MWMechanics { bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, float epsilonRadians) { float currentAngle (actor.getRefData().getPosition().rot[axis]); float diff (targetAngleRadians - currentAngle); if (std::abs(diff) >= osg::DegreesToRadians(180.f)) { if (diff >= 0) { diff = diff - osg::DegreesToRadians(360.f); } else { diff = osg::DegreesToRadians(360.f) + diff; } } float absDiff = std::abs(diff); // The turning animation actually moves you slightly, so the angle will be wrong again. // Use epsilon to prevent jerkiness. if (absDiff < epsilonRadians) return true; float limit = MAX_VEL_ANGULAR_RADIANS * MWBase::Environment::get().getFrameDuration(); if (absDiff > limit) diff = osg::sign(diff) * limit; actor.getClass().getMovementSettings(actor).mRotation[axis] = diff; return false; } bool zTurn(const MWWorld::Ptr& actor, float targetAngleRadians, float epsilonRadians) { return smoothTurn(actor, targetAngleRadians, 2, epsilonRadians); } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/steering.hpp000066400000000000000000000012671264522266000235240ustar00rootroot00000000000000#ifndef OPENMW_MECHANICS_STEERING_H #define OPENMW_MECHANICS_STEERING_H #include namespace MWWorld { class Ptr; } namespace MWMechanics { // Max rotating speed, radian/sec const float MAX_VEL_ANGULAR_RADIANS(10); /// configure rotation settings for an actor to reach this target angle (eventually) /// @return have we reached the target angle? bool zTurn(const MWWorld::Ptr& actor, float targetAngleRadians, float epsilonRadians = osg::DegreesToRadians(0.5)); bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, float epsilonRadians = osg::DegreesToRadians(0.5)); } #endif openmw-openmw-0.38.0/apps/openmw/mwmechanics/summoning.cpp000066400000000000000000000225341264522266000237130ustar00rootroot00000000000000#include "summoning.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwrender/animation.hpp" #include "creaturestats.hpp" #include "aifollow.hpp" namespace MWMechanics { void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId); if (!ptr.isEmpty()) { // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation // plays though, which is a rather lame exploit in vanilla. MWBase::Environment::get().getWorld()->deleteObject(ptr); const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() .search("VFX_Summon_End"); if (fx) MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, "", ptr.getRefData().getPosition().asVec3()); } else if (creatureActorId != -1) { // We didn't find the creature. It's probably in an inactive cell. // Add to graveyard so we can delete it when the cell becomes active. std::vector& graveyard = casterStats.getSummonedCreatureGraveyard(); graveyard.push_back(creatureActorId); } } UpdateSummonedCreatures::UpdateSummonedCreatures(const MWWorld::Ptr &actor) : mActor(actor) { } UpdateSummonedCreatures::~UpdateSummonedCreatures() { } void UpdateSummonedCreatures::visit(EffectKey key, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) { if (isSummoningEffect(key.mId) && magnitude > 0) { mActiveEffects.insert(std::make_pair(key.mId, sourceId)); } } void UpdateSummonedCreatures::finish() { static std::map summonMap; if (summonMap.empty()) { summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID"; summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID"; summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID"; summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID"; summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID"; summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID"; summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID"; summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID"; summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID"; summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID"; summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID"; summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID"; summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID"; summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID"; summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID"; summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID"; summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID"; summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID"; summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID"; summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID"; summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID"; summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID"; } MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); // Update summon effects std::map& creatureMap = creatureStats.getSummonedCreatureMap(); for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) { bool found = mActiveEffects.find(it->first) != mActiveEffects.end(); if (!found) { // Effect has ended cleanupSummonedCreature(creatureStats, it->second); creatureMap.erase(it++); continue; } ++it; } for (std::set >::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it) { bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end(); if (!found) { ESM::Position ipos = mActor.getRefData().getPosition(); osg::Vec3f pos(ipos.asVec3()); osg::Quat rot (-ipos.rot[2], osg::Vec3f(0,0,1)); const float distance = 50; pos = pos + (rot * osg::Vec3f(0,1,0)) * distance; ipos.pos[0] = pos.x(); ipos.pos[1] = pos.y(); ipos.pos[2] = pos.z(); ipos.rot[0] = 0; ipos.rot[1] = 0; ipos.rot[2] = 0; const std::string& creatureGmst = summonMap[it->first]; std::string creatureID = MWBase::Environment::get().getWorld()->getStore().get().find(creatureGmst)->getString(); if (!creatureID.empty()) { MWWorld::CellStore* store = mActor.getCell(); int creatureActorId = -1; try { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); ref.getPtr().getCellRef().setPosition(ipos); MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); // Make the summoned creature follow its master and help in fights AiFollow package(mActor.getCellRef().getRefId()); summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); creatureActorId = summonedCreatureStats.getActorId(); MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); if (anim) { const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() .search("VFX_Summon_Start"); if (fx) anim->addEffect("meshes\\" + fx->mModel, -1, false); } } catch (std::exception& e) { std::cerr << "Failed to spawn summoned creature: " << e.what() << std::endl; // still insert into creatureMap so we don't try to spawn again every frame, that would spam the warning log } creatureMap.insert(std::make_pair(*it, creatureActorId)); } } } for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); ) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead()) { // Purge the magic effect so a new creature can be summoned if desired creatureStats.getActiveSpells().purgeEffect(it->first.first, it->first.second); if (mActor.getClass().hasInventoryStore(ptr)) mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second); cleanupSummonedCreature(creatureStats, it->second); creatureMap.erase(it++); } else ++it; } std::vector& graveyard = creatureStats.getSummonedCreatureGraveyard(); for (std::vector::iterator it = graveyard.begin(); it != graveyard.end(); ) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it); if (!ptr.isEmpty()) { it = graveyard.erase(it); const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() .search("VFX_Summon_End"); if (fx) MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, "", ptr.getRefData().getPosition().asVec3()); MWBase::Environment::get().getWorld()->deleteObject(ptr); } else ++it; } } } openmw-openmw-0.38.0/apps/openmw/mwmechanics/summoning.hpp000066400000000000000000000016641264522266000237210ustar00rootroot00000000000000#ifndef OPENMW_MECHANICS_SUMMONING_H #define OPENMW_MECHANICS_SUMMONING_H #include #include "magiceffects.hpp" #include "../mwworld/ptr.hpp" namespace MWMechanics { class CreatureStats; struct UpdateSummonedCreatures : public EffectSourceVisitor { UpdateSummonedCreatures(const MWWorld::Ptr& actor); virtual ~UpdateSummonedCreatures(); virtual void visit (MWMechanics::EffectKey key, const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1); /// To call after all effect sources have been visited void finish(); private: MWWorld::Ptr mActor; std::set > mActiveEffects; }; void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId); } #endif openmw-openmw-0.38.0/apps/openmw/mwphysics/000077500000000000000000000000001264522266000207155ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwphysics/actor.cpp000066400000000000000000000107611264522266000225360ustar00rootroot00000000000000#include "actor.hpp" #include #include #include #include #include #include "../mwworld/class.hpp" #include "convert.hpp" #include "collisiontype.hpp" namespace MWPhysics { Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world) : mCanWaterWalk(false), mWalkingOnWater(false) , mCollisionObject(0), mForce(0.f, 0.f, 0.f), mOnGround(false) , mInternalCollisionMode(true) , mExternalCollisionMode(true) , mCollisionWorld(world) { mPtr = ptr; mHalfExtents = shape->mCollisionBoxHalfExtents; mMeshTranslation = shape->mCollisionBoxTranslate; // Use capsule shape only if base is square (nonuniform scaling apparently doesn't work on it) if (std::abs(mHalfExtents.x()-mHalfExtents.y())= mHalfExtents.x()) { // Could also be btCapsuleShapeZ, but the movement solver seems to have issues with it (jumping on slopes doesn't work) mShape.reset(new btCylinderShapeZ(toBullet(mHalfExtents))); } else mShape.reset(new btBoxShape(toBullet(mHalfExtents))); mCollisionObject.reset(new btCollisionObject); mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); mCollisionObject->setActivationState(DISABLE_DEACTIVATION); mCollisionObject->setCollisionShape(mShape.get()); mCollisionObject->setUserPointer(static_cast(this)); updateRotation(); updateScale(); // already called by updateScale() //updatePosition(); updateCollisionMask(); } Actor::~Actor() { if (mCollisionObject.get()) mCollisionWorld->removeCollisionObject(mCollisionObject.get()); } void Actor::enableCollisionMode(bool collision) { mInternalCollisionMode = collision; } void Actor::enableCollisionBody(bool collision) { if (mExternalCollisionMode != collision) { mExternalCollisionMode = collision; updateCollisionMask(); } } void Actor::updateCollisionMask() { mCollisionWorld->removeCollisionObject(mCollisionObject.get()); int collisionMask = CollisionType_World | CollisionType_HeightMap; if (mExternalCollisionMode) collisionMask |= CollisionType_Actor | CollisionType_Projectile | CollisionType_Door; if (mCanWaterWalk) collisionMask |= CollisionType_Water; mCollisionWorld->addCollisionObject(mCollisionObject.get(), CollisionType_Actor, collisionMask); } void Actor::updatePosition() { osg::Vec3f position = mPtr.getRefData().getPosition().asVec3(); btTransform tr = mCollisionObject->getWorldTransform(); osg::Vec3f scaledTranslation = mRotation * osg::componentMultiply(mMeshTranslation, mScale); osg::Vec3f newPosition = scaledTranslation + position; tr.setOrigin(toBullet(newPosition)); mCollisionObject->setWorldTransform(tr); } osg::Vec3f Actor::getPosition() const { return toOsg(mCollisionObject->getWorldTransform().getOrigin()); } void Actor::updateRotation () { btTransform tr = mCollisionObject->getWorldTransform(); mRotation = mPtr.getRefData().getBaseNode()->getAttitude(); tr.setRotation(toBullet(mRotation)); mCollisionObject->setWorldTransform(tr); updatePosition(); } void Actor::updateScale() { float scale = mPtr.getCellRef().getScale(); osg::Vec3f scaleVec(scale,scale,scale); mPtr.getClass().adjustScale(mPtr, scaleVec, false); mScale = scaleVec; mShape->setLocalScaling(toBullet(mScale)); scaleVec = osg::Vec3f(scale,scale,scale); mPtr.getClass().adjustScale(mPtr, scaleVec, true); mRenderingScale = scaleVec; updatePosition(); } osg::Vec3f Actor::getHalfExtents() const { return osg::componentMultiply(mHalfExtents, mScale); } osg::Vec3f Actor::getRenderingHalfExtents() const { return osg::componentMultiply(mHalfExtents, mRenderingScale); } void Actor::setInertialForce(const osg::Vec3f &force) { mForce = force; } void Actor::setOnGround(bool grounded) { mOnGround = grounded; } bool Actor::isWalkingOnWater() const { return mWalkingOnWater; } void Actor::setWalkingOnWater(bool walkingOnWater) { mWalkingOnWater = walkingOnWater; } void Actor::setCanWaterWalk(bool waterWalk) { if (waterWalk != mCanWaterWalk) { mCanWaterWalk = waterWalk; updateCollisionMask(); } } } openmw-openmw-0.38.0/apps/openmw/mwphysics/actor.hpp000066400000000000000000000100041264522266000225310ustar00rootroot00000000000000#ifndef OPENMW_MWPHYSICS_ACTOR_H #define OPENMW_MWPHYSICS_ACTOR_H #include #include "../mwworld/ptr.hpp" #include #include #include class btCollisionWorld; class btCollisionShape; class btCollisionObject; namespace Resource { class BulletShapeInstance; } namespace MWPhysics { class PtrHolder { public: virtual ~PtrHolder() {} void updatePtr(const MWWorld::Ptr& updated) { mPtr = updated; } MWWorld::Ptr getPtr() { return mPtr; } MWWorld::ConstPtr getPtr() const { return mPtr; } protected: MWWorld::Ptr mPtr; }; class Actor : public PtrHolder { public: Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world); ~Actor(); /** * Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry. */ void enableCollisionMode(bool collision); bool getCollisionMode() const { return mInternalCollisionMode; } /** * Enables or disables the *external* collision body. If disabled, other actors will not collide with this actor. */ void enableCollisionBody(bool collision); void updateScale(); void updateRotation(); void updatePosition(); /** * Returns the half extents of the collision body (scaled according to collision scale) */ osg::Vec3f getHalfExtents() const; /** * Returns the position of the collision body * @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. */ osg::Vec3f getPosition() const; /** * Returns the half extents of the collision body (scaled according to rendering scale) * @note The reason we need this extra method is because of an inconsistency in MW - NPC race scales aren't applied to the collision shape, * most likely to make environment collision testing easier. However in some cases (swimming level) we want the actual scale. */ osg::Vec3f getRenderingHalfExtents() const; /** * Sets the current amount of inertial force (incl. gravity) affecting this physic actor */ void setInertialForce(const osg::Vec3f &force); /** * Gets the current amount of inertial force (incl. gravity) affecting this physic actor */ const osg::Vec3f &getInertialForce() const { return mForce; } void setOnGround(bool grounded); bool getOnGround() const { return mInternalCollisionMode && mOnGround; } btCollisionObject* getCollisionObject() const { return mCollisionObject.get(); } /// Sets whether this actor should be able to collide with the water surface void setCanWaterWalk(bool waterWalk); /// Sets whether this actor has been walking on the water surface in the last frame void setWalkingOnWater(bool walkingOnWater); bool isWalkingOnWater() const; private: /// Removes then re-adds the collision object to the dynamics world void updateCollisionMask(); bool mCanWaterWalk; bool mWalkingOnWater; std::auto_ptr mShape; std::auto_ptr mCollisionObject; osg::Vec3f mMeshTranslation; osg::Vec3f mHalfExtents; osg::Quat mRotation; osg::Vec3f mScale; osg::Vec3f mRenderingScale; osg::Vec3f mPosition; osg::Vec3f mForce; bool mOnGround; bool mInternalCollisionMode; bool mExternalCollisionMode; btCollisionWorld* mCollisionWorld; Actor(const Actor&); Actor& operator=(const Actor&); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwphysics/collisiontype.hpp000066400000000000000000000005241264522266000243240ustar00rootroot00000000000000#ifndef OPENMW_MWPHYSICS_COLLISIONTYPE_H #define OPENMW_MWPHYSICS_COLLISIONTYPE_H namespace MWPhysics { enum CollisionType { CollisionType_World = 1<<0, CollisionType_Door = 1<<1, CollisionType_Actor = 1<<2, CollisionType_HeightMap = 1<<3, CollisionType_Projectile = 1<<4, CollisionType_Water = 1<<5 }; } #endif openmw-openmw-0.38.0/apps/openmw/mwphysics/convert.hpp000066400000000000000000000013241264522266000231060ustar00rootroot00000000000000#ifndef OPENMW_MWPHYSICS_CONVERT_H #define OPENMW_MWPHYSICS_CONVERT_H #include #include #include #include namespace MWPhysics { inline btVector3 toBullet(const osg::Vec3f& vec) { return btVector3(vec.x(), vec.y(), vec.z()); } inline btQuaternion toBullet(const osg::Quat& quat) { return btQuaternion(quat.x(), quat.y(), quat.z(), quat.w()); } inline osg::Vec3f toOsg(const btVector3& vec) { return osg::Vec3f(vec.x(), vec.y(), vec.z()); } inline osg::Quat toOsg(const btQuaternion& quat) { return osg::Quat(quat.x(), quat.y(), quat.z(), quat.w()); } } #endif openmw-openmw-0.38.0/apps/openmw/mwphysics/physicssystem.cpp000066400000000000000000001610041264522266000243520ustar00rootroot00000000000000#include "physicssystem.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // FindRecIndexVisitor #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwrender/bulletdebugdraw.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "collisiontype.hpp" #include "actor.hpp" #include "convert.hpp" #include "trace.h" namespace MWPhysics { static const float sMaxSlope = 49.0f; static const float sStepSizeUp = 34.0f; static const float sStepSizeDown = 62.0f; // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. static const int sMaxIterations = 8; // FIXME: move to a separate file class MovementSolver { private: static float getSlope(osg::Vec3f normal) { normal.normalize(); return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f))); } static bool stepMove(btCollisionObject *colobj, osg::Vec3f &position, const osg::Vec3f &toMove, float &remainingTime, btCollisionWorld* collisionWorld) { /* * Slide up an incline or set of stairs. Should be called only after a * collision detection otherwise unnecessary tracing will be performed. * * NOTE: with a small change this method can be used to step over an obstacle * of height sStepSize. * * If successful return 'true' and update 'position' to the new possible * location and adjust 'remainingTime'. * * If not successful return 'false'. May fail for these reasons: * - can't move directly up from current position * - having moved up by between epsilon() and sStepSize, can't move forward * - having moved forward by between epsilon() and toMove, * = moved down between 0 and just under sStepSize but slope was too steep, or * = moved the full sStepSize down (FIXME: this could be a bug) * * * * Starting position. Obstacle or stairs with height upto sStepSize in front. * * +--+ +--+ |XX * | | -------> toMove | | +--+XX * | | | | |XXXXX * | | +--+ | | +--+XXXXX * | | |XX| | | |XXXXXXXX * +--+ +--+ +--+ +-------- * ============================================== */ /* * Try moving up sStepSize using stepper. * FIXME: does not work in case there is no front obstacle but there is one above * * +--+ +--+ * | | | | * | | | | |XX * | | | | +--+XX * | | | | |XXXXX * +--+ +--+ +--+ +--+XXXXX * |XX| |XXXXXXXX * +--+ +-------- * ============================================== */ ActorTracer tracer, stepper; stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld); if(stepper.mFraction < std::numeric_limits::epsilon()) return false; // didn't even move the smallest representable amount // (TODO: shouldn't this be larger? Why bother with such a small amount?) /* * Try moving from the elevated position using tracer. * * +--+ +--+ * | | |YY| FIXME: collision with object YY * | | +--+ * | | * <------------------->| | * +--+ +--+ * |XX| the moved amount is toMove*tracer.mFraction * +--+ * ============================================== */ tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, collisionWorld); if(tracer.mFraction < std::numeric_limits::epsilon()) return false; // didn't even move the smallest representable amount /* * Try moving back down sStepSizeDown using stepper. * NOTE: if there is an obstacle below (e.g. stairs), we'll be "stepping up". * Below diagram is the case where we "stepped over" an obstacle in front. * * +--+ * |YY| * +--+ +--+ * | | * | | * +--+ | | * |XX| | | * +--+ +--+ * ============================================== */ stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld); if(stepper.mFraction < 1.0f && getSlope(stepper.mPlaneNormal) <= sMaxSlope) { // don't allow stepping up other actors if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor) return false; // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing // NOTE: caller's variables 'position' & 'remainingTime' are modified here position = stepper.mEndPos; remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance return true; } // moved between 0 and just under sStepSize distance but slope was too great, // or moved full sStepSize distance (FIXME: is this a bug?) return false; } ///Project a vector u on another vector v static inline osg::Vec3f project(const osg::Vec3f& u, const osg::Vec3f &v) { return v * (u * v); // ^ dot product } ///Helper for computing the character sliding static inline osg::Vec3f slide(const osg::Vec3f& direction, const osg::Vec3f &planeNormal) { return direction - project(direction, planeNormal); } static inline osg::Vec3f reflect(const osg::Vec3& velocity, const osg::Vec3f& normal) { return velocity - (normal * (normal * velocity)) * 2; // ^ dot product } public: static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight) { osg::Vec3f position(ptr.getRefData().getPosition().asVec3()); ActorTracer tracer; tracer.findGround(actor, position, position-osg::Vec3f(0,0,maxHeight), collisionWorld); if(tracer.mFraction >= 1.0f) { actor->setOnGround(false); return position; } else { // Check if we actually found a valid spawn point (use an infinitely thin ray this time). // Required for some broken door destinations in Morrowind.esm, where the spawn point // intersects with other geometry if the actor's base is taken into account btVector3 from = toBullet(position); btVector3 to = from - btVector3(0,0,maxHeight); btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); resultCallback1.m_collisionFilterGroup = 0xff; resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap; collisionWorld->rayTest(from, to, resultCallback1); if (resultCallback1.hasHit() && ( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length() > 30 || getSlope(tracer.mPlaneNormal) > sMaxSlope)) { actor->setOnGround(getSlope(toOsg(resultCallback1.m_hitNormalWorld)) <= sMaxSlope); return toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, 1.f); } actor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope); return tracer.mEndPos; } } static osg::Vec3f move(const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time, bool isFlying, float waterlevel, float slowFall, btCollisionWorld* collisionWorld, std::map& standingCollisionTracker) { const ESM::Position& refpos = ptr.getRefData().getPosition(); osg::Vec3f position(refpos.asVec3()); // Early-out for totally static creatures // (Not sure if gravity should still apply?) if (!ptr.getClass().isMobile(ptr)) return position; // Reset per-frame data physicActor->setWalkingOnWater(false); // Anything to collide with? if(!physicActor->getCollisionMode()) { return position + (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1)) ) * movement * time; } btCollisionObject *colobj = physicActor->getCollisionObject(); osg::Vec3f halfExtents = physicActor->getHalfExtents(); // NOTE: here we don't account for the collision box translation (i.e. physicActor->getPosition() - refpos.pos). // That means the collision shape used for moving this actor is in a different spot than the collision shape // other actors are using to collide against this actor. // While this is strictly speaking wrong, it's needed for MW compatibility. position.z() += halfExtents.z(); static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get() .find("fSwimHeightScale")->getFloat(); float swimlevel = waterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale); ActorTracer tracer; osg::Vec3f inertia = physicActor->getInertialForce(); osg::Vec3f velocity; if(position.z() < swimlevel || isFlying) { velocity = (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; } else { velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; if (velocity.z() > 0.f) inertia = velocity; if(!physicActor->getOnGround()) { velocity = velocity + physicActor->getInertialForce(); } } // dead actors underwater will float to the surface if (ptr.getClass().getCreatureStats(ptr).isDead() && position.z() < swimlevel) velocity = osg::Vec3f(0,0,1) * 25; ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0; // Now that we have the effective movement vector, apply wind forces to it if (MWBase::Environment::get().getWorld()->isInStorm()) { osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length()))); static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fStromWalkMult")->getFloat(); velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f)); } osg::Vec3f origVelocity = velocity; osg::Vec3f newPosition = position; /* * A loop to find newPosition using tracer, if successful different from the starting position. * nextpos is the local variable used to find potential newPosition, using velocity and remainingTime * The initial velocity was set earlier (see above). */ float remainingTime = time; for(int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations) { osg::Vec3f nextpos = newPosition + velocity * remainingTime; // If not able to fly, don't allow to swim up into the air if(newPosition.z() < swimlevel && !isFlying && // can't fly nextpos.z() > swimlevel && // but about to go above water newPosition.z() <= swimlevel) { const osg::Vec3f down(0,0,-1); float movelen = velocity.normalize(); osg::Vec3f reflectdir = reflect(velocity, down); reflectdir.normalize(); velocity = slide(reflectdir, down)*movelen; // NOTE: remainingTime is unchanged before the loop continues continue; // velocity updated, calculate nextpos again } if((newPosition - nextpos).length2() > 0.0001) { // trace to where character would go if there were no obstructions tracer.doTrace(colobj, newPosition, nextpos, collisionWorld); // check for obstructions if(tracer.mFraction >= 1.0f) { newPosition = tracer.mEndPos; // ok to move, so set newPosition break; } } else { // The current position and next position are nearly the same, so just exit. // Note: Bullet can trigger an assert in debug modes if the positions // are the same, since that causes it to attempt to normalize a zero // length vector (which can also happen with nearly identical vectors, since // precision can be lost due to any math Bullet does internally). Since we // aren't performing any collision detection, we want to reject the next // position, so that we don't slowly move inside another object. break; } osg::Vec3f oldPosition = newPosition; // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // NOTE: stepMove modifies newPosition if successful bool result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld); if (!result) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent { osg::Vec3f normalizedVelocity = velocity; normalizedVelocity.normalize(); result = stepMove(colobj, newPosition, normalizedVelocity*10.f, remainingTime, collisionWorld); } if(result) { // don't let pure water creatures move out of water after stepMove if (ptr.getClass().isPureWaterCreature(ptr) && newPosition.z() + halfExtents.z() > waterlevel) newPosition = oldPosition; } else { // Can't move this way, try to find another spot along the plane osg::Vec3f direction = velocity; float movelen = direction.normalize(); osg::Vec3f reflectdir = reflect(velocity, tracer.mPlaneNormal); reflectdir.normalize(); osg::Vec3f newVelocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; if ((newVelocity-velocity).length2() < 0.01) break; if ((velocity * origVelocity) <= 0.f) break; // ^ dot product velocity = newVelocity; // Do not allow sliding upward if there is gravity. Stepping will have taken // care of that. if(!(newPosition.z() < swimlevel || isFlying)) velocity.z() = std::min(velocity.z(), 0.0f); } } bool isOnGround = false; if (!(inertia.z() > 0.f) && !(newPosition.z() < swimlevel)) { osg::Vec3f from = newPosition; osg::Vec3f to = newPosition - (physicActor->getOnGround() ? osg::Vec3f(0,0,sStepSizeDown+2.f) : osg::Vec3f(0,0,2.f)); tracer.doTrace(colobj, from, to, collisionWorld); if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor) { const btCollisionObject* standingOn = tracer.mHitObject; PtrHolder* ptrHolder = static_cast(standingOn->getUserPointer()); if (ptrHolder) standingCollisionTracker[ptr] = ptrHolder->getPtr(); if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water) physicActor->setWalkingOnWater(true); if (!isFlying) newPosition.z() = tracer.mEndPos.z() + 1.0f; isOnGround = true; } else { // standing on actors is not allowed (see above). // in addition to that, apply a sliding effect away from the center of the actor, // so that we do not stay suspended in air indefinitely. if (tracer.mFraction < 1.0f && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor) { if (osg::Vec3f(velocity.x(), velocity.y(), 0).length2() < 100.f*100.f) { btVector3 aabbMin, aabbMax; tracer.mHitObject->getCollisionShape()->getAabb(tracer.mHitObject->getWorldTransform(), aabbMin, aabbMax); btVector3 center = (aabbMin + aabbMax) / 2.f; inertia = osg::Vec3f(position.x() - center.x(), position.y() - center.y(), 0); inertia.normalize(); inertia *= 100; } } isOnGround = false; } } if(isOnGround || newPosition.z() < swimlevel || isFlying) physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f)); else { inertia.z() += time * -627.2f; if (inertia.z() < 0) inertia.z() *= slowFall; physicActor->setInertialForce(inertia); } physicActor->setOnGround(isOnGround); newPosition.z() -= halfExtents.z(); // remove what was added at the beginning return newPosition; } }; // --------------------------------------------------------------- class HeightField { public: HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts) { // find the minimum and maximum heights (needed for bullet) float minh = heights[0]; float maxh = heights[0]; for(int i = 1;i < sqrtVerts*sqrtVerts;++i) { float h = heights[i]; if(h > maxh) maxh = h; if(h < minh) minh = h; } mShape = new btHeightfieldTerrainShape( sqrtVerts, sqrtVerts, heights, 1, minh, maxh, 2, PHY_FLOAT, true ); mShape->setUseDiamondSubdivision(true); mShape->setLocalScaling(btVector3(triSize, triSize, 1)); btTransform transform(btQuaternion::getIdentity(), btVector3((x+0.5f) * triSize * (sqrtVerts-1), (y+0.5f) * triSize * (sqrtVerts-1), (maxh+minh)*0.5f)); mCollisionObject = new btCollisionObject; mCollisionObject->setCollisionShape(mShape); mCollisionObject->setWorldTransform(transform); } ~HeightField() { delete mCollisionObject; delete mShape; } btCollisionObject* getCollisionObject() { return mCollisionObject; } private: btHeightfieldTerrainShape* mShape; btCollisionObject* mCollisionObject; void operator=(const HeightField&); HeightField(const HeightField&); }; // -------------------------------------------------------------- class Object : public PtrHolder { public: Object(const MWWorld::Ptr& ptr, osg::ref_ptr shapeInstance) : mShapeInstance(shapeInstance) , mSolid(true) { mPtr = ptr; mCollisionObject.reset(new btCollisionObject); mCollisionObject->setCollisionShape(shapeInstance->getCollisionShape()); mCollisionObject->setUserPointer(static_cast(this)); setScale(ptr.getCellRef().getScale()); setRotation(toBullet(ptr.getRefData().getBaseNode()->getAttitude())); const float* pos = ptr.getRefData().getPosition().pos; setOrigin(btVector3(pos[0], pos[1], pos[2])); } void setScale(float scale) { mShapeInstance->getCollisionShape()->setLocalScaling(btVector3(scale,scale,scale)); } void setRotation(const btQuaternion& quat) { mCollisionObject->getWorldTransform().setRotation(quat); } void setOrigin(const btVector3& vec) { mCollisionObject->getWorldTransform().setOrigin(vec); } btCollisionObject* getCollisionObject() { return mCollisionObject.get(); } /// Return solid flag. Not used by the object itself, true by default. bool isSolid() const { return mSolid; } void setSolid(bool solid) { mSolid = solid; } bool isAnimated() const { return !mShapeInstance->mAnimatedShapes.empty(); } void animateCollisionShapes(btCollisionWorld* collisionWorld) { if (mShapeInstance->mAnimatedShapes.empty()) return; assert (mShapeInstance->getCollisionShape()->isCompound()); btCompoundShape* compound = dynamic_cast(mShapeInstance->getCollisionShape()); for (std::map::iterator it = mShapeInstance->mAnimatedShapes.begin(); it != mShapeInstance->mAnimatedShapes.end();) { int recIndex = it->first; int shapeIndex = it->second; NifOsg::FindGroupByRecIndex visitor(recIndex); mPtr.getRefData().getBaseNode()->accept(visitor); if (!visitor.mFound) { std::cerr << "animateCollisionShapes: Can't find node " << recIndex << std::endl; return; } osg::NodePath path = visitor.mFoundPath; path.erase(path.begin()); // Attempt to remove "animated" shapes that are not actually animated // We may get these because the BulletNifLoader does not know if a .kf file with additional controllers will be attached later on. // On the first animateCollisionShapes call, we'll consider the graph completely loaded (with extra controllers and what not), // so now we can better decide if the shape is really animated. bool animated = false; for (osg::NodePath::iterator nodePathIt = path.begin(); nodePathIt != path.end(); ++nodePathIt) { osg::Node* node = *nodePathIt; if (node->getUpdateCallback()) animated = true; } if (!animated) { mShapeInstance->mAnimatedShapes.erase(it++); break; } osg::Matrixf matrix = osg::computeLocalToWorld(path); osg::Vec3f scale = matrix.getScale(); matrix.orthoNormalize(matrix); btTransform transform; transform.setOrigin(toBullet(matrix.getTrans()) * compound->getLocalScaling()); for (int i=0; i<3; ++i) for (int j=0; j<3; ++j) transform.getBasis()[i][j] = matrix(j,i); // NB column/row major difference compound->getChildShape(shapeIndex)->setLocalScaling(compound->getLocalScaling() * toBullet(scale)); compound->updateChildTransform(shapeIndex, transform); ++it; } collisionWorld->updateSingleAabb(mCollisionObject.get()); } private: std::auto_ptr mCollisionObject; osg::ref_ptr mShapeInstance; bool mSolid; }; // --------------------------------------------------------------- PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode) : mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager())) , mDebugDrawEnabled(false) , mTimeAccum(0.0f) , mWaterHeight(0) , mWaterEnabled(false) , mParentNode(parentNode) { mCollisionConfiguration = new btDefaultCollisionConfiguration(); mDispatcher = new btCollisionDispatcher(mCollisionConfiguration); mBroadphase = new btDbvtBroadphase(); mCollisionWorld = new btCollisionWorld(mDispatcher, mBroadphase, mCollisionConfiguration); // Don't update AABBs of all objects every frame. Most objects in MW are static, so we don't need this. // Should a "static" object ever be moved, we have to update its AABB manually using DynamicsWorld::updateSingleAabb. mCollisionWorld->setForceUpdateAllAabbs(false); } PhysicsSystem::~PhysicsSystem() { if (mWaterCollisionObject.get()) mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get()); for (HeightFieldMap::iterator it = mHeightFields.begin(); it != mHeightFields.end(); ++it) { mCollisionWorld->removeCollisionObject(it->second->getCollisionObject()); delete it->second; } for (ObjectMap::iterator it = mObjects.begin(); it != mObjects.end(); ++it) { mCollisionWorld->removeCollisionObject(it->second->getCollisionObject()); delete it->second; } for (ActorMap::iterator it = mActors.begin(); it != mActors.end(); ++it) { delete it->second; } delete mCollisionWorld; delete mCollisionConfiguration; delete mDispatcher; delete mBroadphase; } bool PhysicsSystem::toggleDebugRendering() { mDebugDrawEnabled = !mDebugDrawEnabled; if (mDebugDrawEnabled && !mDebugDrawer.get()) { mDebugDrawer.reset(new MWRender::DebugDrawer(mParentNode, mCollisionWorld)); mCollisionWorld->setDebugDrawer(mDebugDrawer.get()); mDebugDrawer->setDebugMode(mDebugDrawEnabled); } else if (mDebugDrawer.get()) mDebugDrawer->setDebugMode(mDebugDrawEnabled); return mDebugDrawEnabled; } void PhysicsSystem::markAsNonSolid(const MWWorld::ConstPtr &ptr) { ObjectMap::iterator found = mObjects.find(ptr); if (found == mObjects.end()) return; found->second->setSolid(false); } bool PhysicsSystem::isOnSolidGround (const MWWorld::Ptr& actor) const { const Actor* physactor = getActor(actor); if (!physactor || !physactor->getOnGround()) return false; CollisionMap::const_iterator found = mStandingCollisions.find(actor); if (found == mStandingCollisions.end()) return true; // assume standing on terrain (which is a non-object, so not collision tracked) ObjectMap::const_iterator foundObj = mObjects.find(found->second); if (foundObj == mObjects.end()) return false; if (!foundObj->second->isSolid()) return false; return true; } class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback { const btCollisionObject* mMe; // Store the real origin, since the shape's origin is its center btVector3 mOrigin; public: const btCollisionObject *mObject; btVector3 mContactPoint; btScalar mLeastDistSqr; DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const btVector3 &origin) : mMe(me), mOrigin(origin), mObject(NULL), mContactPoint(0,0,0), mLeastDistSqr(std::numeric_limits::max()) { } #if BT_BULLET_VERSION >= 281 virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) { const btCollisionObject* collisionObject = col1Wrap->m_collisionObject; #else virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* col0, int partId0, int index0, const btCollisionObject* col1, int partId1, int index1) { const btCollisionObject* collisionObject = col1; #endif if (collisionObject != mMe) { btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA()); if(!mObject || distsqr < mLeastDistSqr) { mObject = collisionObject; mLeastDistSqr = distsqr; mContactPoint = cp.getPositionWorldOnA(); } } return 0.f; } }; std::pair PhysicsSystem::getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orient, float queryDistance) { const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); btConeShape shape (osg::DegreesToRadians(store.find("fCombatAngleXY")->getFloat()/2.0f), queryDistance); shape.setLocalScaling(btVector3(1, 1, osg::DegreesToRadians(store.find("fCombatAngleZ")->getFloat()/2.0f) / shape.getRadius())); // The shape origin is its center, so we have to move it forward by half the length. The // real origin will be provided to getFilteredContact to find the closest. osg::Vec3f center = origin + (orient * osg::Vec3f(0.0f, queryDistance*0.5f, 0.0f)); btCollisionObject object; object.setCollisionShape(&shape); object.setWorldTransform(btTransform(toBullet(orient), toBullet(center))); const btCollisionObject* me = NULL; const Actor* physactor = getActor(actor); if (physactor) me = physactor->getCollisionObject(); DeepestNotMeContactTestResultCallback resultCallback(me, toBullet(origin)); resultCallback.m_collisionFilterGroup = CollisionType_Actor; resultCallback.m_collisionFilterMask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor; mCollisionWorld->contactTest(&object, resultCallback); if (resultCallback.mObject) { PtrHolder* holder = static_cast(resultCallback.mObject->getUserPointer()); if (holder) return std::make_pair(holder->getPtr(), toOsg(resultCallback.mContactPoint)); } return std::make_pair(MWWorld::Ptr(), osg::Vec3f()); } float PhysicsSystem::getHitDistance(const osg::Vec3f &point, const MWWorld::ConstPtr &target) const { btCollisionObject* targetCollisionObj = NULL; const Actor* actor = getActor(target); if (actor) targetCollisionObj = actor->getCollisionObject(); if (!targetCollisionObj) return 0.f; btTransform rayFrom; rayFrom.setIdentity(); rayFrom.setOrigin(toBullet(point)); // target the collision object's world origin, this should be the center of the collision object btTransform rayTo; rayTo.setIdentity(); rayTo.setOrigin(targetCollisionObj->getWorldTransform().getOrigin()); btCollisionWorld::ClosestRayResultCallback cb(rayFrom.getOrigin(), rayTo.getOrigin()); btCollisionWorld::rayTestSingle(rayFrom, rayTo, targetCollisionObj, targetCollisionObj->getCollisionShape(), targetCollisionObj->getWorldTransform(), cb); if (!cb.hasHit()) { // didn't hit the target. this could happen if point is already inside the collision box return 0.f; } else return (point - toOsg(cb.m_hitPointWorld)).length(); } class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { public: ClosestNotMeRayResultCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to) : btCollisionWorld::ClosestRayResultCallback(from, to) , mMe(me) { } virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) { if (rayResult.m_collisionObject == mMe) return 1.f; return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); } private: const btCollisionObject* mMe; }; PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore, int mask, int group) const { btVector3 btFrom = toBullet(from); btVector3 btTo = toBullet(to); const btCollisionObject* me = NULL; if (!ignore.isEmpty()) { const Actor* actor = getActor(ignore); if (actor) me = actor->getCollisionObject(); } ClosestNotMeRayResultCallback resultCallback(me, btFrom, btTo); resultCallback.m_collisionFilterGroup = group; resultCallback.m_collisionFilterMask = mask; mCollisionWorld->rayTest(btFrom, btTo, resultCallback); RayResult result; result.mHit = resultCallback.hasHit(); if (resultCallback.hasHit()) { result.mHitPos = toOsg(resultCallback.m_hitPointWorld); result.mHitNormal = toOsg(resultCallback.m_hitNormalWorld); if (PtrHolder* ptrHolder = static_cast(resultCallback.m_collisionObject->getUserPointer())) result.mHitObject = ptrHolder->getPtr(); } return result; } PhysicsSystem::RayResult PhysicsSystem::castSphere(const osg::Vec3f &from, const osg::Vec3f &to, float radius) { btCollisionWorld::ClosestConvexResultCallback callback(toBullet(from), toBullet(to)); callback.m_collisionFilterGroup = 0xff; callback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door; btSphereShape shape(radius); const btQuaternion btrot = btQuaternion::getIdentity(); btTransform from_ (btrot, toBullet(from)); btTransform to_ (btrot, toBullet(to)); mCollisionWorld->convexSweepTest(&shape, from_, to_, callback); RayResult result; result.mHit = callback.hasHit(); if (result.mHit) { result.mHitPos = toOsg(callback.m_hitPointWorld); result.mHitNormal = toOsg(callback.m_hitNormalWorld); } return result; } bool PhysicsSystem::getLineOfSight(const MWWorld::ConstPtr &actor1, const MWWorld::ConstPtr &actor2) const { const Actor* physactor1 = getActor(actor1); const Actor* physactor2 = getActor(actor2); if (!physactor1 || !physactor2) return false; osg::Vec3f pos1 (physactor1->getPosition() + osg::Vec3f(0,0,physactor1->getHalfExtents().z() * 0.8)); // eye level osg::Vec3f pos2 (physactor2->getPosition() + osg::Vec3f(0,0,physactor2->getHalfExtents().z() * 0.8)); RayResult result = castRay(pos1, pos2, MWWorld::Ptr(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door); return !result.mHit; } // physactor->getOnGround() is not a reliable indicator of whether the actor // is on the ground (defaults to false, which means code blocks such as // CharacterController::update() may falsely detect "falling"). // // Also, collisions can move z position slightly off zero, giving a false // indication. In order to reduce false detection of jumping, small distance // below the actor is detected and ignored. A value of 1.5 is used here, but // something larger may be more suitable. This change should resolve Bug#1271. // // TODO: There might be better places to update PhysicActor::mOnGround. bool PhysicsSystem::isOnGround(const MWWorld::Ptr &actor) { Actor* physactor = getActor(actor); if(!physactor) return false; if(physactor->getOnGround()) return true; else { osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); ActorTracer tracer; // a small distance above collision object is considered "on ground" tracer.findGround(physactor, pos, pos - osg::Vec3f(0, 0, 1.5f), // trace a small amount down mCollisionWorld); if(tracer.mFraction < 1.0f) // collision, must be close to something below { physactor->setOnGround(true); return true; } else return false; } } osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); if (physactor) return physactor->getHalfExtents(); else return osg::Vec3f(); } osg::Vec3f PhysicsSystem::getRenderingHalfExtents(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); if (physactor) return physactor->getRenderingHalfExtents(); else return osg::Vec3f(); } osg::Vec3f PhysicsSystem::getPosition(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); if (physactor) return physactor->getPosition(); else return osg::Vec3f(); } class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback { public: ContactTestResultCallback(const btCollisionObject* testedAgainst) : mTestedAgainst(testedAgainst) { } const btCollisionObject* mTestedAgainst; std::vector mResult; #if BT_BULLET_VERSION >= 281 virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) { const btCollisionObject* collisionObject = col0Wrap->m_collisionObject; if (collisionObject == mTestedAgainst) collisionObject = col1Wrap->m_collisionObject; #else virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* col0, int partId0, int index0, const btCollisionObject* col1, int partId1, int index1) { const btCollisionObject* collisionObject = col0; if (collisionObject == mTestedAgainst) collisionObject = col1; #endif PtrHolder* holder = static_cast(collisionObject->getUserPointer()); if (holder) mResult.push_back(holder->getPtr()); return 0.f; } }; std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const { btCollisionObject* me = NULL; ObjectMap::const_iterator found = mObjects.find(ptr); if (found != mObjects.end()) me = found->second->getCollisionObject(); else return std::vector(); ContactTestResultCallback resultCallback (me); resultCallback.m_collisionFilterGroup = collisionGroup; resultCallback.m_collisionFilterMask = collisionMask; mCollisionWorld->contactTest(me, resultCallback); return resultCallback.mResult; } osg::Vec3f PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, float maxHeight) { ActorMap::iterator found = mActors.find(ptr); if (found == mActors.end()) return ptr.getRefData().getPosition().asVec3(); else return MovementSolver::traceDown(ptr, found->second, mCollisionWorld, maxHeight); } void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts) { HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts); mHeightFields[std::make_pair(x,y)] = heightfield; mCollisionWorld->addCollisionObject(heightfield->getCollisionObject(), CollisionType_HeightMap, CollisionType_Actor|CollisionType_Projectile); } void PhysicsSystem::removeHeightField (int x, int y) { HeightFieldMap::iterator heightfield = mHeightFields.find(std::make_pair(x,y)); if(heightfield != mHeightFields.end()) { mCollisionWorld->removeCollisionObject(heightfield->second->getCollisionObject()); delete heightfield->second; mHeightFields.erase(heightfield); } } void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType) { osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); if (!shapeInstance || !shapeInstance->getCollisionShape()) return; Object *obj = new Object(ptr, shapeInstance); mObjects.insert(std::make_pair(ptr, obj)); if (obj->isAnimated()) mAnimatedObjects.insert(obj); mCollisionWorld->addCollisionObject(obj->getCollisionObject(), collisionType, CollisionType_Actor|CollisionType_HeightMap|CollisionType_Projectile); } void PhysicsSystem::remove(const MWWorld::Ptr &ptr) { ObjectMap::iterator found = mObjects.find(ptr); if (found != mObjects.end()) { mCollisionWorld->removeCollisionObject(found->second->getCollisionObject()); mAnimatedObjects.erase(found->second); delete found->second; mObjects.erase(found); } ActorMap::iterator foundActor = mActors.find(ptr); if (foundActor != mActors.end()) { delete foundActor->second; mActors.erase(foundActor); } } void PhysicsSystem::updateCollisionMapPtr(CollisionMap& map, const MWWorld::Ptr &old, const MWWorld::Ptr &updated) { CollisionMap::iterator found = map.find(old); if (found != map.end()) { map[updated] = found->second; map.erase(found); } for (CollisionMap::iterator it = map.begin(); it != map.end(); ++it) { if (it->second == old) it->second = updated; } } void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) { ObjectMap::iterator found = mObjects.find(old); if (found != mObjects.end()) { Object* obj = found->second; obj->updatePtr(updated); mObjects.erase(found); mObjects.insert(std::make_pair(updated, obj)); } ActorMap::iterator foundActor = mActors.find(old); if (foundActor != mActors.end()) { Actor* actor = foundActor->second; actor->updatePtr(updated); mActors.erase(foundActor); mActors.insert(std::make_pair(updated, actor)); } updateCollisionMapPtr(mStandingCollisions, old, updated); } Actor *PhysicsSystem::getActor(const MWWorld::Ptr &ptr) { ActorMap::iterator found = mActors.find(ptr); if (found != mActors.end()) return found->second; return NULL; } const Actor *PhysicsSystem::getActor(const MWWorld::ConstPtr &ptr) const { ActorMap::const_iterator found = mActors.find(ptr); if (found != mActors.end()) return found->second; return NULL; } void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr) { ObjectMap::iterator found = mObjects.find(ptr); if (found != mObjects.end()) { float scale = ptr.getCellRef().getScale(); found->second->setScale(scale); mCollisionWorld->updateSingleAabb(found->second->getCollisionObject()); return; } ActorMap::iterator foundActor = mActors.find(ptr); if (foundActor != mActors.end()) { foundActor->second->updateScale(); mCollisionWorld->updateSingleAabb(foundActor->second->getCollisionObject()); return; } } void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr) { ObjectMap::iterator found = mObjects.find(ptr); if (found != mObjects.end()) { found->second->setRotation(toBullet(ptr.getRefData().getBaseNode()->getAttitude())); mCollisionWorld->updateSingleAabb(found->second->getCollisionObject()); return; } ActorMap::iterator foundActor = mActors.find(ptr); if (foundActor != mActors.end()) { foundActor->second->updateRotation(); mCollisionWorld->updateSingleAabb(foundActor->second->getCollisionObject()); return; } } void PhysicsSystem::updatePosition(const MWWorld::Ptr &ptr) { ObjectMap::iterator found = mObjects.find(ptr); if (found != mObjects.end()) { found->second->setOrigin(toBullet(ptr.getRefData().getPosition().asVec3())); mCollisionWorld->updateSingleAabb(found->second->getCollisionObject()); return; } ActorMap::iterator foundActor = mActors.find(ptr); if (foundActor != mActors.end()) { foundActor->second->updatePosition(); mCollisionWorld->updateSingleAabb(foundActor->second->getCollisionObject()); return; } } void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) { osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); if (!shapeInstance) return; Actor* actor = new Actor(ptr, shapeInstance, mCollisionWorld); mActors.insert(std::make_pair(ptr, actor)); } bool PhysicsSystem::toggleCollisionMode() { ActorMap::iterator found = mActors.find(MWMechanics::getPlayer()); if (found != mActors.end()) { bool cmode = found->second->getCollisionMode(); cmode = !cmode; found->second->enableCollisionMode(cmode); return cmode; } return false; } void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &movement) { PtrVelocityList::iterator iter = mMovementQueue.begin(); for(;iter != mMovementQueue.end();++iter) { if(iter->first == ptr) { iter->second = movement; return; } } mMovementQueue.push_back(std::make_pair(ptr, movement)); } void PhysicsSystem::clearQueuedMovement() { mMovementQueue.clear(); mStandingCollisions.clear(); } const PtrVelocityList& PhysicsSystem::applyQueuedMovement(float dt) { mMovementResults.clear(); mTimeAccum += dt; if(mTimeAccum >= 1.0f/60.0f) { // Collision events should be available on every frame mStandingCollisions.clear(); const MWBase::World *world = MWBase::Environment::get().getWorld(); PtrVelocityList::iterator iter = mMovementQueue.begin(); for(;iter != mMovementQueue.end();++iter) { float waterlevel = -std::numeric_limits::max(); const MWWorld::CellStore *cell = iter->first.getCell(); if(cell->getCell()->hasWater()) waterlevel = cell->getWaterLevel(); float oldHeight = iter->first.getRefData().getPosition().pos[2]; const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects(); bool waterCollision = false; if (effects.get(ESM::MagicEffect::WaterWalking).getMagnitude() && cell->getCell()->hasWater() && !world->isUnderwater(iter->first.getCell(), osg::Vec3f(iter->first.getRefData().getPosition().asVec3()))) waterCollision = true; ActorMap::iterator foundActor = mActors.find(iter->first); if (foundActor == mActors.end()) // actor was already removed from the scene continue; Actor* physicActor = foundActor->second; physicActor->setCanWaterWalk(waterCollision); // Slow fall reduces fall speed by a factor of (effect magnitude / 200) float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f)); osg::Vec3f newpos = MovementSolver::move(physicActor->getPtr(), physicActor, iter->second, mTimeAccum, world->isFlying(iter->first), waterlevel, slowFall, mCollisionWorld, mStandingCollisions); float heightDiff = newpos.z() - oldHeight; if (heightDiff < 0) iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff); mMovementResults.push_back(std::make_pair(iter->first, newpos)); } mTimeAccum = 0.0f; } mMovementQueue.clear(); return mMovementResults; } void PhysicsSystem::stepSimulation(float dt) { for (std::set::iterator it = mAnimatedObjects.begin(); it != mAnimatedObjects.end(); ++it) (*it)->animateCollisionShapes(mCollisionWorld); CProfileManager::Reset(); CProfileManager::Increment_Frame_Counter(); } void PhysicsSystem::debugDraw() { if (mDebugDrawer.get()) mDebugDrawer->step(); } bool PhysicsSystem::isActorStandingOn(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const { for (CollisionMap::const_iterator it = mStandingCollisions.begin(); it != mStandingCollisions.end(); ++it) { if (it->first == actor && it->second == object) return true; } return false; } void PhysicsSystem::getActorsStandingOn(const MWWorld::ConstPtr &object, std::vector &out) const { for (CollisionMap::const_iterator it = mStandingCollisions.begin(); it != mStandingCollisions.end(); ++it) { if (it->second == object) out.push_back(it->first); } } bool PhysicsSystem::isActorCollidingWith(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const { std::vector collisions = getCollisions(object, CollisionType_World, CollisionType_Actor); return (std::find(collisions.begin(), collisions.end(), actor) != collisions.end()); } void PhysicsSystem::getActorsCollidingWith(const MWWorld::ConstPtr &object, std::vector &out) const { std::vector collisions = getCollisions(object, CollisionType_World, CollisionType_Actor); out.insert(out.end(), collisions.begin(), collisions.end()); } void PhysicsSystem::disableWater() { if (mWaterEnabled) { mWaterEnabled = false; updateWater(); } } void PhysicsSystem::enableWater(float height) { if (!mWaterEnabled || mWaterHeight != height) { mWaterEnabled = true; mWaterHeight = height; updateWater(); } } void PhysicsSystem::setWaterHeight(float height) { if (mWaterHeight != height) { mWaterHeight = height; updateWater(); } } void PhysicsSystem::updateWater() { if (mWaterCollisionObject.get()) { mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get()); } if (!mWaterEnabled) return; mWaterCollisionObject.reset(new btCollisionObject()); mWaterCollisionShape.reset(new btStaticPlaneShape(btVector3(0,0,1), mWaterHeight)); mWaterCollisionObject->setCollisionShape(mWaterCollisionShape.get()); mCollisionWorld->addCollisionObject(mWaterCollisionObject.get(), CollisionType_Water, CollisionType_Actor); } } openmw-openmw-0.38.0/apps/openmw/mwphysics/physicssystem.hpp000066400000000000000000000204571264522266000243650ustar00rootroot00000000000000#ifndef OPENMW_MWPHYSICS_PHYSICSSYSTEM_H #define OPENMW_MWPHYSICS_PHYSICSSYSTEM_H #include #include #include #include #include #include "../mwworld/ptr.hpp" #include "collisiontype.hpp" namespace osg { class Group; } namespace MWRender { class DebugDrawer; } namespace Resource { class BulletShapeManager; } namespace Resource { class ResourceSystem; } class btCollisionWorld; class btBroadphaseInterface; class btDefaultCollisionConfiguration; class btCollisionDispatcher; class btCollisionObject; class btCollisionShape; namespace MWPhysics { typedef std::vector > PtrVelocityList; class HeightField; class Object; class Actor; class PhysicsSystem { public: PhysicsSystem (Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode); ~PhysicsSystem (); void enableWater(float height); void setWaterHeight(float height); void disableWater(); void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World); void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated); Actor* getActor(const MWWorld::Ptr& ptr); const Actor* getActor(const MWWorld::ConstPtr& ptr) const; // Object or Actor void remove (const MWWorld::Ptr& ptr); void updateScale (const MWWorld::Ptr& ptr); void updateRotation (const MWWorld::Ptr& ptr); void updatePosition (const MWWorld::Ptr& ptr); void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts); void removeHeightField (int x, int y); bool toggleCollisionMode(); void stepSimulation(float dt); void debugDraw(); std::vector getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with osg::Vec3f traceDown(const MWWorld::Ptr &ptr, float maxHeight); std::pair getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orientation, float queryDistance); /// Get distance from \a point to the collision shape of \a target. Uses a raycast to find where the /// target vector hits the collision shape and then calculates distance from the intersection point. /// This can be used to find out how much nearer we need to move to the target for a "getHitContact" to be successful. /// \note Only Actor targets are supported at the moment. float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const; struct RayResult { bool mHit; osg::Vec3f mHitPos; osg::Vec3f mHitNormal; MWWorld::Ptr mHitObject; }; /// @param me Optional, a Ptr to ignore in the list of results RayResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore = MWWorld::ConstPtr(), int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const; RayResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius); /// Return true if actor1 can see actor2. bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const; bool isOnGround (const MWWorld::Ptr& actor); /// Get physical half extents (scaled) of the given actor. osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor) const; /// @see MWPhysics::Actor::getRenderingHalfExtents osg::Vec3f getRenderingHalfExtents(const MWWorld::ConstPtr& actor) const; /// Get the position of the collision shape for the actor. Use together with getHalfExtents() to get the collision bounds in world space. /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. osg::Vec3f getPosition(const MWWorld::ConstPtr& actor) const; /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to applyQueuedMovement. void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); /// Apply all queued movements, then clear the list. const PtrVelocityList& applyQueuedMovement(float dt); /// Clear the queued movements list without applying. void clearQueuedMovement(); /// Return true if \a actor has been standing on \a object in this frame /// This will trigger whenever the object is directly below the actor. /// It doesn't matter if the actor is stationary or moving. bool isActorStandingOn(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const; /// Get the handle of all actors standing on \a object in this frame. void getActorsStandingOn(const MWWorld::ConstPtr& object, std::vector& out) const; /// Return true if \a actor has collided with \a object in this frame. /// This will detect running into objects, but will not detect climbing stairs, stepping up a small object, etc. bool isActorCollidingWith(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const; /// Get the handle of all actors colliding with \a object in this frame. void getActorsCollidingWith(const MWWorld::ConstPtr& object, std::vector& out) const; bool toggleDebugRendering(); /// Mark the given object as a 'non-solid' object. A non-solid object means that /// \a isOnSolidGround will return false for actors standing on that object. void markAsNonSolid (const MWWorld::ConstPtr& ptr); bool isOnSolidGround (const MWWorld::Ptr& actor) const; private: void updateWater(); btBroadphaseInterface* mBroadphase; btDefaultCollisionConfiguration* mCollisionConfiguration; btCollisionDispatcher* mDispatcher; btCollisionWorld* mCollisionWorld; std::auto_ptr mShapeManager; typedef std::map ObjectMap; ObjectMap mObjects; std::set mAnimatedObjects; // stores pointers to elements in mObjects typedef std::map ActorMap; ActorMap mActors; typedef std::map, HeightField*> HeightFieldMap; HeightFieldMap mHeightFields; bool mDebugDrawEnabled; // Tracks standing collisions happening during a single frame. // This will detect standing on an object, but won't detect running e.g. against a wall. typedef std::map CollisionMap; CollisionMap mStandingCollisions; // replaces all occurences of 'old' in the map by 'updated', no matter if its a key or value void updateCollisionMapPtr(CollisionMap& map, const MWWorld::Ptr &old, const MWWorld::Ptr &updated); PtrVelocityList mMovementQueue; PtrVelocityList mMovementResults; float mTimeAccum; float mWaterHeight; float mWaterEnabled; std::auto_ptr mWaterCollisionObject; std::auto_ptr mWaterCollisionShape; std::auto_ptr mDebugDrawer; osg::ref_ptr mParentNode; PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwphysics/trace.cpp000066400000000000000000000111371264522266000225220ustar00rootroot00000000000000#include "trace.h" #include #include #include #include #include "collisiontype.hpp" #include "actor.hpp" #include "convert.hpp" namespace MWPhysics { class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: ClosestNotMeConvexResultCallback(btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), mMe(me), mUp(up), mMinSlopeDot(minSlopeDot) { } virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) { if(convexResult.m_hitCollisionObject == mMe) return btScalar( 1 ); btVector3 hitNormalWorld; if(normalInWorldSpace) hitNormalWorld = convexResult.m_hitNormalLocal; else { ///need to transform normal into worldspace hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; } btScalar dotUp = mUp.dot(hitNormalWorld); if(dotUp < mMinSlopeDot) return btScalar(1); return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); } protected: btCollisionObject *mMe; const btVector3 mUp; const btScalar mMinSlopeDot; }; void ActorTracer::doTrace(btCollisionObject *actor, const osg::Vec3f& start, const osg::Vec3f& end, btCollisionWorld* world) { const btVector3 btstart = toBullet(start); const btVector3 btend = toBullet(end); const btTransform &trans = actor->getWorldTransform(); btTransform from(trans); btTransform to(trans); from.setOrigin(btstart); to.setOrigin(btend); ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); // Inherit the actor's collision group and mask newTraceCallback.m_collisionFilterGroup = actor->getBroadphaseHandle()->m_collisionFilterGroup; newTraceCallback.m_collisionFilterMask = actor->getBroadphaseHandle()->m_collisionFilterMask; btCollisionShape *shape = actor->getCollisionShape(); assert(shape->isConvex()); world->convexSweepTest(static_cast(shape), from, to, newTraceCallback); // Copy the hit data over to our trace results struct: if(newTraceCallback.hasHit()) { const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; mFraction = newTraceCallback.m_closestHitFraction; mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); mEndPos = (end-start)*mFraction + start; mHitObject = newTraceCallback.m_hitCollisionObject; } else { mEndPos = end; mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f); mFraction = 1.0f; mHitObject = NULL; } } void ActorTracer::findGround(const Actor* actor, const osg::Vec3f& start, const osg::Vec3f& end, btCollisionWorld* world) { const btVector3 btstart(start.x(), start.y(), start.z()+1.0f); const btVector3 btend(end.x(), end.y(), end.z()+1.0f); const btTransform &trans = actor->getCollisionObject()->getWorldTransform(); btTransform from(trans.getBasis(), btstart); btTransform to(trans.getBasis(), btend); ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionObject(), btstart-btend, btScalar(0.0)); // Inherit the actor's collision group and mask newTraceCallback.m_collisionFilterGroup = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterGroup; newTraceCallback.m_collisionFilterMask = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterMask; newTraceCallback.m_collisionFilterMask &= ~CollisionType_Actor; btVector3 halfExtents = toBullet(actor->getHalfExtents()); halfExtents[2] = 1.0f; btCylinderShapeZ base(halfExtents); world->convexSweepTest(&base, from, to, newTraceCallback); if(newTraceCallback.hasHit()) { const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; mFraction = newTraceCallback.m_closestHitFraction; mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); mEndPos = (end-start)*mFraction + start; mEndPos[2] += 1.0f; } else { mEndPos = end; mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f); mFraction = 1.0f; } } } openmw-openmw-0.38.0/apps/openmw/mwphysics/trace.h000066400000000000000000000011221264522266000221600ustar00rootroot00000000000000#ifndef OENGINE_BULLET_TRACE_H #define OENGINE_BULLET_TRACE_H #include class btCollisionObject; class btCollisionWorld; namespace MWPhysics { class Actor; struct ActorTracer { osg::Vec3f mEndPos; osg::Vec3f mPlaneNormal; const btCollisionObject* mHitObject; float mFraction; void doTrace(btCollisionObject *actor, const osg::Vec3f& start, const osg::Vec3f& end, btCollisionWorld* world); void findGround(const Actor* actor, const osg::Vec3f& start, const osg::Vec3f& end, btCollisionWorld* world); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/000077500000000000000000000000001264522266000205125ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwrender/.gitignore000066400000000000000000000000041264522266000224740ustar00rootroot00000000000000old openmw-openmw-0.38.0/apps/openmw/mwrender/animation.cpp000066400000000000000000001447501264522266000232100ustar00rootroot00000000000000#include "animation.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KeyframeHolder #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" #include "../mwworld/cellstore.hpp" #include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority #include "vismask.hpp" #include "util.hpp" #include "rotatecontroller.hpp" namespace { class GlowUpdater : public SceneUtil::StateSetUpdater { public: GlowUpdater(osg::Vec4f color, const std::vector >& textures) : mTexUnit(1) // FIXME: might not always be 1 , mColor(color) , mTextures(textures) { } virtual void setDefaults(osg::StateSet *stateset) { stateset->setTextureMode(mTexUnit, GL_TEXTURE_2D, osg::StateAttribute::ON); osg::TexGen* texGen = new osg::TexGen; texGen->setMode(osg::TexGen::SPHERE_MAP); stateset->setTextureAttributeAndModes(mTexUnit, texGen, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); osg::TexEnvCombine* texEnv = new osg::TexEnvCombine; texEnv->setSource0_RGB(osg::TexEnvCombine::CONSTANT); texEnv->setConstantColor(mColor); texEnv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE); texEnv->setSource2_RGB(osg::TexEnvCombine::TEXTURE); texEnv->setOperand2_RGB(osg::TexEnvCombine::SRC_COLOR); stateset->setTextureAttributeAndModes(mTexUnit, texEnv, osg::StateAttribute::ON); } virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) { float time = nv->getFrameStamp()->getSimulationTime(); int index = (int)(time*16) % mTextures.size(); stateset->setTextureAttribute(mTexUnit, mTextures[index], osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } private: int mTexUnit; osg::Vec4f mColor; std::vector > mTextures; }; class NodeMapVisitor : public osg::NodeVisitor { public: NodeMapVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} void apply(osg::MatrixTransform& trans) { mMap[Misc::StringUtils::lowerCase(trans.getName())] = &trans; traverse(trans); } typedef std::map > NodeMap; const NodeMap& getNodeMap() const { return mMap; } private: NodeMap mMap; }; NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) { NifOsg::TextKeyMap::const_iterator iter(keys.begin()); for(;iter != keys.end();++iter) { if(iter->second.compare(0, groupname.size(), groupname) == 0 && iter->second.compare(groupname.size(), 2, ": ") == 0) break; } return iter; } float calcAnimVelocity(const std::multimap& keys, NifOsg::KeyframeController *nonaccumctrl, const osg::Vec3f& accum, const std::string &groupname) { const std::string start = groupname+": start"; const std::string loopstart = groupname+": loop start"; const std::string loopstop = groupname+": loop stop"; const std::string stop = groupname+": stop"; float starttime = std::numeric_limits::max(); float stoptime = 0.0f; // Pick the last Loop Stop key and the last Loop Start key. // This is required because of broken text keys in AshVampire.nif. // It has *two* WalkForward: Loop Stop keys at different times, the first one is used for stopping playback // but the animation velocity calculation uses the second one. // As result the animation velocity calculation is not correct, and this incorrect velocity must be replicated, // because otherwise the Creature's Speed (dagoth uthol) would not be sufficient to move fast enough. NifOsg::TextKeyMap::const_reverse_iterator keyiter(keys.rbegin()); while(keyiter != keys.rend()) { if(keyiter->second == start || keyiter->second == loopstart) { starttime = keyiter->first; break; } ++keyiter; } keyiter = keys.rbegin(); while(keyiter != keys.rend()) { if (keyiter->second == stop) stoptime = keyiter->first; else if (keyiter->second == loopstop) { stoptime = keyiter->first; break; } ++keyiter; } if(stoptime > starttime) { osg::Vec3f startpos = osg::componentMultiply(nonaccumctrl->getTranslation(starttime), accum); osg::Vec3f endpos = osg::componentMultiply(nonaccumctrl->getTranslation(stoptime), accum); return (startpos-endpos).length() / (stoptime - starttime); } return 0.0f; } /// @brief Base class for visitors that remove nodes from a scene graph. /// Subclasses need to fill the mToRemove vector. /// To use, node->accept(removeVisitor); removeVisitor.remove(); class RemoveVisitor : public osg::NodeVisitor { public: RemoveVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { } void remove() { for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) it->second->removeChild(it->first); } protected: // typedef std::vector > RemoveVec; std::vector > mToRemove; }; // Removes all drawables from a graph. class RemoveDrawableVisitor : public RemoveVisitor { public: virtual void apply(osg::Geode &geode) { applyImpl(geode); } #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) virtual void apply(osg::Drawable& drw) { applyImpl(drw); } #endif void applyImpl(osg::Node& node) { osg::NodePath::iterator parent = getNodePath().end()-2; // We know that the parent is a Group because only Groups can have children. osg::Group* parentGroup = static_cast(*parent); // Try to prune nodes that would be empty after the removal if (parent != getNodePath().begin()) { // This could be extended to remove the parent's parent, and so on if they are empty as well. // But for NIF files, there won't be a benefit since only TriShapes can be set to STATIC dataVariance. osg::Group* parentParent = static_cast(*(parent - 1)); if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC) { mToRemove.push_back(std::make_pair(parentGroup, parentParent)); return; } } mToRemove.push_back(std::make_pair(&node, parentGroup)); } }; class RemoveTriBipVisitor : public RemoveVisitor { public: virtual void apply(osg::Geode &node) { applyImpl(node); } #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) virtual void apply(osg::Drawable& drw) { applyImpl(drw); } #endif void applyImpl(osg::Node& node) { const std::string toFind = "tri bip"; if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0) { osg::Group* parent = static_cast(*(getNodePath().end()-2)); // Not safe to remove in apply(), since the visitor is still iterating the child list mToRemove.push_back(std::make_pair(&node, parent)); } } }; } namespace MWRender { struct Animation::AnimSource { osg::ref_ptr mKeyframes; typedef std::map > ControllerMap; ControllerMap mControllerMap[Animation::sNumBlendMasks]; const std::multimap& getTextKeys(); }; class ResetAccumRootCallback : public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::MatrixTransform* transform = static_cast(node); osg::Matrix mat = transform->getMatrix(); osg::Vec3f position = mat.getTrans(); position = osg::componentMultiply(mResetAxes, position); mat.setTrans(position); transform->setMatrix(mat); traverse(node, nv); } void setAccumulate(const osg::Vec3f& accumulate) { // anything that accumulates (1.f) should be reset in the callback to (0.f) mResetAxes.x() = accumulate.x() != 0.f ? 0.f : 1.f; mResetAxes.y() = accumulate.y() != 0.f ? 0.f : 1.f; mResetAxes.z() = accumulate.z() != 0.f ? 0.f : 1.f; } private: osg::Vec3f mResetAxes; }; Animation::Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem) : mInsert(parentNode) , mSkeleton(NULL) , mPtr(ptr) , mResourceSystem(resourceSystem) , mAccumulate(1.f, 1.f, 0.f) , mTextKeyListener(NULL) , mHeadYawRadians(0.f) , mHeadPitchRadians(0.f) , mAlpha(1.f) { for(size_t i = 0;i < sNumBlendMasks;i++) mAnimationTimePtr[i].reset(new AnimationTime); } Animation::~Animation() { setLightEffect(0.f); if (mObjectRoot) mInsert->removeChild(mObjectRoot); } MWWorld::Ptr Animation::getPtr() { return mPtr; } void Animation::setActive(bool active) { if (mSkeleton) mSkeleton->setActive(active); } void Animation::updatePtr(const MWWorld::Ptr &ptr) { mPtr = ptr; } void Animation::setAccumulation(const osg::Vec3f& accum) { mAccumulate = accum; if (mResetAccumRootCallback) mResetAccumRootCallback->setAccumulate(mAccumulate); } size_t Animation::detectBlendMask(osg::Node* node) { static const char sBlendMaskRoots[sNumBlendMasks][32] = { "", /* Lower body / character root */ "Bip01 Spine1", /* Torso */ "Bip01 L Clavicle", /* Left arm */ "Bip01 R Clavicle", /* Right arm */ }; while(node != mObjectRoot) { const std::string &name = node->getName(); for(size_t i = 1;i < sNumBlendMasks;i++) { if(name == sBlendMaskRoots[i]) return i; } assert(node->getNumParents() > 0); node = node->getParent(0); } return 0; } const std::multimap &Animation::AnimSource::getTextKeys() { return mKeyframes->mTextKeys; } void Animation::addAnimSource(const std::string &model) { std::string kfname = model; Misc::StringUtils::lowerCaseInPlace(kfname); if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) kfname.replace(kfname.size()-4, 4, ".kf"); else return; if(!mResourceSystem->getVFS()->exists(kfname)) return; boost::shared_ptr animsrc; animsrc.reset(new AnimSource); animsrc->mKeyframes = mResourceSystem->getKeyframeManager()->get(kfname); if (!animsrc->mKeyframes || animsrc->mKeyframes->mTextKeys.empty() || animsrc->mKeyframes->mKeyframeControllers.empty()) return; for (NifOsg::KeyframeHolder::KeyframeControllerMap::const_iterator it = animsrc->mKeyframes->mKeyframeControllers.begin(); it != animsrc->mKeyframes->mKeyframeControllers.end(); ++it) { std::string bonename = Misc::StringUtils::lowerCase(it->first); NodeMap::const_iterator found = mNodeMap.find(bonename); if (found == mNodeMap.end()) { std::cerr << "addAnimSource: can't find bone '" + bonename << "' in " << model << " (referenced by " << kfname << ")" << std::endl; continue; } osg::Node* node = found->second; size_t blendMask = detectBlendMask(node); // clone the controller, because each Animation needs its own ControllerSource osg::ref_ptr cloned = osg::clone(it->second.get(), osg::CopyOp::DEEP_COPY_ALL); cloned->setSource(mAnimationTimePtr[blendMask]); animsrc->mControllerMap[blendMask].insert(std::make_pair(bonename, cloned)); } mAnimSources.push_back(animsrc); SceneUtil::AssignControllerSourcesVisitor assignVisitor(mAnimationTimePtr[0]); mObjectRoot->accept(assignVisitor); if (!mAccumRoot) { NodeMap::const_iterator found = mNodeMap.find("root bone"); if (found == mNodeMap.end()) found = mNodeMap.find("bip01"); if (found != mNodeMap.end()) mAccumRoot = found->second; } } void Animation::clearAnimSources() { mStates.clear(); for(size_t i = 0;i < sNumBlendMasks;i++) mAnimationTimePtr[i]->setTimePtr(boost::shared_ptr()); mAccumCtrl = NULL; mAnimSources.clear(); } bool Animation::hasAnimation(const std::string &anim) { AnimSourceList::const_iterator iter(mAnimSources.begin()); for(;iter != mAnimSources.end();++iter) { const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); if(findGroupStart(keys, anim) != keys.end()) return true; } return false; } float Animation::getStartTime(const std::string &groupname) const { for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) { const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); NifOsg::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); if(found != keys.end()) return found->first; } return -1.f; } float Animation::getTextKeyTime(const std::string &textKey) const { for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) { const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); for(NifOsg::TextKeyMap::const_iterator iterKey(keys.begin()); iterKey != keys.end(); ++iterKey) { if(iterKey->second.compare(0, textKey.size(), textKey) == 0) return iterKey->first; } } return -1.f; } void Animation::handleTextKey(AnimState &state, const std::string &groupname, const std::multimap::const_iterator &key, const std::multimap& map) { const std::string &evt = key->second; size_t off = groupname.size()+2; size_t len = evt.size() - off; if(evt.compare(0, groupname.size(), groupname) == 0 && evt.compare(groupname.size(), 2, ": ") == 0) { if(evt.compare(off, len, "loop start") == 0) state.mLoopStartTime = key->first; else if(evt.compare(off, len, "loop stop") == 0) state.mLoopStopTime = key->first; } if (mTextKeyListener) { try { mTextKeyListener->handleTextKey(groupname, key, map); } catch (std::exception& e) { std::cerr << "Error handling text key " << evt << ": " << e.what() << std::endl; } } } void Animation::play(const std::string &groupname, const AnimPriority& priority, int blendMask, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops, bool loopfallback) { if(!mObjectRoot || mAnimSources.empty()) return; if(groupname.empty()) { resetActiveGroups(); return; } AnimStateMap::iterator stateiter = mStates.begin(); while(stateiter != mStates.end()) { if(stateiter->second.mPriority == priority) mStates.erase(stateiter++); else ++stateiter; } stateiter = mStates.find(groupname); if(stateiter != mStates.end()) { stateiter->second.mPriority = priority; resetActiveGroups(); return; } /* Look in reverse; last-inserted source has priority. */ AnimState state; AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); for(;iter != mAnimSources.rend();++iter) { const NifOsg::TextKeyMap &textkeys = (*iter)->getTextKeys(); if(reset(state, textkeys, groupname, start, stop, startpoint, loopfallback)) { state.mSource = *iter; state.mSpeedMult = speedmult; state.mLoopCount = loops; state.mPlaying = (state.getTime() < state.mStopTime); state.mPriority = priority; state.mBlendMask = blendMask; state.mAutoDisable = autodisable; mStates[groupname] = state; NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime())); if (state.mPlaying) { while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, groupname, textkey, textkeys); ++textkey; } } if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0) { state.mLoopCount--; state.setTime(state.mLoopStartTime); state.mPlaying = true; if(state.getTime() >= state.mLoopStopTime) break; NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime())); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, groupname, textkey, textkeys); ++textkey; } } break; } } if(iter == mAnimSources.rend()) std::cerr<< "Failed to find animation "<second.compare(0, groupname.size(), groupname) == 0 && groupend->second.compare(groupname.size(), 2, ": ") == 0) break; } std::string starttag = groupname+": "+start; NifOsg::TextKeyMap::const_reverse_iterator startkey(groupend); while(startkey != keys.rend() && startkey->second != starttag) ++startkey; if(startkey == keys.rend() && start == "loop start") { starttag = groupname+": start"; startkey = groupend; while(startkey != keys.rend() && startkey->second != starttag) ++startkey; } if(startkey == keys.rend()) return false; const std::string stoptag = groupname+": "+stop; NifOsg::TextKeyMap::const_reverse_iterator stopkey(groupend); while(stopkey != keys.rend() // We have to ignore extra garbage at the end. // The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop". // Why, just why? :( && (stopkey->second.size() < stoptag.size() || stopkey->second.substr(0,stoptag.size()) != stoptag)) ++stopkey; if(stopkey == keys.rend()) return false; if(startkey->first > stopkey->first) return false; state.mStartTime = startkey->first; if (loopfallback) { state.mLoopStartTime = startkey->first; state.mLoopStopTime = stopkey->first; } else { state.mLoopStartTime = startkey->first; state.mLoopStopTime = std::numeric_limits::max(); } state.mStopTime = stopkey->first; state.setTime(state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint)); // mLoopStartTime and mLoopStopTime normally get assigned when encountering these keys while playing the animation // (see handleTextKey). But if startpoint is already past these keys, or start time is == stop time, we need to assign them now. const std::string loopstarttag = groupname+": loop start"; const std::string loopstoptag = groupname+": loop stop"; NifOsg::TextKeyMap::const_reverse_iterator key(groupend); for (; key != startkey && key != keys.rend(); ++key) { if (key->first > state.getTime()) continue; if (key->second == loopstarttag) state.mLoopStartTime = key->first; else if (key->second == loopstoptag) state.mLoopStopTime = key->first; } return true; } void Animation::setTextKeyListener(Animation::TextKeyListener *listener) { mTextKeyListener = listener; } void Animation::resetActiveGroups() { // remove all previous external controllers from the scene graph for (ControllerMap::iterator it = mActiveControllers.begin(); it != mActiveControllers.end(); ++it) { osg::Node* node = it->first; node->removeUpdateCallback(it->second); // Should be no longer needed with OSG 3.4 it->second->setNestedCallback(NULL); } mActiveControllers.clear(); mAccumCtrl = NULL; for(size_t blendMask = 0;blendMask < sNumBlendMasks;blendMask++) { AnimStateMap::const_iterator active = mStates.end(); AnimStateMap::const_iterator state = mStates.begin(); for(;state != mStates.end();++state) { if(!(state->second.mBlendMask&(1<second.mPriority[(BoneGroup)blendMask] < state->second.mPriority[(BoneGroup)blendMask]) active = state; } mAnimationTimePtr[blendMask]->setTimePtr(active == mStates.end() ? boost::shared_ptr() : active->second.mTime); // add external controllers for the AnimSource active in this blend mask if (active != mStates.end()) { boost::shared_ptr animsrc = active->second.mSource; for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[blendMask].begin(); it != animsrc->mControllerMap[blendMask].end(); ++it) { osg::ref_ptr node = mNodeMap.at(it->first); // this should not throw, we already checked for the node existing in addAnimSource node->addUpdateCallback(it->second); mActiveControllers.insert(std::make_pair(node, it->second)); if (blendMask == 0 && node == mAccumRoot) { mAccumCtrl = it->second; // make sure reset is last in the chain of callbacks if (!mResetAccumRootCallback) { mResetAccumRootCallback = new ResetAccumRootCallback; mResetAccumRootCallback->setAccumulate(mAccumulate); } mAccumRoot->addUpdateCallback(mResetAccumRootCallback); mActiveControllers.insert(std::make_pair(mAccumRoot, mResetAccumRootCallback)); } } } } addControllers(); } void Animation::stopLooping(const std::string& groupname) { AnimStateMap::iterator stateiter = mStates.find(groupname); if(stateiter != mStates.end()) { stateiter->second.mLoopCount = 0; return; } } void Animation::adjustSpeedMult(const std::string &groupname, float speedmult) { AnimStateMap::iterator state(mStates.find(groupname)); if(state != mStates.end()) state->second.mSpeedMult = speedmult; } bool Animation::isPlaying(const std::string &groupname) const { AnimStateMap::const_iterator state(mStates.find(groupname)); if(state != mStates.end()) return state->second.mPlaying; return false; } bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult) const { AnimStateMap::const_iterator iter = mStates.find(groupname); if(iter == mStates.end()) { if(complete) *complete = 0.0f; if(speedmult) *speedmult = 0.0f; return false; } if(complete) { if(iter->second.mStopTime > iter->second.mStartTime) *complete = (iter->second.getTime() - iter->second.mStartTime) / (iter->second.mStopTime - iter->second.mStartTime); else *complete = (iter->second.mPlaying ? 0.0f : 1.0f); } if(speedmult) *speedmult = iter->second.mSpeedMult; return true; } float Animation::getCurrentTime(const std::string &groupname) const { AnimStateMap::const_iterator iter = mStates.find(groupname); if(iter == mStates.end()) return -1.f; return iter->second.getTime(); } void Animation::disable(const std::string &groupname) { AnimStateMap::iterator iter = mStates.find(groupname); if(iter != mStates.end()) mStates.erase(iter); resetActiveGroups(); } float Animation::getVelocity(const std::string &groupname) const { if (!mAccumRoot) return 0.0f; // Look in reverse; last-inserted source has priority. AnimSourceList::const_reverse_iterator animsrc(mAnimSources.rbegin()); for(;animsrc != mAnimSources.rend();++animsrc) { const NifOsg::TextKeyMap &keys = (*animsrc)->getTextKeys(); if(findGroupStart(keys, groupname) != keys.end()) break; } if(animsrc == mAnimSources.rend()) return 0.0f; float velocity = 0.0f; const NifOsg::TextKeyMap &keys = (*animsrc)->getTextKeys(); const AnimSource::ControllerMap& ctrls = (*animsrc)->mControllerMap[0]; for (AnimSource::ControllerMap::const_iterator it = ctrls.begin(); it != ctrls.end(); ++it) { if (Misc::StringUtils::ciEqual(it->first, mAccumRoot->getName())) { velocity = calcAnimVelocity(keys, it->second, mAccumulate, groupname); break; } } // If there's no velocity, keep looking if(!(velocity > 1.0f)) { AnimSourceList::const_reverse_iterator animiter = mAnimSources.rbegin(); while(*animiter != *animsrc) ++animiter; while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend()) { const NifOsg::TextKeyMap &keys = (*animiter)->getTextKeys(); const AnimSource::ControllerMap& ctrls = (*animiter)->mControllerMap[0]; for (AnimSource::ControllerMap::const_iterator it = ctrls.begin(); it != ctrls.end(); ++it) { if (Misc::StringUtils::ciEqual(it->first, mAccumRoot->getName())) { velocity = calcAnimVelocity(keys, it->second, mAccumulate, groupname); break; } } } } return velocity; } void Animation::updatePosition(float oldtime, float newtime, osg::Vec3f& position) { // Get the difference from the last update, and move the position osg::Vec3f off = osg::componentMultiply(mAccumCtrl->getTranslation(newtime), mAccumulate); position += off - osg::componentMultiply(mAccumCtrl->getTranslation(oldtime), mAccumulate); } osg::Vec3f Animation::runAnimation(float duration) { osg::Vec3f movement(0.f, 0.f, 0.f); AnimStateMap::iterator stateiter = mStates.begin(); while(stateiter != mStates.end()) { AnimState &state = stateiter->second; const NifOsg::TextKeyMap &textkeys = state.mSource->getTextKeys(); NifOsg::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.getTime())); float timepassed = duration * state.mSpeedMult; while(state.mPlaying) { float targetTime; if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0) goto handle_loop; targetTime = state.getTime() + timepassed; if(textkey == textkeys.end() || textkey->first > targetTime) { if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) updatePosition(state.getTime(), targetTime, movement); state.setTime(std::min(targetTime, state.mStopTime)); } else { if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) updatePosition(state.getTime(), textkey->first, movement); state.setTime(textkey->first); } state.mPlaying = (state.getTime() < state.mStopTime); timepassed = targetTime - state.getTime(); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, stateiter->first, textkey, textkeys); ++textkey; } if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0) { handle_loop: state.mLoopCount--; state.setTime(state.mLoopStartTime); state.mPlaying = true; textkey = textkeys.lower_bound(state.getTime()); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, stateiter->first, textkey, textkeys); ++textkey; } if(state.getTime() >= state.mLoopStopTime) break; } if(timepassed <= 0.0f) break; } if(!state.mPlaying && state.mAutoDisable) { mStates.erase(stateiter++); resetActiveGroups(); } else ++stateiter; } updateEffects(duration); if (mHeadController) { const float epsilon = 0.001f; bool enable = (std::abs(mHeadPitchRadians) > epsilon || std::abs(mHeadYawRadians) > epsilon); mHeadController->setEnabled(enable); if (enable) mHeadController->setRotate(osg::Quat(mHeadPitchRadians, osg::Vec3f(1,0,0)) * osg::Quat(mHeadYawRadians, osg::Vec3f(0,0,1))); } return movement; } void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature) { osg::ref_ptr previousStateset; if (mObjectRoot) { previousStateset = mObjectRoot->getStateSet(); mObjectRoot->getParent(0)->removeChild(mObjectRoot); } mObjectRoot = NULL; mSkeleton = NULL; mNodeMap.clear(); mActiveControllers.clear(); mAccumRoot = NULL; mAccumCtrl = NULL; if (!forceskeleton) { osg::ref_ptr created = mResourceSystem->getSceneManager()->createInstance(model, mInsert); mObjectRoot = created->asGroup(); if (!mObjectRoot) { mInsert->removeChild(created); mObjectRoot = new osg::Group; mObjectRoot->addChild(created); mInsert->addChild(mObjectRoot); } } else { osg::ref_ptr created = mResourceSystem->getSceneManager()->createInstance(model); osg::ref_ptr skel = dynamic_cast(created.get()); if (!skel) { skel = new SceneUtil::Skeleton; skel->addChild(created); } mSkeleton = skel.get(); mObjectRoot = skel; mInsert->addChild(mObjectRoot); } if (previousStateset) mObjectRoot->setStateSet(previousStateset); if (baseonly) { RemoveDrawableVisitor removeDrawableVisitor; mObjectRoot->accept(removeDrawableVisitor); removeDrawableVisitor.remove(); } if (isCreature) { RemoveTriBipVisitor removeTriBipVisitor; mObjectRoot->accept(removeTriBipVisitor); removeTriBipVisitor.remove(); } NodeMapVisitor visitor; mObjectRoot->accept(visitor); mNodeMap = visitor.getNodeMap(); mObjectRoot->addCullCallback(new SceneUtil::LightListCallback); } osg::Group* Animation::getObjectRoot() { return mObjectRoot.get(); } osg::Group* Animation::getOrCreateObjectRoot() { if (mObjectRoot) return mObjectRoot.get(); mObjectRoot = new osg::Group; mInsert->addChild(mObjectRoot); return mObjectRoot.get(); } void Animation::addGlow(osg::ref_ptr node, osg::Vec4f glowColor) { std::vector > textures; for (int i=0; i<32; ++i) { std::stringstream stream; stream << "textures/magicitem/caust"; stream << std::setw(2); stream << std::setfill('0'); stream << i; stream << ".dds"; textures.push_back(mResourceSystem->getTextureManager()->getTexture2D(stream.str(), osg::Texture2D::REPEAT, osg::Texture2D::REPEAT)); } osg::ref_ptr glowupdater (new GlowUpdater(glowColor, textures)); node->addUpdateCallback(glowupdater); } // TODO: Should not be here osg::Vec4f Animation::getEnchantmentColor(MWWorld::Ptr item) { osg::Vec4f result(1,1,1,1); std::string enchantmentName = item.getClass().getEnchantment(item); if (enchantmentName.empty()) return result; const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(enchantmentName); assert (enchantment->mEffects.mList.size()); const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( enchantment->mEffects.mList.front().mEffectID); result.x() = magicEffect->mData.mRed / 255.f; result.y() = magicEffect->mData.mGreen / 255.f; result.z() = magicEffect->mData.mBlue / 255.f; return result; } void Animation::addExtraLight(osg::ref_ptr parent, const ESM::Light *esmLight) { SceneUtil::FindByNameVisitor visitor("AttachLight"); parent->accept(visitor); osg::Group* attachTo = NULL; if (visitor.mFoundNode) { attachTo = visitor.mFoundNode; } else { osg::ComputeBoundsVisitor computeBound; computeBound.setTraversalMask(~Mask_ParticleSystem); parent->accept(computeBound); // PositionAttitudeTransform seems to be slightly faster than MatrixTransform osg::ref_ptr trans(new osg::PositionAttitudeTransform); trans->setPosition(computeBound.getBoundingBox().center()); parent->addChild(trans); attachTo = trans; } osg::ref_ptr lightSource = new SceneUtil::LightSource; osg::ref_ptr light (new osg::Light); lightSource->setNodeMask(Mask_Lighting); const MWWorld::Fallback* fallback = MWBase::Environment::get().getWorld()->getFallback(); float radius = esmLight->mData.mRadius; lightSource->setRadius(radius); static bool outQuadInLin = fallback->getFallbackBool("LightAttenuation_OutQuadInLin"); static bool useQuadratic = fallback->getFallbackBool("LightAttenuation_UseQuadratic"); static float quadraticValue = fallback->getFallbackFloat("LightAttenuation_QuadraticValue"); static float quadraticRadiusMult = fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); static bool useLinear = fallback->getFallbackBool("LightAttenuation_UseLinear"); static float linearRadiusMult = fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); static float linearValue = fallback->getFallbackFloat("LightAttenuation_LinearValue"); bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior(); SceneUtil::configureLight(light, radius, exterior, outQuadInLin, useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue); osg::Vec4f diffuse = SceneUtil::colourFromRGB(esmLight->mData.mColor); if (esmLight->mData.mFlags & ESM::Light::Negative) { diffuse *= -1; diffuse.a() = 1; } light->setDiffuse(diffuse); light->setAmbient(osg::Vec4f(0,0,0,1)); light->setSpecular(osg::Vec4f(0,0,0,0)); lightSource->setLight(light); osg::ref_ptr ctrl (new SceneUtil::LightController); ctrl->setDiffuse(light->getDiffuse()); if (esmLight->mData.mFlags & ESM::Light::Flicker) ctrl->setType(SceneUtil::LightController::LT_Flicker); if (esmLight->mData.mFlags & ESM::Light::FlickerSlow) ctrl->setType(SceneUtil::LightController::LT_FlickerSlow); if (esmLight->mData.mFlags & ESM::Light::Pulse) ctrl->setType(SceneUtil::LightController::LT_Pulse); if (esmLight->mData.mFlags & ESM::Light::PulseSlow) ctrl->setType(SceneUtil::LightController::LT_PulseSlow); lightSource->addUpdateCallback(ctrl); attachTo->addChild(lightSource); } void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, std::string texture) { if (!mObjectRoot.get()) return; // Early out if we already have this effect for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) if (it->mLoop && loop && it->mEffectId == effectId && it->mBoneName == bonename) return; EffectParams params; params.mModelName = model; osg::ref_ptr parentNode; if (bonename.empty()) parentNode = mInsert; else { NodeMap::iterator found = mNodeMap.find(Misc::StringUtils::lowerCase(bonename)); if (found == mNodeMap.end()) throw std::runtime_error("Can't find bone " + bonename); parentNode = found->second; } osg::ref_ptr node = mResourceSystem->getSceneManager()->createInstance(model, parentNode); node->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); params.mObjects = PartHolderPtr(new PartHolder(node)); SceneUtil::FindMaxControllerLengthVisitor findMaxLengthVisitor; node->accept(findMaxLengthVisitor); // FreezeOnCull doesn't work so well with effect particles, that tend to have moving emitters SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; node->accept(disableFreezeOnCullVisitor); params.mMaxControllerLength = findMaxLengthVisitor.getMaxLength(); node->setNodeMask(Mask_Effect); params.mLoop = loop; params.mEffectId = effectId; params.mBoneName = bonename; params.mAnimTime = boost::shared_ptr(new EffectAnimationTime); SceneUtil::AssignControllerSourcesVisitor assignVisitor(boost::shared_ptr(params.mAnimTime)); node->accept(assignVisitor); overrideTexture(texture, mResourceSystem, node); // TODO: in vanilla morrowind the effect is scaled based on the host object's bounding box. mEffects.push_back(params); } void Animation::removeEffect(int effectId) { for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) { if (it->mEffectId == effectId) { mEffects.erase(it); return; } } } void Animation::getLoopingEffects(std::vector &out) { for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) { if (it->mLoop) out.push_back(it->mEffectId); } } void Animation::updateEffects(float duration) { for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ) { it->mAnimTime->addTime(duration); if (it->mAnimTime->getTime() >= it->mMaxControllerLength) { if (it->mLoop) { // Start from the beginning again; carry over the remainder // Not sure if this is actually needed, the controller function might already handle loops float remainder = it->mAnimTime->getTime() - it->mMaxControllerLength; it->mAnimTime->resetTime(remainder); } else { it = mEffects.erase(it); continue; } } ++it; } } bool Animation::upperBodyReady() const { for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) { if (stateiter->second.mPriority.contains(int(MWMechanics::Priority_Hit)) || stateiter->second.mPriority.contains(int(MWMechanics::Priority_Weapon)) || stateiter->second.mPriority.contains(int(MWMechanics::Priority_Knockdown)) || stateiter->second.mPriority.contains(int(MWMechanics::Priority_Death))) return false; } return true; } const osg::Node* Animation::getNode(const std::string &name) const { std::string lowerName = Misc::StringUtils::lowerCase(name); NodeMap::const_iterator found = mNodeMap.find(lowerName); if (found == mNodeMap.end()) return NULL; else return found->second; } void Animation::setAlpha(float alpha) { if (alpha == mAlpha) return; mAlpha = alpha; if (alpha != 1.f) { osg::StateSet* stateset (new osg::StateSet); osg::BlendFunc* blendfunc (new osg::BlendFunc); stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); // FIXME: overriding diffuse/ambient/emissive colors osg::Material* material (new osg::Material); material->setColorMode(osg::Material::OFF); material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,alpha)); material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); mObjectRoot->setStateSet(stateset); } else { mObjectRoot->setStateSet(NULL); } setRenderBin(); } void Animation::setRenderBin() { if (mAlpha != 1.f) { osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); } else if (osg::StateSet* stateset = mObjectRoot->getStateSet()) stateset->setRenderBinToInherit(); } void Animation::setLightEffect(float effect) { if (effect == 0) { if (mGlowLight) { mInsert->removeChild(mGlowLight); mGlowLight = NULL; } } else { effect += 3; float radius = effect * 66.f; float linearAttenuation = 0.5f / effect; if (!mGlowLight || linearAttenuation != mGlowLight->getLight(0)->getLinearAttenuation()) { if (mGlowLight) { mInsert->removeChild(mGlowLight); mGlowLight = NULL; } osg::ref_ptr light (new osg::Light); light->setDiffuse(osg::Vec4f(0,0,0,0)); light->setSpecular(osg::Vec4f(0,0,0,0)); light->setAmbient(osg::Vec4f(1.5f,1.5f,1.5f,1.f)); light->setLinearAttenuation(linearAttenuation); mGlowLight = new SceneUtil::LightSource; mGlowLight->setNodeMask(Mask_Lighting); mInsert->addChild(mGlowLight); mGlowLight->setLight(light); } mGlowLight->setRadius(radius); } } void Animation::addControllers() { mHeadController = NULL; if (mPtr.getClass().isBipedal(mPtr)) { NodeMap::iterator found = mNodeMap.find("bip01 head"); if (found != mNodeMap.end() && dynamic_cast(found->second.get())) { osg::Node* node = found->second; mHeadController = new RotateController(mObjectRoot.get()); node->addUpdateCallback(mHeadController); mActiveControllers.insert(std::make_pair(node, mHeadController)); } } } void Animation::setHeadPitch(float pitchRadians) { mHeadPitchRadians = pitchRadians; } void Animation::setHeadYaw(float yawRadians) { mHeadYawRadians = yawRadians; } float Animation::getHeadPitch() const { return mHeadPitchRadians; } float Animation::getHeadYaw() const { return mHeadYawRadians; } // ------------------------------------------------------ float Animation::AnimationTime::getValue(osg::NodeVisitor*) { if (mTimePtr) return *mTimePtr; return 0.f; } float EffectAnimationTime::getValue(osg::NodeVisitor*) { return mTime; } void EffectAnimationTime::addTime(float duration) { mTime += duration; } void EffectAnimationTime::resetTime(float time) { mTime = time; } float EffectAnimationTime::getTime() const { return mTime; } // -------------------------------------------------------------------------------- ObjectAnimation::ObjectAnimation(const MWWorld::Ptr &ptr, const std::string &model, Resource::ResourceSystem* resourceSystem, bool animated, bool allowLight) : Animation(ptr, osg::ref_ptr(ptr.getRefData().getBaseNode()), resourceSystem) { if (!model.empty()) { setObjectRoot(model, false, false, false); if (animated) addAnimSource(model); if (!ptr.getClass().getEnchantment(ptr).empty()) addGlow(mObjectRoot, getEnchantmentColor(ptr)); } if (ptr.getTypeName() == typeid(ESM::Light).name() && allowLight) addExtraLight(getOrCreateObjectRoot(), ptr.get()->mBase); } Animation::AnimState::~AnimState() { } // ------------------------------ PartHolder::PartHolder(osg::ref_ptr node) : mNode(node) { } PartHolder::~PartHolder() { if (mNode->getNumParents()) mNode->getParent(0)->removeChild(mNode); } } openmw-openmw-0.38.0/apps/openmw/mwrender/animation.hpp000066400000000000000000000371371264522266000232150ustar00rootroot00000000000000#ifndef GAME_RENDER_ANIMATION_H #define GAME_RENDER_ANIMATION_H #include "../mwworld/ptr.hpp" #include namespace ESM { struct Light; } namespace Resource { class ResourceSystem; } namespace NifOsg { class KeyframeHolder; class KeyframeController; } namespace SceneUtil { class LightSource; class Skeleton; } namespace MWRender { class ResetAccumRootCallback; class RotateController; class EffectAnimationTime : public SceneUtil::ControllerSource { private: float mTime; public: virtual float getValue(osg::NodeVisitor* nv); void addTime(float duration); void resetTime(float time); float getTime() const; EffectAnimationTime() : mTime(0) { } }; /// @brief Detaches the node from its parent when the object goes out of scope. class PartHolder { public: PartHolder(osg::ref_ptr node); ~PartHolder(); osg::ref_ptr getNode() { return mNode; } private: osg::ref_ptr mNode; void operator= (const PartHolder&); PartHolder(const PartHolder&); }; typedef boost::shared_ptr PartHolderPtr; class Animation { public: enum BoneGroup { BoneGroup_LowerBody = 0, BoneGroup_Torso, BoneGroup_LeftArm, BoneGroup_RightArm }; enum BlendMask { BlendMask_LowerBody = 1<<0, BlendMask_Torso = 1<<1, BlendMask_LeftArm = 1<<2, BlendMask_RightArm = 1<<3, BlendMask_UpperBody = BlendMask_Torso | BlendMask_LeftArm | BlendMask_RightArm, BlendMask_All = BlendMask_LowerBody | BlendMask_UpperBody }; /* This is the number of *discrete* blend masks. */ static const size_t sNumBlendMasks = 4; /// Holds an animation priority value for each BoneGroup. struct AnimPriority { /// Convenience constructor, initialises all priorities to the same value. AnimPriority(int priority) { for (unsigned int i=0; i::const_iterator &key, const std::multimap& map) = 0; }; void setTextKeyListener(TextKeyListener* listener); protected: class AnimationTime : public SceneUtil::ControllerSource { private: boost::shared_ptr mTimePtr; public: void setTimePtr(boost::shared_ptr time) { mTimePtr = time; } boost::shared_ptr getTimePtr() const { return mTimePtr; } virtual float getValue(osg::NodeVisitor* nv); }; class NullAnimationTime : public SceneUtil::ControllerSource { public: virtual float getValue(osg::NodeVisitor *nv) { return 0.f; } }; struct AnimSource; struct AnimState { boost::shared_ptr mSource; float mStartTime; float mLoopStartTime; float mLoopStopTime; float mStopTime; typedef boost::shared_ptr TimePtr; TimePtr mTime; float mSpeedMult; bool mPlaying; size_t mLoopCount; AnimPriority mPriority; int mBlendMask; bool mAutoDisable; AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f), mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), mPriority(0), mBlendMask(0), mAutoDisable(true) { } ~AnimState(); float getTime() const { return *mTime; } void setTime(float time) { *mTime = time; } }; typedef std::map AnimStateMap; AnimStateMap mStates; typedef std::vector > AnimSourceList; AnimSourceList mAnimSources; osg::ref_ptr mInsert; osg::ref_ptr mObjectRoot; SceneUtil::Skeleton* mSkeleton; // The node expected to accumulate movement during movement animations. osg::ref_ptr mAccumRoot; // The controller animating that node. osg::ref_ptr mAccumCtrl; // Used to reset the position of the accumulation root every frame - the movement should be applied to the physics system osg::ref_ptr mResetAccumRootCallback; // Keep track of controllers that we added to our scene graph. // We may need to rebuild these controllers when the active animation groups / sources change. typedef std::multimap, osg::ref_ptr > ControllerMap; ControllerMap mActiveControllers; boost::shared_ptr mAnimationTimePtr[sNumBlendMasks]; // Stored in all lowercase for a case-insensitive lookup typedef std::map > NodeMap; NodeMap mNodeMap; MWWorld::Ptr mPtr; Resource::ResourceSystem* mResourceSystem; osg::Vec3f mAccumulate; struct EffectParams { std::string mModelName; // Just here so we don't add the same effect twice PartHolderPtr mObjects; boost::shared_ptr mAnimTime; float mMaxControllerLength; int mEffectId; bool mLoop; std::string mBoneName; }; std::vector mEffects; TextKeyListener* mTextKeyListener; osg::ref_ptr mHeadController; float mHeadYawRadians; float mHeadPitchRadians; osg::ref_ptr mGlowLight; float mAlpha; /* Sets the appropriate animations on the bone groups based on priority. */ void resetActiveGroups(); size_t detectBlendMask(osg::Node* node); /* Updates the position of the accum root node for the given time, and * returns the wanted movement vector from the previous time. */ void updatePosition(float oldtime, float newtime, osg::Vec3f& position); /* Resets the animation to the time of the specified start marker, without * moving anything, and set the end time to the specified stop marker. If * the marker is not found, or if the markers are the same, it returns * false. */ bool reset(AnimState &state, const std::multimap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, bool loopfallback); void handleTextKey(AnimState &state, const std::string &groupname, const std::multimap::const_iterator &key, const std::multimap& map); /** Sets the root model of the object. * * Note that you must make sure all animation sources are cleared before reseting the object * root. All nodes previously retrieved with getNode will also become invalidated. * @param forceskeleton Wrap the object root in a Skeleton, even if it contains no skinned parts. Use this if you intend to add skinned parts manually. * @param baseonly If true, then any meshes or particle systems in the model are ignored * (useful for NPCs, where only the skeleton is needed for the root, and the actual NPC parts are then assembled from separate files). */ void setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature); /* Adds the keyframe controllers in the specified model as a new animation source. Note that * the filename portion of the provided model name will be prepended with 'x', and the .nif * extension will be replaced with .kf. */ void addAnimSource(const std::string &model); /** Adds an additional light to the given node using the specified ESM record. */ void addExtraLight(osg::ref_ptr parent, const ESM::Light *light); void clearAnimSources(); /** * Provided to allow derived classes adding their own controllers. Note, the controllers must be added to mActiveControllers * so they get cleaned up properly on the next controller rebuild. A controller rebuild may be necessary to ensure correct ordering. */ virtual void addControllers(); osg::Vec4f getEnchantmentColor(MWWorld::Ptr item); void addGlow(osg::ref_ptr node, osg::Vec4f glowColor); /// Set the render bin for this animation's object root. May be customized by subclasses. virtual void setRenderBin(); public: Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem); virtual ~Animation(); MWWorld::Ptr getPtr(); /// Set active flag on the object skeleton, if one exists. /// @see SceneUtil::Skeleton::setActive void setActive(bool active); osg::Group* getOrCreateObjectRoot(); osg::Group* getObjectRoot(); /** * @brief Add an effect mesh attached to a bone or the insert scene node * @param model * @param effectId An ID for this effect by which you can identify it later. If this is not wanted, set to -1. * @param loop Loop the effect. If false, it is removed automatically after it finishes playing. If true, * you need to remove it manually using removeEffect when the effect should end. * @param bonename Bone to attach to, or empty string to use the scene node instead * @param texture override the texture specified in the model's materials - if empty, do not override * @note Will not add an effect twice. */ void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", std::string texture = ""); void removeEffect (int effectId); void getLoopingEffects (std::vector& out); virtual void updatePtr(const MWWorld::Ptr &ptr); bool hasAnimation(const std::string &anim); // Specifies the axis' to accumulate on. Non-accumulated axis will just // move visually, but not affect the actual movement. Each x/y/z value // should be on the scale of 0 to 1. void setAccumulation(const osg::Vec3f& accum); /** Plays an animation. * \param groupname Name of the animation group to play. * \param priority Priority of the animation. The animation will play on * bone groups that don't have another animation set of a * higher priority. * \param blendMask Bone groups to play the animation on. * \param autodisable Automatically disable the animation when it stops * playing. * \param speedmult Speed multiplier for the animation. * \param start Key marker from which to start. * \param stop Key marker to stop at. * \param startpoint How far in between the two markers to start. 0 starts * at the start marker, 1 starts at the stop marker. * \param loops How many times to loop the animation. This will use the * "loop start" and "loop stop" markers if they exist, * otherwise it may fall back to "start" and "stop", but only if * the \a loopFallback parameter is true. * \param loopFallback Allow looping an animation that has no loop keys, i.e. fall back to use * the "start" and "stop" keys for looping? */ void play(const std::string &groupname, const AnimPriority& priority, int blendMask, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops, bool loopfallback=false); /** If the given animation group is currently playing, set its remaining loop count to '0'. */ void stopLooping(const std::string& groupName); /** Adjust the speed multiplier of an already playing animation. */ void adjustSpeedMult (const std::string& groupname, float speedmult); /** Returns true if the named animation group is playing. */ bool isPlaying(const std::string &groupname) const; /// Returns true if no important animations are currently playing on the upper body. bool upperBodyReady() const; /** Gets info about the given animation group. * \param groupname Animation group to check. * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. * \param speedmult Stores the animation speed multiplier * \return True if the animation is active, false otherwise. */ bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; /// Get the absolute position in the animation track of the first text key with the given group. float getStartTime(const std::string &groupname) const; /// Get the absolute position in the animation track of the text key float getTextKeyTime(const std::string &textKey) const; /// Get the current absolute position in the animation track for the animation that is currently playing from the given group. float getCurrentTime(const std::string& groupname) const; /** Disables the specified animation group; * \param groupname Animation group to disable. */ void disable(const std::string &groupname); /** Retrieves the velocity (in units per second) that the animation will move. */ float getVelocity(const std::string &groupname) const; virtual osg::Vec3f runAnimation(float duration); /// This is typically called as part of runAnimation, but may be called manually if needed. void updateEffects(float duration); /// Return a node with the specified name, or NULL if not existing. /// @note The matching is case-insensitive. const osg::Node* getNode(const std::string& name) const; virtual void showWeapons(bool showWeapon) {} virtual void showCarriedLeft(bool show) {} virtual void setWeaponGroup(const std::string& group) {} virtual void setVampire(bool vampire) {} /// A value < 1 makes the animation translucent, 1.f = fully opaque void setAlpha(float alpha); virtual void setPitchFactor(float factor) {} virtual void attachArrow() {} virtual void releaseArrow(float attackStrength) {} virtual void enableHeadAnimation(bool enable) {} // TODO: move outside of this class /// Makes this object glow, by placing a Light in its center. /// @param effect Controls the radius and intensity of the light. virtual void setLightEffect(float effect); virtual void setHeadPitch(float pitchRadians); virtual void setHeadYaw(float yawRadians); virtual float getHeadPitch() const; virtual float getHeadYaw() const; virtual void setAccurateAiming(bool enabled) {} private: Animation(const Animation&); void operator=(Animation&); }; class ObjectAnimation : public Animation { public: ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, Resource::ResourceSystem* resourceSystem, bool animated, bool allowLight); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/bulletdebugdraw.cpp000066400000000000000000000046151264522266000244000ustar00rootroot00000000000000#include "bulletdebugdraw.hpp" #include #include #include #include #include #include "vismask.hpp" namespace { osg::Vec3f toOsg(const btVector3& vec) { return osg::Vec3f(vec.x(), vec.y(), vec.z()); } } namespace MWRender { DebugDrawer::DebugDrawer(osg::ref_ptr parentNode, btCollisionWorld *world) : mParentNode(parentNode), mWorld(world), mDebugOn(true) { mGeode = new osg::Geode; mParentNode->addChild(mGeode); mGeode->setNodeMask(Mask_Debug); createGeometry(); mParentNode->addChild(mGeode); } void DebugDrawer::createGeometry() { if (!mGeometry) { mGeometry = new osg::Geometry; mVertices = new osg::Vec3Array; mDrawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES); mGeometry->setUseDisplayList(false); mGeometry->setVertexArray(mVertices); mGeometry->setDataVariance(osg::Object::DYNAMIC); mGeometry->addPrimitiveSet(mDrawArrays); mGeode->addDrawable(mGeometry); } } void DebugDrawer::destroyGeometry() { if (mGeometry) { mGeode->removeDrawable(mGeometry); mGeometry = NULL; mVertices = NULL; mDrawArrays = NULL; } } DebugDrawer::~DebugDrawer() { mParentNode->removeChild(mGeode); } void DebugDrawer::step() { if (mDebugOn) { mVertices->clear(); mWorld->debugDrawWorld(); mDrawArrays->setCount(mVertices->size()); mVertices->dirty(); mGeometry->dirtyBound(); } } void DebugDrawer::drawLine(const btVector3 &from, const btVector3 &to, const btVector3 &color) { mVertices->push_back(toOsg(from)); mVertices->push_back(toOsg(to)); } void DebugDrawer::drawContactPoint(const btVector3 &PointOnB, const btVector3 &normalOnB, btScalar distance, int lifeTime, const btVector3 &color) { mVertices->push_back(toOsg(PointOnB)); mVertices->push_back(toOsg(PointOnB) + (toOsg(normalOnB) * distance * 20)); } void DebugDrawer::reportErrorWarning(const char *warningString) { std::cerr << warningString << std::endl; } void DebugDrawer::setDebugMode(int isOn) { mDebugOn = (isOn == 0) ? false : true; if (!mDebugOn) destroyGeometry(); else createGeometry(); } int DebugDrawer::getDebugMode() const { return mDebugOn; } } openmw-openmw-0.38.0/apps/openmw/mwrender/bulletdebugdraw.hpp000066400000000000000000000024571264522266000244070ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_BULLETDEBUGDRAW_H #define OPENMW_MWRENDER_BULLETDEBUGDRAW_H #include #include #include #include class btCollisionWorld; namespace osg { class Group; class Geode; class Geometry; } namespace MWRender { class DebugDrawer : public btIDebugDraw { protected: osg::ref_ptr mParentNode; btCollisionWorld *mWorld; osg::ref_ptr mGeode; osg::ref_ptr mGeometry; osg::ref_ptr mVertices; osg::ref_ptr mDrawArrays; bool mDebugOn; void createGeometry(); void destroyGeometry(); public: DebugDrawer(osg::ref_ptr parentNode, btCollisionWorld *world); ~DebugDrawer(); void step(); void drawLine(const btVector3& from,const btVector3& to,const btVector3& color); void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color); void reportErrorWarning(const char* warningString); void draw3dText(const btVector3& location,const char* textString) {} //0 for off, anything else for on. void setDebugMode(int isOn); //0 for off, anything else for on. int getDebugMode() const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/camera.cpp000066400000000000000000000244701264522266000224550ustar00rootroot00000000000000#include "camera.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/refdata.hpp" #include "npcanimation.hpp" namespace { class UpdateRenderCameraCallback : public osg::NodeCallback { public: UpdateRenderCameraCallback(MWRender::Camera* cam) : mCamera(cam) { } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::Camera* cam = static_cast(node); // traverse first to update animations, in case the camera is attached to an animated node traverse(node, nv); mCamera->updateCamera(cam); } private: MWRender::Camera* mCamera; }; } namespace MWRender { Camera::Camera (osg::Camera* camera) : mHeightScale(1.f), mCamera(camera), mAnimation(NULL), mFirstPersonView(true), mPreviewMode(false), mFreeLook(true), mNearest(30.f), mFurthest(800.f), mIsNearest(false), mHeight(124.f), mMaxCameraDistance(192.f), mDistanceAdjusted(false), mVanityToggleQueued(false), mViewModeToggleQueued(false), mCameraDistance(0.f) { mVanity.enabled = false; mVanity.allowed = true; mPreviewCam.pitch = 0.f; mPreviewCam.yaw = 0.f; mPreviewCam.offset = 400.f; mMainCam.pitch = 0.f; mMainCam.yaw = 0.f; mMainCam.offset = 400.f; mUpdateCallback = new UpdateRenderCameraCallback(this); mCamera->addUpdateCallback(mUpdateCallback); } Camera::~Camera() { mCamera->removeUpdateCallback(mUpdateCallback); } MWWorld::Ptr Camera::getTrackingPtr() const { return mTrackingPtr; } osg::Vec3d Camera::getFocalPoint() { const osg::Node* trackNode = mTrackingNode; if (!trackNode) return osg::Vec3d(); osg::MatrixList mats = trackNode->getWorldMatrices(); if (!mats.size()) return osg::Vec3d(); const osg::Matrix& worldMat = mats[0]; osg::Vec3d position = worldMat.getTrans(); if (!isFirstPerson()) position.z() += mHeight * mHeightScale; return position; } void Camera::updateCamera(osg::Camera *cam) { if (mTrackingPtr.isEmpty()) return; osg::Vec3d position = getFocalPoint(); osg::Quat orient = osg::Quat(getPitch(), osg::Vec3d(1,0,0)) * osg::Quat(getYaw(), osg::Vec3d(0,0,1)); osg::Vec3d offset = orient * osg::Vec3d(0, -mCameraDistance, 0); position += offset; osg::Vec3d forward = orient * osg::Vec3d(0,1,0); osg::Vec3d up = orient * osg::Vec3d(0,0,1); cam->setViewMatrixAsLookAt(position, position + forward, up); } void Camera::reset() { togglePreviewMode(false); toggleVanityMode(false); if (!mFirstPersonView) toggleViewMode(); } void Camera::rotateCamera(float pitch, float yaw, bool adjust) { if (adjust) { pitch += getPitch(); yaw += getYaw(); } setYaw(yaw); setPitch(pitch); } void Camera::attachTo(const MWWorld::Ptr &ptr) { mTrackingPtr = ptr; } void Camera::update(float duration, bool paused) { if (mAnimation->upperBodyReady()) { // Now process the view changes we queued earlier if (mVanityToggleQueued) { toggleVanityMode(!mVanity.enabled); mVanityToggleQueued = false; } if (mViewModeToggleQueued) { togglePreviewMode(false); toggleViewMode(); mViewModeToggleQueued = false; } } if (paused) return; // only show the crosshair in game mode and in first person mode. MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); wm->showCrosshair(!wm->isGuiMode() && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); if(mVanity.enabled) { rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true); } } void Camera::toggleViewMode(bool force) { // Changing the view will stop all playing animations, so if we are playing // anything important, queue the view change for later if (!mAnimation->upperBodyReady() && !force) { mViewModeToggleQueued = true; return; } else mViewModeToggleQueued = false; mFirstPersonView = !mFirstPersonView; processViewChange(); if (mFirstPersonView) { mCameraDistance = 0.f; } else { mCameraDistance = mMaxCameraDistance; } } void Camera::allowVanityMode(bool allow) { if (!allow && mVanity.enabled) toggleVanityMode(false); mVanity.allowed = allow; } bool Camera::toggleVanityMode(bool enable) { // Changing the view will stop all playing animations, so if we are playing // anything important, queue the view change for later if (isFirstPerson() && !mAnimation->upperBodyReady()) { mVanityToggleQueued = true; return false; } if(!mVanity.allowed && enable) return false; if(mVanity.enabled == enable) return true; mVanity.enabled = enable; processViewChange(); float offset = mPreviewCam.offset; if (mVanity.enabled) { setPitch(osg::DegreesToRadians(-30.f)); mMainCam.offset = mCameraDistance; } else { offset = mMainCam.offset; } mCameraDistance = offset; return true; } void Camera::togglePreviewMode(bool enable) { if (mFirstPersonView && !mAnimation->upperBodyReady()) return; if(mPreviewMode == enable) return; mPreviewMode = enable; processViewChange(); float offset = mCameraDistance; if (mPreviewMode) { mMainCam.offset = offset; offset = mPreviewCam.offset; } else { mPreviewCam.offset = offset; offset = mMainCam.offset; } mCameraDistance = offset; } void Camera::setSneakOffset(float offset) { mAnimation->setFirstPersonOffset(osg::Vec3f(0,0,-offset)); } float Camera::getYaw() { if(mVanity.enabled || mPreviewMode) return mPreviewCam.yaw; return mMainCam.yaw; } void Camera::setYaw(float angle) { if (angle > osg::PI) { angle -= osg::PI*2; } else if (angle < -osg::PI) { angle += osg::PI*2; } if (mVanity.enabled || mPreviewMode) { mPreviewCam.yaw = angle; } else { mMainCam.yaw = angle; } } float Camera::getPitch() { if (mVanity.enabled || mPreviewMode) { return mPreviewCam.pitch; } return mMainCam.pitch; } void Camera::setPitch(float angle) { const float epsilon = 0.000001f; float limit = osg::PI_2 - epsilon; if(mPreviewMode) limit /= 2; if(angle > limit) angle = limit; else if(angle < -limit) angle = -limit; if (mVanity.enabled || mPreviewMode) { mPreviewCam.pitch = angle; } else { mMainCam.pitch = angle; } } float Camera::getCameraDistance() const { return mCameraDistance; } void Camera::setCameraDistance(float dist, bool adjust, bool override) { if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) return; mIsNearest = false; if (adjust) dist += mCameraDistance; if (dist >= mFurthest) { dist = mFurthest; } else if (!override && dist < 10.f) { dist = 10.f; } else if (override && dist <= mNearest) { dist = mNearest; mIsNearest = true; } mCameraDistance = dist; if (override) { if (mVanity.enabled || mPreviewMode) { mPreviewCam.offset = mCameraDistance; } else if (!mFirstPersonView) { mMaxCameraDistance = mCameraDistance; } } else { mDistanceAdjusted = true; } } void Camera::setCameraDistance() { if (mDistanceAdjusted) { if (mVanity.enabled || mPreviewMode) { mCameraDistance = mPreviewCam.offset; } else if (!mFirstPersonView) { mCameraDistance = mMaxCameraDistance; } } mDistanceAdjusted = false; } void Camera::setAnimation(NpcAnimation *anim) { mAnimation = anim; processViewChange(); } void Camera::processViewChange() { if(isFirstPerson()) { mAnimation->setViewMode(NpcAnimation::VM_FirstPerson); mTrackingNode = mAnimation->getNode("Camera"); if (!mTrackingNode) mTrackingNode = mAnimation->getNode("Head"); mHeightScale = 1.f; } else { mAnimation->setViewMode(NpcAnimation::VM_Normal); SceneUtil::PositionAttitudeTransform* transform = mTrackingPtr.getRefData().getBaseNode(); mTrackingNode = transform; if (transform) mHeightScale = transform->getScale().z(); else mHeightScale = 1.f; } rotateCamera(getPitch(), getYaw(), false); } void Camera::getPosition(osg::Vec3f &focal, osg::Vec3f &camera) { focal = getFocalPoint(); osg::Quat orient = osg::Quat(getPitch(), osg::Vec3d(1,0,0)) * osg::Quat(getYaw(), osg::Vec3d(0,0,1)); osg::Vec3d offset = orient * osg::Vec3d(0, -mCameraDistance, 0); camera = focal + offset; } void Camera::togglePlayerLooking(bool enable) { mFreeLook = enable; } bool Camera::isVanityOrPreviewModeEnabled() { return mPreviewMode || mVanity.enabled; } bool Camera::isNearest() { return mIsNearest; } } openmw-openmw-0.38.0/apps/openmw/mwrender/camera.hpp000066400000000000000000000064071264522266000224620ustar00rootroot00000000000000#ifndef GAME_MWRENDER_CAMERA_H #define GAME_MWRENDER_CAMERA_H #include #include #include #include #include "../mwworld/ptr.hpp" namespace osg { class Camera; class NodeCallback; class Node; } namespace MWRender { class NpcAnimation; /// \brief Camera control class Camera { struct CamData { float pitch, yaw, offset; }; MWWorld::Ptr mTrackingPtr; osg::ref_ptr mTrackingNode; float mHeightScale; osg::ref_ptr mCamera; NpcAnimation *mAnimation; bool mFirstPersonView; bool mPreviewMode; bool mFreeLook; float mNearest; float mFurthest; bool mIsNearest; struct { bool enabled, allowed; } mVanity; float mHeight, mMaxCameraDistance; CamData mMainCam, mPreviewCam; bool mDistanceAdjusted; bool mVanityToggleQueued; bool mViewModeToggleQueued; float mCameraDistance; osg::ref_ptr mUpdateCallback; public: Camera(osg::Camera* camera); ~Camera(); MWWorld::Ptr getTrackingPtr() const; /// Update the view matrix of \a cam void updateCamera(osg::Camera* cam); /// Reset to defaults void reset(); /// Set where the camera is looking at. Uses Morrowind (euler) angles /// \param rot Rotation angles in radians void rotateCamera(float pitch, float yaw, bool adjust); float getYaw(); void setYaw(float angle); float getPitch(); void setPitch(float angle); /// Attach camera to object void attachTo(const MWWorld::Ptr &); /// @param Force view mode switch, even if currently not allowed by the animation. void toggleViewMode(bool force=false); bool toggleVanityMode(bool enable); void allowVanityMode(bool allow); /// @note this may be ignored if an important animation is currently playing void togglePreviewMode(bool enable); /// \brief Lowers the camera for sneak. void setSneakOffset(float offset); bool isFirstPerson() const { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } void processViewChange(); void update(float duration, bool paused=false); /// Set camera distance for current mode. Don't work on 1st person view. /// \param adjust Indicates should distance be adjusted or set. /// \param override If true new distance will be used as default. /// If false, default distance can be restored with setCameraDistance(). void setCameraDistance(float dist, bool adjust = false, bool override = true); /// Restore default camera distance for current mode. void setCameraDistance(); float getCameraDistance() const; void setAnimation(NpcAnimation *anim); osg::Vec3d getFocalPoint(); /// Stores focal and camera world positions in passed arguments void getPosition(osg::Vec3f &focal, osg::Vec3f &camera); void togglePlayerLooking(bool enable); bool isVanityOrPreviewModeEnabled(); bool isNearest(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/cell.hpp000066400000000000000000000017151264522266000221460ustar00rootroot00000000000000#ifndef GAME_RENDER_CELL_H #define GAME_RENDER_CELL_H #include namespace MWRender { class CellRender { public: virtual ~CellRender() {}; /// Make the cell visible. Load the cell if necessary. virtual void show() = 0; /// Remove the cell from rendering, but don't remove it from /// memory. virtual void hide() = 0; /// Destroy all rendering objects connected with this cell. virtual void destroy() = 0; /// Make the reference with the given handle visible. virtual void enable (const std::string& handle) = 0; /// Make the reference with the given handle invisible. virtual void disable (const std::string& handle) = 0; /// Remove the reference with the given handle permanently from the scene. virtual void deleteObject (const std::string& handle) = 0; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/characterpreview.cpp000066400000000000000000000335061264522266000245630ustar00rootroot00000000000000#include "characterpreview.hpp" #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/actorutil.hpp" #include "npcanimation.hpp" #include "vismask.hpp" namespace MWRender { class DrawOnceCallback : public osg::NodeCallback { public: DrawOnceCallback () : mRendered(false) , mLastRenderedFrame(0) { } virtual void operator () (osg::Node* node, osg::NodeVisitor* nv) { if (!mRendered) { mRendered = true; mLastRenderedFrame = nv->getTraversalNumber(); traverse(node, nv); } else { node->setNodeMask(0); } } void redrawNextFrame() { mRendered = false; } unsigned int getLastRenderedFrame() const { return mLastRenderedFrame; } private: bool mRendered; unsigned int mLastRenderedFrame; }; CharacterPreview::CharacterPreview(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem, MWWorld::Ptr character, int sizeX, int sizeY, const osg::Vec3f& position, const osg::Vec3f& lookAt) : mViewer(viewer) , mResourceSystem(resourceSystem) , mPosition(position) , mLookAt(lookAt) , mCharacter(character) , mAnimation(NULL) , mSizeX(sizeX) , mSizeY(sizeY) { mTexture = new osg::Texture2D; mTexture->setTextureSize(sizeX, sizeY); mTexture->setInternalFormat(GL_RGBA); mTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); mCamera = new osg::Camera; // hints that the camera is not relative to the master camera mCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); mCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); mCamera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 0.f)); mCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); const float fovYDegrees = 12.3f; mCamera->setProjectionMatrixAsPerspective(fovYDegrees, sizeX/static_cast(sizeY), 0.1f, 10000.f); // zNear and zFar are autocomputed mCamera->setViewport(0, 0, sizeX, sizeY); mCamera->setRenderOrder(osg::Camera::PRE_RENDER); mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture); mCamera->setGraphicsContext(mViewer->getCamera()->getGraphicsContext()); mCamera->setNodeMask(Mask_RenderToTexture); osg::ref_ptr lightManager = new SceneUtil::LightManager; lightManager->setStartLight(1); osg::ref_ptr stateset = new osg::StateSet; stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON); stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON); osg::ref_ptr lightmodel = new osg::LightModel; lightmodel->setAmbientIntensity(osg::Vec4(0.25, 0.25, 0.25, 1.0)); stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON); /// \todo Read the fallback values from INIImporter (Inventory:Directional*) ? osg::ref_ptr light = new osg::Light; light->setPosition(osg::Vec4(-0.3,0.3,0.7, 0.0)); light->setDiffuse(osg::Vec4(1,1,1,1)); light->setAmbient(osg::Vec4(0,0,0,1)); light->setSpecular(osg::Vec4(0,0,0,0)); light->setLightNum(0); light->setConstantAttenuation(1.f); light->setLinearAttenuation(0.f); light->setQuadraticAttenuation(0.f); osg::ref_ptr lightSource = new osg::LightSource; lightSource->setLight(light); lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON); lightManager->setStateSet(stateset); lightManager->addChild(lightSource); mCamera->addChild(lightManager); mNode = new osg::PositionAttitudeTransform; lightManager->addChild(mNode); mDrawOnceCallback = new DrawOnceCallback; mCamera->addUpdateCallback(mDrawOnceCallback); mViewer->getSceneData()->asGroup()->addChild(mCamera); mCharacter.mCell = NULL; } CharacterPreview::~CharacterPreview () { mViewer->getSceneData()->asGroup()->removeChild(mCamera); } int CharacterPreview::getTextureWidth() const { return mSizeX; } int CharacterPreview::getTextureHeight() const { return mSizeY; } void CharacterPreview::onSetup() { } osg::ref_ptr CharacterPreview::getTexture() { return mTexture; } void CharacterPreview::rebuild() { mAnimation.reset(NULL); mAnimation.reset(new NpcAnimation(mCharacter, mNode, mResourceSystem, true, true, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal))); onSetup(); redraw(); } void CharacterPreview::redraw() { mCamera->setNodeMask(~0); mDrawOnceCallback->redrawNextFrame(); } // -------------------------------------------------------------------------------------------------- InventoryPreview::InventoryPreview(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem, MWWorld::Ptr character) : CharacterPreview(viewer, resourceSystem, character, 512, 1024, osg::Vec3f(0, 700, 71), osg::Vec3f(0,0,71)) { } void InventoryPreview::setViewport(int sizeX, int sizeY) { sizeX = std::max(sizeX, 0); sizeY = std::max(sizeY, 0); mCamera->setViewport(0, 0, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY)); redraw(); } void InventoryPreview::update() { if (!mAnimation.get()) return; mAnimation->showWeapons(true); mAnimation->updateParts(); MWWorld::InventoryStore &inv = mCharacter.getClass().getInventoryStore(mCharacter); MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); std::string groupname; bool showCarriedLeft = true; if(iter == inv.end()) groupname = "inventoryhandtohand"; else { const std::string &type = iter->getTypeName(); if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) groupname = "inventoryweapononehand"; else if(type == typeid(ESM::Weapon).name()) { MWWorld::LiveCellRef *ref = iter->get(); int type = ref->mBase->mData.mType; if(type == ESM::Weapon::ShortBladeOneHand || type == ESM::Weapon::LongBladeOneHand || type == ESM::Weapon::BluntOneHand || type == ESM::Weapon::AxeOneHand || type == ESM::Weapon::MarksmanThrown || type == ESM::Weapon::MarksmanCrossbow || type == ESM::Weapon::MarksmanBow) groupname = "inventoryweapononehand"; else if(type == ESM::Weapon::LongBladeTwoHand || type == ESM::Weapon::BluntTwoClose || type == ESM::Weapon::AxeTwoHand) groupname = "inventoryweapontwohand"; else if(type == ESM::Weapon::BluntTwoWide || type == ESM::Weapon::SpearTwoWide) groupname = "inventoryweapontwowide"; else groupname = "inventoryhandtohand"; showCarriedLeft = (iter->getClass().canBeEquipped(*iter, mCharacter).first != 2); } else groupname = "inventoryhandtohand"; } mAnimation->showCarriedLeft(showCarriedLeft); mCurrentAnimGroup = groupname; mAnimation->play(mCurrentAnimGroup, 1, Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0); MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && showCarriedLeft) { if(!mAnimation->getInfo("torch")) mAnimation->play("torch", 2, Animation::BlendMask_LeftArm, false, 1.0f, "start", "stop", 0.0f, ~0ul, true); } else if(mAnimation->getInfo("torch")) mAnimation->disable("torch"); mAnimation->runAnimation(0.0f); redraw(); } int InventoryPreview::getSlotSelected (int posX, int posY) { float projX = (posX / mCamera->getViewport()->width()) * 2 - 1.f; float projY = (posY / mCamera->getViewport()->height()) * 2 - 1.f; // With Intersector::WINDOW, the intersection ratios are slightly inaccurate. Seems to be a // precision issue - compiling with OSG_USE_FLOAT_MATRIX=0, Intersector::WINDOW works ok. // Using Intersector::PROJECTION results in better precision because the start/end points and the model matrices // don't go through as many transformations. osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector(osgUtil::Intersector::PROJECTION, projX, projY)); intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST); osgUtil::IntersectionVisitor visitor(intersector); visitor.setTraversalMode(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN); // Set the traversal number from the last draw, so that the frame switch used for RigGeometry double buffering works correctly visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame()); osg::Node::NodeMask nodeMask = mCamera->getNodeMask(); mCamera->setNodeMask(~0); mCamera->accept(visitor); mCamera->setNodeMask(nodeMask); if (intersector->containsIntersections()) { osgUtil::LineSegmentIntersector::Intersection intersection = intersector->getFirstIntersection(); return mAnimation->getSlot(intersection.nodePath); } return -1; } void InventoryPreview::updatePtr(const MWWorld::Ptr &ptr) { mCharacter = MWWorld::Ptr(ptr.getBase(), NULL); } void InventoryPreview::onSetup() { osg::Vec3f scale (1.f, 1.f, 1.f); mCharacter.getClass().adjustScale(mCharacter, scale, true); mNode->setScale(scale); mCamera->setViewMatrixAsLookAt(mPosition * scale.z(), mLookAt * scale.z(), osg::Vec3f(0,0,1)); } // -------------------------------------------------------------------------------------------------- RaceSelectionPreview::RaceSelectionPreview(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem) : CharacterPreview(viewer, resourceSystem, MWMechanics::getPlayer(), 512, 512, osg::Vec3f(0, 125, 8), osg::Vec3f(0,0,8)) , mBase (*mCharacter.get()->mBase) , mRef(&mBase) , mPitchRadians(osg::DegreesToRadians(6.f)) { mCharacter = MWWorld::Ptr(&mRef, NULL); } RaceSelectionPreview::~RaceSelectionPreview() { } void RaceSelectionPreview::setAngle(float angleRadians) { mNode->setAttitude(osg::Quat(mPitchRadians, osg::Vec3(1,0,0)) * osg::Quat(angleRadians, osg::Vec3(0,0,1))); redraw(); } void RaceSelectionPreview::setPrototype(const ESM::NPC &proto) { mBase = proto; mBase.mId = "player"; rebuild(); } class UpdateCameraCallback : public osg::NodeCallback { public: UpdateCameraCallback(osg::ref_ptr nodeToFollow, const osg::Vec3& posOffset, const osg::Vec3& lookAtOffset) : mNodeToFollow(nodeToFollow) , mPosOffset(posOffset) , mLookAtOffset(lookAtOffset) { } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::Camera* cam = static_cast(node); // Update keyframe controllers in the scene graph first... traverse(node, nv); // Now update camera utilizing the updated head position osg::MatrixList mats = mNodeToFollow->getWorldMatrices(); if (!mats.size()) return; osg::Matrix worldMat = mats[0]; osg::Vec3 headOffset = worldMat.getTrans(); cam->setViewMatrixAsLookAt(headOffset + mPosOffset, headOffset + mLookAtOffset, osg::Vec3(0,0,1)); } private: osg::ref_ptr mNodeToFollow; osg::Vec3 mPosOffset; osg::Vec3 mLookAtOffset; }; void RaceSelectionPreview::onSetup () { mAnimation->play("idle", 1, Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->runAnimation(0.f); // attach camera to follow the head node if (mUpdateCameraCallback) mCamera->removeUpdateCallback(mUpdateCameraCallback); const osg::Node* head = mAnimation->getNode("Bip01 Head"); if (head) { mUpdateCameraCallback = new UpdateCameraCallback(head, mPosition, mLookAt); mCamera->addUpdateCallback(mUpdateCameraCallback); } else std::cerr << "Error: Bip01 Head node not found" << std::endl; } } openmw-openmw-0.38.0/apps/openmw/mwrender/characterpreview.hpp000066400000000000000000000055741264522266000245740ustar00rootroot00000000000000#ifndef MWRENDER_CHARACTERPREVIEW_H #define MWRENDER_CHARACTERPREVIEW_H #include #include #include #include #include #include "../mwworld/ptr.hpp" namespace osg { class Texture2D; class Camera; } namespace osgViewer { class Viewer; } namespace MWRender { class NpcAnimation; class DrawOnceCallback; class CharacterPreview { public: CharacterPreview(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem, MWWorld::Ptr character, int sizeX, int sizeY, const osg::Vec3f& position, const osg::Vec3f& lookAt); virtual ~CharacterPreview(); int getTextureWidth() const; int getTextureHeight() const; void redraw(); void rebuild(); osg::ref_ptr getTexture(); private: CharacterPreview(const CharacterPreview&); CharacterPreview& operator=(const CharacterPreview&); protected: virtual bool renderHeadOnly() { return false; } virtual void onSetup(); osg::ref_ptr mViewer; Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mTexture; osg::ref_ptr mCamera; osg::ref_ptr mDrawOnceCallback; osg::Vec3f mPosition; osg::Vec3f mLookAt; MWWorld::Ptr mCharacter; std::auto_ptr mAnimation; osg::ref_ptr mNode; std::string mCurrentAnimGroup; int mSizeX; int mSizeY; }; class InventoryPreview : public CharacterPreview { public: InventoryPreview(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem, MWWorld::Ptr character); void updatePtr(const MWWorld::Ptr& ptr); void update(); // Render preview again, e.g. after changed equipment void setViewport(int sizeX, int sizeY); int getSlotSelected(int posX, int posY); protected: virtual void onSetup(); }; class UpdateCameraCallback; class RaceSelectionPreview : public CharacterPreview { ESM::NPC mBase; MWWorld::LiveCellRef mRef; protected: virtual bool renderHeadOnly() { return true; } virtual void onSetup(); public: RaceSelectionPreview(osgViewer::Viewer* viewer, Resource::ResourceSystem* resourceSystem); virtual ~RaceSelectionPreview(); void setAngle(float angleRadians); const ESM::NPC &getPrototype() const { return mBase; } void setPrototype(const ESM::NPC &proto); private: osg::ref_ptr mUpdateCameraCallback; float mPitchRadians; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/creatureanimation.cpp000066400000000000000000000124001264522266000247250ustar00rootroot00000000000000#include "creatureanimation.hpp" #include #include #include #include #include #include #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" namespace MWRender { CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem) : Animation(ptr, osg::ref_ptr(ptr.getRefData().getBaseNode()), resourceSystem) { MWWorld::LiveCellRef *ref = mPtr.get(); if(!model.empty()) { setObjectRoot(model, false, false, true); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\xbase_anim.nif"); addAnimSource(model); } } CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem) : Animation(ptr, osg::ref_ptr(ptr.getRefData().getBaseNode()), resourceSystem) , mShowWeapons(false) , mShowCarriedLeft(false) { MWWorld::LiveCellRef *ref = mPtr.get(); if(!model.empty()) { setObjectRoot(model, true, false, true); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\xbase_anim.nif"); addAnimSource(model); mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr); updateParts(); } mWeaponAnimationTime = boost::shared_ptr(new WeaponAnimationTime(this)); } void CreatureWeaponAnimation::showWeapons(bool showWeapon) { if (showWeapon != mShowWeapons) { mShowWeapons = showWeapon; updateParts(); } } void CreatureWeaponAnimation::showCarriedLeft(bool show) { if (show != mShowCarriedLeft) { mShowCarriedLeft = show; updateParts(); } } void CreatureWeaponAnimation::updateParts() { mWeapon.reset(); mShield.reset(); if (mShowWeapons) updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight); if (mShowCarriedLeft) updatePart(mShield, MWWorld::InventoryStore::Slot_CarriedLeft); } void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot) { if (!mObjectRoot) return; MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); MWWorld::ContainerStoreIterator it = inv.getSlot(slot); if (it == inv.end()) { scene.reset(); return; } MWWorld::Ptr item = *it; std::string bonename; if (slot == MWWorld::InventoryStore::Slot_CarriedRight) bonename = "Weapon Bone"; else bonename = "Shield Bone"; osg::ref_ptr node = mResourceSystem->getSceneManager()->createInstance(item.getClass().getModel(item)); osg::ref_ptr attached = SceneUtil::attach(node, mObjectRoot, bonename, bonename); mResourceSystem->getSceneManager()->notifyAttached(attached); scene.reset(new PartHolder(attached)); if (!item.getClass().getEnchantment(item).empty()) addGlow(attached, getEnchantmentColor(item)); // Crossbows start out with a bolt attached // FIXME: code duplicated from NpcAnimation if (slot == MWWorld::InventoryStore::Slot_CarriedRight && item.getTypeName() == typeid(ESM::Weapon).name() && item.get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt) attachArrow(); else mAmmunition.reset(); } else mAmmunition.reset(); boost::shared_ptr source; if (slot == MWWorld::InventoryStore::Slot_CarriedRight) source = mWeaponAnimationTime; else source.reset(new NullAnimationTime); SceneUtil::AssignControllerSourcesVisitor assignVisitor(source); attached->accept(assignVisitor); } void CreatureWeaponAnimation::attachArrow() { WeaponAnimation::attachArrow(mPtr); } void CreatureWeaponAnimation::releaseArrow(float attackStrength) { WeaponAnimation::releaseArrow(mPtr, attackStrength); } osg::Group *CreatureWeaponAnimation::getArrowBone() { if (!mWeapon) return NULL; SceneUtil::FindByNameVisitor findVisitor ("ArrowBone"); mWeapon->getNode()->accept(findVisitor); return findVisitor.mFoundNode; } osg::Node *CreatureWeaponAnimation::getWeaponNode() { return mWeapon ? mWeapon->getNode().get() : NULL; } Resource::ResourceSystem *CreatureWeaponAnimation::getResourceSystem() { return mResourceSystem; } void CreatureWeaponAnimation::addControllers() { Animation::addControllers(); WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get()); } osg::Vec3f CreatureWeaponAnimation::runAnimation(float duration) { osg::Vec3f ret = Animation::runAnimation(duration); WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0]); return ret; } } openmw-openmw-0.38.0/apps/openmw/mwrender/creatureanimation.hpp000066400000000000000000000042771264522266000247470ustar00rootroot00000000000000#ifndef GAME_RENDER_CREATUREANIMATION_H #define GAME_RENDER_CREATUREANIMATION_H #include "animation.hpp" #include "weaponanimation.hpp" #include "../mwworld/inventorystore.hpp" namespace MWWorld { class Ptr; } namespace MWRender { class CreatureAnimation : public Animation { public: CreatureAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem); virtual ~CreatureAnimation() {} }; // For creatures with weapons and shields // Animation is already virtual anyway, so might as well make a separate class. // Most creatures don't need weapons/shields, so this will save some memory. class CreatureWeaponAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener { public: CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem); virtual ~CreatureWeaponAnimation() {} virtual void equipmentChanged() { updateParts(); } virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool show); void updateParts(); void updatePart(PartHolderPtr& scene, int slot); virtual void attachArrow(); virtual void releaseArrow(float attackStrength); // WeaponAnimation virtual osg::Group* getArrowBone(); virtual osg::Node* getWeaponNode(); virtual Resource::ResourceSystem* getResourceSystem(); virtual void showWeapon(bool show) { showWeapons(show); } virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); } virtual void addControllers(); virtual osg::Vec3f runAnimation(float duration); /// A relative factor (0-1) that decides if and how much the skeleton should be pitched /// to indicate the facing orientation of the character. virtual void setPitchFactor(float factor) { mPitchFactor = factor; } private: PartHolderPtr mWeapon; PartHolderPtr mShield; bool mShowWeapons; bool mShowCarriedLeft; boost::shared_ptr mWeaponAnimationTime; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/effectmanager.cpp000066400000000000000000000041571264522266000240140ustar00rootroot00000000000000#include "effectmanager.hpp" #include #include #include #include #include "animation.hpp" #include "vismask.hpp" #include "util.hpp" namespace MWRender { EffectManager::EffectManager(osg::ref_ptr parent, Resource::ResourceSystem* resourceSystem) : mParentNode(parent) , mResourceSystem(resourceSystem) { } EffectManager::~EffectManager() { clear(); } void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale) { osg::ref_ptr node = mResourceSystem->getSceneManager()->createInstance(model); node->setNodeMask(Mask_Effect); Effect effect; effect.mAnimTime.reset(new EffectAnimationTime); SceneUtil::FindMaxControllerLengthVisitor findMaxLengthVisitor; node->accept(findMaxLengthVisitor); effect.mMaxControllerLength = findMaxLengthVisitor.getMaxLength(); osg::ref_ptr trans = new osg::PositionAttitudeTransform; trans->setPosition(worldPosition); trans->setScale(osg::Vec3f(scale, scale, scale)); trans->addChild(node); SceneUtil::AssignControllerSourcesVisitor assignVisitor(effect.mAnimTime); node->accept(assignVisitor); overrideTexture(textureOverride, mResourceSystem, node); mParentNode->addChild(trans); mResourceSystem->getSceneManager()->notifyAttached(node); mEffects[trans] = effect; } void EffectManager::update(float dt) { for (EffectMap::iterator it = mEffects.begin(); it != mEffects.end(); ) { it->second.mAnimTime->addTime(dt); if (it->second.mAnimTime->getTime() >= it->second.mMaxControllerLength) { mParentNode->removeChild(it->first); mEffects.erase(it++); } else ++it; } } void EffectManager::clear() { for (EffectMap::iterator it = mEffects.begin(); it != mEffects.end(); ++it) { mParentNode->removeChild(it->first); } mEffects.clear(); } } openmw-openmw-0.38.0/apps/openmw/mwrender/effectmanager.hpp000066400000000000000000000027631264522266000240220ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_EFFECTMANAGER_H #define OPENMW_MWRENDER_EFFECTMANAGER_H #include #include #include #include namespace osg { class Group; class Vec3f; class PositionAttitudeTransform; } namespace Resource { class ResourceSystem; } namespace MWRender { class EffectAnimationTime; // Note: effects attached to another object should be managed by MWRender::Animation::addEffect. // This class manages "free" effects, i.e. attached to a dedicated scene node in the world. class EffectManager { public: EffectManager(osg::ref_ptr parent, Resource::ResourceSystem* resourceSystem); ~EffectManager(); /// Add an effect. When it's finished playing, it will be removed automatically. void addEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPosition, float scale); void update(float dt); /// Remove all effects void clear(); private: struct Effect { float mMaxControllerLength; boost::shared_ptr mAnimTime; }; typedef std::map, Effect> EffectMap; EffectMap mEffects; osg::ref_ptr mParentNode; Resource::ResourceSystem* mResourceSystem; EffectManager(const EffectManager&); void operator=(const EffectManager&); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/globalmap.cpp000066400000000000000000000464711264522266000231700ustar00rootroot00000000000000#include "globalmap.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" #include "vismask.hpp" namespace { // Create a screen-aligned quad with given texture coordinates. // Assumes a top-left origin of the sampled image. osg::ref_ptr createTexturedQuad(float leftTexCoord, float topTexCoord, float rightTexCoord, float bottomTexCoord) { osg::ref_ptr geom = new osg::Geometry; osg::ref_ptr verts = new osg::Vec3Array; verts->push_back(osg::Vec3f(-1, -1, 0)); verts->push_back(osg::Vec3f(-1, 1, 0)); verts->push_back(osg::Vec3f(1, 1, 0)); verts->push_back(osg::Vec3f(1, -1, 0)); geom->setVertexArray(verts); osg::ref_ptr texcoords = new osg::Vec2Array; texcoords->push_back(osg::Vec2f(leftTexCoord, 1.f-bottomTexCoord)); texcoords->push_back(osg::Vec2f(leftTexCoord, 1.f-topTexCoord)); texcoords->push_back(osg::Vec2f(rightTexCoord, 1.f-topTexCoord)); texcoords->push_back(osg::Vec2f(rightTexCoord, 1.f-bottomTexCoord)); osg::ref_ptr colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.f, 1.f, 1.f, 1.f)); geom->setColorArray(colors, osg::Array::BIND_OVERALL); geom->setTexCoordArray(0, texcoords, osg::Array::BIND_PER_VERTEX); geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4)); return geom; } class CameraUpdateGlobalCallback : public osg::NodeCallback { public: CameraUpdateGlobalCallback(MWRender::GlobalMap* parent) : mRendered(false) , mParent(parent) { } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { if (mRendered) { node->setNodeMask(0); return; } traverse(node, nv); if (!mRendered) { mRendered = true; mParent->markForRemoval(static_cast(node)); } } private: bool mRendered; MWRender::GlobalMap* mParent; }; } namespace MWRender { GlobalMap::GlobalMap(osg::Group* root) : mRoot(root) , mWidth(0) , mHeight(0) , mMinX(0), mMaxX(0) , mMinY(0), mMaxY(0) { mCellSize = Settings::Manager::getInt("global map cell size", "Map"); } GlobalMap::~GlobalMap() { } void GlobalMap::render (Loading::Listener* loadingListener) { const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); // get the size of the world MWWorld::Store::iterator it = esmStore.get().extBegin(); for (; it != esmStore.get().extEnd(); ++it) { if (it->getGridX() < mMinX) mMinX = it->getGridX(); if (it->getGridX() > mMaxX) mMaxX = it->getGridX(); if (it->getGridY() < mMinY) mMinY = it->getGridY(); if (it->getGridY() > mMaxY) mMaxY = it->getGridY(); } mWidth = mCellSize*(mMaxX-mMinX+1); mHeight = mCellSize*(mMaxY-mMinY+1); loadingListener->loadingOn(); loadingListener->setLabel("Creating map"); loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); loadingListener->setProgress(0); osg::ref_ptr image = new osg::Image; image->allocateImage(mWidth, mHeight, 1, GL_RGB, GL_UNSIGNED_BYTE); unsigned char* data = image->data(); for (int x = mMinX; x <= mMaxX; ++x) { for (int y = mMinY; y <= mMaxY; ++y) { ESM::Land* land = esmStore.get().search (x,y); if (land) { int mask = ESM::Land::DATA_WNAM; if (!land->isDataLoaded(mask)) land->loadData(mask); } const ESM::Land::LandData *landData = land ? land->getLandData (ESM::Land::DATA_WNAM) : 0; for (int cellY=0; cellY(float(cellX)/float(mCellSize) * 9); int vertexY = static_cast(float(cellY) / float(mCellSize) * 9); int texelX = (x-mMinX) * mCellSize + cellX; int texelY = (y-mMinY) * mCellSize + cellY; unsigned char r,g,b; float y = 0; if (landData) y = (landData->mWnam[vertexY * 9 + vertexX] << 4) / 2048.f; else y = (SCHAR_MIN << 4) / 2048.f; if (y < 0) { r = static_cast(14 * y + 38); g = static_cast(20 * y + 56); b = static_cast(18 * y + 51); } else if (y < 0.3f) { if (y < 0.1f) y *= 8.f; else { y -= 0.1f; y += 0.8f; } r = static_cast(66 - 32 * y); g = static_cast(48 - 23 * y); b = static_cast(33 - 16 * y); } else { y -= 0.3f; y *= 1.428f; r = static_cast(34 - 29 * y); g = static_cast(25 - 20 * y); b = static_cast(17 - 12 * y); } data[texelY * mWidth * 3 + texelX * 3] = r; data[texelY * mWidth * 3 + texelX * 3+1] = g; data[texelY * mWidth * 3 + texelX * 3+2] = b; } } loadingListener->increaseProgress(); if (land) land->unloadData(); } } mBaseTexture = new osg::Texture2D; mBaseTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mBaseTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); mBaseTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mBaseTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); mBaseTexture->setImage(image); mBaseTexture->setResizeNonPowerOfTwoHint(false); clear(); loadingListener->loadingOff(); } void GlobalMap::worldPosToImageSpace(float x, float z, float& imageX, float& imageY) { imageX = float(x / 8192.f - mMinX) / (mMaxX - mMinX + 1); imageY = 1.f-float(z / 8192.f - mMinY) / (mMaxY - mMinY + 1); } void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY) { imageX = float(x - mMinX) / (mMaxX - mMinX + 1); // NB y + 1, because we want the top left corner, not bottom left where the origin of the cell is imageY = 1.f-float(y - mMinY + 1) / (mMaxY - mMinY + 1); } void GlobalMap::requestOverlayTextureUpdate(int x, int y, int width, int height, osg::ref_ptr texture, bool clear, bool cpuCopy, float srcLeft, float srcTop, float srcRight, float srcBottom) { osg::ref_ptr camera (new osg::Camera); camera->setNodeMask(Mask_RenderToTexture); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setViewMatrix(osg::Matrix::identity()); camera->setProjectionMatrix(osg::Matrix::identity()); camera->setProjectionResizePolicy(osg::Camera::FIXED); camera->setRenderOrder(osg::Camera::PRE_RENDER); y = mHeight - y - height; // convert top-left origin to bottom-left camera->setViewport(x, y, width, height); if (clear) { camera->setClearMask(GL_COLOR_BUFFER_BIT); camera->setClearColor(osg::Vec4(0,0,0,0)); } else camera->setClearMask(GL_NONE); camera->setUpdateCallback(new CameraUpdateGlobalCallback(this)); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); camera->attach(osg::Camera::COLOR_BUFFER, mOverlayTexture); // no need for a depth buffer camera->setImplicitBufferAttachmentMask(osg::DisplaySettings::IMPLICIT_COLOR_BUFFER_ATTACHMENT); if (cpuCopy) { // Attach an image to copy the render back to the CPU when finished osg::ref_ptr image (new osg::Image); image->setPixelFormat(mOverlayImage->getPixelFormat()); image->setDataType(mOverlayImage->getDataType()); camera->attach(osg::Camera::COLOR_BUFFER, image); ImageDest imageDest; imageDest.mImage = image; imageDest.mX = x; imageDest.mY = y; mPendingImageDest.push_back(imageDest); } // Create a quad rendering the updated texture if (texture) { osg::ref_ptr geom = createTexturedQuad(srcLeft, srcTop, srcRight, srcBottom); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(0); osg::StateSet* stateset = geom->getOrCreateStateSet(); stateset->setAttribute(depth); stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); osg::ref_ptr geode = new osg::Geode; geode->addDrawable(geom); camera->addChild(geode); } mRoot->addChild(camera); mActiveCameras.push_back(camera); } void GlobalMap::exploreCell(int cellX, int cellY, osg::ref_ptr localMapTexture) { if (!localMapTexture) return; int originX = (cellX - mMinX) * mCellSize; int originY = (cellY - mMinY + 1) * mCellSize; // +1 because we want the top left corner of the cell, not the bottom left if (cellX > mMaxX || cellX < mMinX || cellY > mMaxY || cellY < mMinY) return; requestOverlayTextureUpdate(originX, mHeight - originY, mCellSize, mCellSize, localMapTexture, false, true); } void GlobalMap::clear() { if (!mOverlayImage) { mOverlayImage = new osg::Image; mOverlayImage->allocateImage(mWidth, mHeight, 1, GL_RGBA, GL_UNSIGNED_BYTE); assert(mOverlayImage->isDataContiguous()); } memset(mOverlayImage->data(), 0, mOverlayImage->getTotalSizeInBytes()); if (!mOverlayTexture) { mOverlayTexture = new osg::Texture2D; mOverlayTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mOverlayTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); mOverlayTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mOverlayTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); mOverlayTexture->setResizeNonPowerOfTwoHint(false); mOverlayTexture->setInternalFormat(GL_RGBA); mOverlayTexture->setTextureSize(mWidth, mHeight); } mPendingImageDest.clear(); // just push a Camera to clear the FBO, instead of setImage()/dirty() // easier, since we don't need to worry about synchronizing access :) requestOverlayTextureUpdate(0, 0, mWidth, mHeight, osg::ref_ptr(), true, false); } void GlobalMap::write(ESM::GlobalMap& map) { map.mBounds.mMinX = mMinX; map.mBounds.mMaxX = mMaxX; map.mBounds.mMinY = mMinY; map.mBounds.mMaxY = mMaxY; std::ostringstream ostream; osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) { std::cerr << "Can't write map overlay: no png readerwriter found" << std::endl; return; } osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*mOverlayImage, ostream); if (!result.success()) { std::cerr << "Can't write map overlay: " << result.message() << " code " << result.status() << std::endl; return; } std::string data = ostream.str(); map.mImageData = std::vector(data.begin(), data.end()); } struct Box { int mLeft, mTop, mRight, mBottom; Box(int left, int top, int right, int bottom) : mLeft(left), mTop(top), mRight(right), mBottom(bottom) { } bool operator == (const Box& other) { return mLeft == other.mLeft && mTop == other.mTop && mRight == other.mRight && mBottom == other.mBottom; } }; void GlobalMap::read(ESM::GlobalMap& map) { const ESM::GlobalMap::Bounds& bounds = map.mBounds; if (bounds.mMaxX-bounds.mMinX < 0) return; if (bounds.mMaxY-bounds.mMinY < 0) return; if (bounds.mMinX > bounds.mMaxX || bounds.mMinY > bounds.mMaxY) throw std::runtime_error("invalid map bounds"); if (!map.mImageData.size()) return; Files::IMemStream istream(&map.mImageData[0], map.mImageData.size()); osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) { std::cerr << "Can't read map overlay: no png readerwriter found" << std::endl; return; } osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(istream); if (!result.success()) { std::cerr << "Can't read map overlay: " << result.message() << " code " << result.status() << std::endl; return; } osg::ref_ptr image = result.getImage(); int imageWidth = image->s(); int imageHeight = image->t(); int xLength = (bounds.mMaxX-bounds.mMinX+1); int yLength = (bounds.mMaxY-bounds.mMinY+1); // Size of one cell in image space int cellImageSizeSrc = imageWidth / xLength; if (int(imageHeight / yLength) != cellImageSizeSrc) throw std::runtime_error("cell size must be quadratic"); // If cell bounds of the currently loaded content and the loaded savegame do not match, // we need to resize source/dest boxes to accommodate // This means nonexisting cells will be dropped silently int cellImageSizeDst = mCellSize; // Completely off-screen? -> no need to blit anything if (bounds.mMaxX < mMinX || bounds.mMaxY < mMinY || bounds.mMinX > mMaxX || bounds.mMinY > mMaxY) return; int leftDiff = (mMinX - bounds.mMinX); int topDiff = (bounds.mMaxY - mMaxY); int rightDiff = (bounds.mMaxX - mMaxX); int bottomDiff = (mMinY - bounds.mMinY); Box srcBox ( std::max(0, leftDiff * cellImageSizeSrc), std::max(0, topDiff * cellImageSizeSrc), std::min(imageWidth, imageWidth - rightDiff * cellImageSizeSrc), std::min(imageHeight, imageHeight - bottomDiff * cellImageSizeSrc)); Box destBox ( std::max(0, -leftDiff * cellImageSizeDst), std::max(0, -topDiff * cellImageSizeDst), std::min(mWidth, mWidth + rightDiff * cellImageSizeDst), std::min(mHeight, mHeight + bottomDiff * cellImageSizeDst)); osg::ref_ptr texture (new osg::Texture2D); texture->setImage(image); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture->setResizeNonPowerOfTwoHint(false); if (srcBox == destBox && imageWidth == mWidth && imageHeight == mHeight) { mOverlayImage->copySubImage(0, 0, 0, image); requestOverlayTextureUpdate(0, 0, mWidth, mHeight, texture, true, false); } else { // Dimensions don't match. This could mean a changed map region, or a changed map resolution. // In the latter case, we'll want filtering. // Create a RTT Camera and draw the image onto mOverlayImage in the next frame. requestOverlayTextureUpdate(destBox.mLeft, destBox.mTop, destBox.mRight-destBox.mLeft, destBox.mBottom-destBox.mTop, texture, true, true, srcBox.mLeft/float(imageWidth), srcBox.mTop/float(imageHeight), srcBox.mRight/float(imageWidth), srcBox.mBottom/float(imageHeight)); } } osg::ref_ptr GlobalMap::getBaseTexture() { return mBaseTexture; } osg::ref_ptr GlobalMap::getOverlayTexture() { return mOverlayTexture; } void GlobalMap::markForRemoval(osg::Camera *camera) { CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), camera); if (found == mActiveCameras.end()) { std::cerr << "GlobalMap trying to remove an inactive camera" << std::endl; return; } mActiveCameras.erase(found); mCamerasPendingRemoval.push_back(camera); } void GlobalMap::cleanupCameras() { for (CameraVector::iterator it = mCamerasPendingRemoval.begin(); it != mCamerasPendingRemoval.end(); ++it) mRoot->removeChild(*it); mCamerasPendingRemoval.clear(); for (ImageDestVector::iterator it = mPendingImageDest.begin(); it != mPendingImageDest.end();) { ImageDest& imageDest = *it; if (--imageDest.mFramesUntilDone > 0) { ++it; continue; } mOverlayImage->copySubImage(imageDest.mX, imageDest.mY, 0, imageDest.mImage); it = mPendingImageDest.erase(it); } } } openmw-openmw-0.38.0/apps/openmw/mwrender/globalmap.hpp000066400000000000000000000064421264522266000231670ustar00rootroot00000000000000#ifndef GAME_RENDER_GLOBALMAP_H #define GAME_RENDER_GLOBALMAP_H #include #include #include namespace osg { class Texture2D; class Image; class Group; class Camera; } namespace Loading { class Listener; } namespace ESM { struct GlobalMap; } namespace MWRender { class GlobalMap { public: GlobalMap(osg::Group* root); ~GlobalMap(); void render(Loading::Listener* loadingListener); int getWidth() const { return mWidth; } int getHeight() const { return mHeight; } int getCellSize() const { return mCellSize; } void worldPosToImageSpace(float x, float z, float& imageX, float& imageY); void cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY); void exploreCell (int cellX, int cellY, osg::ref_ptr localMapTexture); /// Clears the overlay void clear(); /** * Removes cameras that have already been rendered. Should be called every frame to ensure that * we do not render the same map more than once. Note, this cleanup is difficult to implement in an * automated fashion, since we can't alter the scene graph structure from within an update callback. */ void cleanupCameras(); /** * Mark a camera for cleanup in the next update. For internal use only. */ void markForRemoval(osg::Camera* camera); void write (ESM::GlobalMap& map); void read (ESM::GlobalMap& map); osg::ref_ptr getBaseTexture(); osg::ref_ptr getOverlayTexture(); private: /** * Request rendering a 2d quad onto mOverlayTexture. * x, y, width and height are the destination coordinates (top-left coordinate origin) * @param cpuCopy copy the resulting render onto mOverlayImage as well? */ void requestOverlayTextureUpdate(int x, int y, int width, int height, osg::ref_ptr texture, bool clear, bool cpuCopy, float srcLeft = 0.f, float srcTop = 0.f, float srcRight = 1.f, float srcBottom = 1.f); int mCellSize; osg::ref_ptr mRoot; typedef std::vector > CameraVector; CameraVector mActiveCameras; CameraVector mCamerasPendingRemoval; struct ImageDest { ImageDest() : mX(0), mY(0) , mFramesUntilDone(3) // wait an extra frame to ensure the draw thread has completed its frame. { } osg::ref_ptr mImage; int mX, mY; int mFramesUntilDone; }; typedef std::vector ImageDestVector; ImageDestVector mPendingImageDest; std::vector< std::pair > mExploredCells; osg::ref_ptr mBaseTexture; // GPU copy of overlay // Note, uploads are pushed through a Camera, instead of through mOverlayImage osg::ref_ptr mOverlayTexture; // CPU copy of overlay osg::ref_ptr mOverlayImage; int mWidth; int mHeight; int mMinX, mMaxX, mMinY, mMaxY; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/localmap.cpp000066400000000000000000000567341264522266000230250ustar00rootroot00000000000000#include "localmap.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/cellstore.hpp" #include "vismask.hpp" namespace { class CameraLocalUpdateCallback : public osg::NodeCallback { public: CameraLocalUpdateCallback(MWRender::LocalMap* parent) : mRendered(false) , mParent(parent) { } virtual void operator()(osg::Node* node, osg::NodeVisitor*) { if (mRendered) node->setNodeMask(0); if (!mRendered) { mRendered = true; mParent->markForRemoval(static_cast(node)); } // Note, we intentionally do not traverse children here. The map camera's scene data is the same as the master camera's, // so it has been updated already. //traverse(node, nv); } private: bool mRendered; MWRender::LocalMap* mParent; }; float square(float val) { return val*val; } } namespace MWRender { LocalMap::LocalMap(osgViewer::Viewer* viewer) : mViewer(viewer) , mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) , mMapWorldSize(8192.f) , mAngle(0.f) , mInterior(false) { mRoot = mViewer->getSceneData()->asGroup(); SceneUtil::FindByNameVisitor find("Scene Root"); mRoot->accept(find); mSceneRoot = find.mFoundNode; if (!mSceneRoot) throw std::runtime_error("no scene root found"); } LocalMap::~LocalMap() { for (CameraVector::iterator it = mActiveCameras.begin(); it != mActiveCameras.end(); ++it) mRoot->removeChild(*it); for (CameraVector::iterator it = mCamerasPendingRemoval.begin(); it != mCamerasPendingRemoval.end(); ++it) mRoot->removeChild(*it); } const osg::Vec2f LocalMap::rotatePoint(const osg::Vec2f& point, const osg::Vec2f& center, const float angle) { return osg::Vec2f( std::cos(angle) * (point.x() - center.x()) - std::sin(angle) * (point.y() - center.y()) + center.x(), std::sin(angle) * (point.x() - center.x()) + std::cos(angle) * (point.y() - center.y()) + center.y()); } void LocalMap::clear() { mSegments.clear(); } void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) { if (!mInterior) { const MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; if (segment.mFogOfWarImage && segment.mHasFogState) { std::auto_ptr fog (new ESM::FogState()); fog->mFogTextures.push_back(ESM::FogTexture()); segment.saveFogOfWar(fog->mFogTextures.back()); cell->setFog(fog.release()); } } else { // FIXME: segmenting code duplicated from requestMap osg::Vec2f min(mBounds.xMin(), mBounds.yMin()); osg::Vec2f max(mBounds.xMax(), mBounds.yMax()); osg::Vec2f length = max-min; const int segsX = static_cast(std::ceil(length.x() / mMapWorldSize)); const int segsY = static_cast(std::ceil(length.y() / mMapWorldSize)); std::auto_ptr fog (new ESM::FogState()); fog->mBounds.mMinX = mBounds.xMin(); fog->mBounds.mMaxX = mBounds.xMax(); fog->mBounds.mMinY = mBounds.yMin(); fog->mBounds.mMaxY = mBounds.yMax(); fog->mNorthMarkerAngle = mAngle; fog->mFogTextures.reserve(segsX*segsY); for (int x=0; xmFogTextures.push_back(ESM::FogTexture()); // saving even if !segment.mHasFogState so we don't mess up the segmenting // plus, older openmw versions can't deal with empty images segment.saveFogOfWar(fog->mFogTextures.back()); fog->mFogTextures.back().mX = x; fog->mFogTextures.back().mY = y; } } cell->setFog(fog.release()); } } osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax) { osg::ref_ptr camera (new osg::Camera); camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setCullMask(Mask_Scene|Mask_SimpleWater|Mask_Terrain); camera->setNodeMask(Mask_RenderToTexture); osg::ref_ptr stateset = new osg::StateSet; stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON); stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON); stateset->setMode(GL_FOG, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE); osg::ref_ptr lightmodel = new osg::LightModel; lightmodel->setAmbientIntensity(osg::Vec4(0.3f, 0.3f, 0.3f, 1.f)); stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); osg::ref_ptr light = new osg::Light; light->setPosition(osg::Vec4(-0.3f, -0.3f, 0.7f, 0.f)); light->setDiffuse(osg::Vec4(0.7f, 0.7f, 0.7f, 1.f)); light->setAmbient(osg::Vec4(0,0,0,1)); light->setSpecular(osg::Vec4(0,0,0,0)); light->setLightNum(0); light->setConstantAttenuation(1.f); light->setLinearAttenuation(0.f); light->setQuadraticAttenuation(0.f); osg::ref_ptr lightSource = new osg::LightSource; lightSource->setLight(light); lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); camera->addChild(lightSource); camera->setStateSet(stateset); camera->setGraphicsContext(mViewer->getCamera()->getGraphicsContext()); camera->setViewport(0, 0, mMapResolution, mMapResolution); camera->setUpdateCallback(new CameraLocalUpdateCallback(this)); return camera; } void LocalMap::setupRenderToTexture(osg::ref_ptr camera, int x, int y) { osg::ref_ptr texture (new osg::Texture2D); texture->setTextureSize(mMapResolution, mMapResolution); texture->setInternalFormat(GL_RGB); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); camera->attach(osg::Camera::COLOR_BUFFER, texture); camera->addChild(mSceneRoot); mRoot->addChild(camera); mActiveCameras.push_back(camera); MapSegment& segment = mSegments[std::make_pair(x, y)]; segment.mMapTexture = texture; } void LocalMap::requestMap(std::set cells) { for (std::set::iterator it = cells.begin(); it != cells.end(); ++it) { MWWorld::CellStore* cell = *it; if (cell->isExterior()) requestExteriorMap(cell); else requestInteriorMap(cell); } } void LocalMap::removeCell(MWWorld::CellStore *cell) { saveFogOfWar(cell); if (cell->isExterior()) mSegments.erase(std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())); else mSegments.clear(); } osg::ref_ptr LocalMap::getMapTexture(int x, int y) { SegmentMap::iterator found = mSegments.find(std::make_pair(x, y)); if (found == mSegments.end()) return osg::ref_ptr(); else return found->second.mMapTexture; } osg::ref_ptr LocalMap::getFogOfWarTexture(int x, int y) { SegmentMap::iterator found = mSegments.find(std::make_pair(x, y)); if (found == mSegments.end()) return osg::ref_ptr(); else return found->second.mFogOfWarTexture; } void LocalMap::markForRemoval(osg::Camera *cam) { CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), cam); if (found == mActiveCameras.end()) { std::cerr << "trying to remove an inactive camera" << std::endl; return; } mActiveCameras.erase(found); mCamerasPendingRemoval.push_back(cam); } void LocalMap::cleanupCameras() { if (mCamerasPendingRemoval.empty()) return; for (CameraVector::iterator it = mCamerasPendingRemoval.begin(); it != mCamerasPendingRemoval.end(); ++it) { (*it)->removeChildren(0, (*it)->getNumChildren()); (*it)->setGraphicsContext(NULL); mRoot->removeChild(*it); } mCamerasPendingRemoval.clear(); } void LocalMap::requestExteriorMap(MWWorld::CellStore* cell) { mInterior = false; int x = cell->getCell()->getGridX(); int y = cell->getCell()->getGridY(); osg::BoundingSphere bound = mViewer->getSceneData()->getBound(); float zmin = bound.center().z() - bound.radius(); float zmax = bound.center().z() + bound.radius(); osg::ref_ptr camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize, osg::Vec3d(0,1,0), zmin, zmax); setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY()); MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; if (!segment.mFogOfWarImage) { if (cell->getFog()) segment.loadFogOfWar(cell->getFog()->mFogTextures.back()); else segment.initFogOfWar(); } } void LocalMap::requestInteriorMap(MWWorld::CellStore* cell) { osg::ComputeBoundsVisitor computeBoundsVisitor; computeBoundsVisitor.setTraversalMask(Mask_Scene|Mask_Terrain); mSceneRoot->accept(computeBoundsVisitor); osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox(); // If we're in an empty cell, bail out // The operations in this function are only valid for finite bounds if (!bounds.valid() || bounds.radius2() == 0.0) return; mInterior = true; mBounds = bounds; // Get the cell's NorthMarker rotation. This is used to rotate the entire map. osg::Vec2f north = MWBase::Environment::get().getWorld()->getNorthVector(cell); mAngle = std::atan2(north.x(), north.y()); // Rotate the cell and merge the rotated corners to the bounding box osg::Vec2f origCenter(bounds.center().x(), bounds.center().y()); osg::Vec3f origCorners[8]; for (int i=0; i<8; ++i) origCorners[i] = mBounds.corner(i); for (int i=0; i<8; ++i) { osg::Vec3f corner = origCorners[i]; osg::Vec2f corner2d (corner.x(), corner.y()); corner2d = rotatePoint(corner2d, origCenter, mAngle); mBounds.expandBy(osg::Vec3f(corner2d.x(), corner2d.y(), 0)); } // Do NOT change padding! This will break older savegames. // If the padding really needs to be changed, then it must be saved in the ESM::FogState and // assume the old (500) value as default for older savegames. const float padding = 500.0f; // Apply a little padding mBounds.set(mBounds._min - osg::Vec3f(padding,padding,0.f), mBounds._max + osg::Vec3f(padding,padding,0.f)); float zMin = mBounds.zMin(); float zMax = mBounds.zMax(); // If there is fog state in the CellStore (e.g. when it came from a savegame) we need to do some checks // to see if this state is still valid. // Both the cell bounds and the NorthMarker rotation could be changed by the content files or exchanged models. // If they changed by too much (for bounds, < padding is considered acceptable) then parts of the interior might not // be covered by the map anymore. // The following code detects this, and discards the CellStore's fog state if it needs to. if (cell->getFog()) { ESM::FogState* fog = cell->getFog(); osg::Vec3f newMin (fog->mBounds.mMinX, fog->mBounds.mMinY, zMin); osg::Vec3f newMax (fog->mBounds.mMaxX, fog->mBounds.mMaxY, zMax); osg::Vec3f minDiff = newMin - mBounds._min; osg::Vec3f maxDiff = newMax - mBounds._max; if (std::abs(minDiff.x()) > padding || std::abs(minDiff.y()) > padding || std::abs(maxDiff.x()) > padding || std::abs(maxDiff.y()) > padding || std::abs(mAngle - fog->mNorthMarkerAngle) > osg::DegreesToRadians(5.f)) { // Nuke it cell->setFog(NULL); } else { // Looks sane, use it mBounds = osg::BoundingBox(newMin, newMax); mAngle = fog->mNorthMarkerAngle; } } osg::Vec2f min(mBounds.xMin(), mBounds.yMin()); osg::Vec2f max(mBounds.xMax(), mBounds.yMax()); osg::Vec2f length = max-min; osg::Vec2f center(bounds.center().x(), bounds.center().y()); // divide into segments const int segsX = static_cast(std::ceil(length.x() / mMapWorldSize)); const int segsY = static_cast(std::ceil(length.y() / mMapWorldSize)); int i = 0; for (int x=0; x camera = createOrthographicCamera(pos.x(), pos.y(), mMapWorldSize, mMapWorldSize, osg::Vec3f(north.x(), north.y(), 0.f), zMin, zMax); setupRenderToTexture(camera, x, y); MapSegment& segment = mSegments[std::make_pair(x,y)]; if (!segment.mFogOfWarImage) { if (!cell->getFog()) segment.initFogOfWar(); else { ESM::FogState* fog = cell->getFog(); // We are using the same bounds and angle as we were using when the textures were originally made. Segments should come out the same. if (i >= int(fog->mFogTextures.size())) { std::cout << "Warning: fog texture count mismatch" << std::endl; break; } segment.loadFogOfWar(fog->mFogTextures[i]); } } ++i; } } } void LocalMap::worldToInteriorMapPosition (osg::Vec2f pos, float& nX, float& nY, int& x, int& y) { pos = rotatePoint(pos, osg::Vec2f(mBounds.center().x(), mBounds.center().y()), mAngle); osg::Vec2f min(mBounds.xMin(), mBounds.yMin()); x = static_cast(std::ceil((pos.x() - min.x()) / mMapWorldSize) - 1); y = static_cast(std::ceil((pos.y() - min.y()) / mMapWorldSize) - 1); nX = (pos.x() - min.x() - mMapWorldSize*x)/mMapWorldSize; nY = 1.0f-(pos.y() - min.y() - mMapWorldSize*y)/mMapWorldSize; } osg::Vec2f LocalMap::interiorMapToWorldPosition (float nX, float nY, int x, int y) { osg::Vec2f min(mBounds.xMin(), mBounds.yMin()); osg::Vec2f pos (mMapWorldSize * (nX + x) + min.x(), mMapWorldSize * (1.0f-nY + y) + min.y()); pos = rotatePoint(pos, osg::Vec2f(mBounds.center().x(), mBounds.center().y()), -mAngle); return pos; } bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interior) { const MapSegment& segment = mSegments[std::make_pair(x, y)]; if (!segment.mFogOfWarImage) return false; nX = std::max(0.f, std::min(1.f, nX)); nY = std::max(0.f, std::min(1.f, nY)); int texU = static_cast((sFogOfWarResolution - 1) * nX); int texV = static_cast((sFogOfWarResolution - 1) * nY); uint32_t clr = ((const uint32_t*)segment.mFogOfWarImage->data())[texV * sFogOfWarResolution + texU]; uint8_t alpha = (clr >> 24); return alpha < 200; } osg::Group* LocalMap::getRoot() { return mRoot; } void LocalMap::updatePlayer (const osg::Vec3f& position, const osg::Quat& orientation, float& u, float& v, int& x, int& y, osg::Vec3f& direction) { // retrieve the x,y grid coordinates the player is in osg::Vec2f pos(position.x(), position.y()); if (mInterior) { worldToInteriorMapPosition(pos, u,v, x,y); osg::Quat cameraOrient (mAngle, osg::Vec3(0,0,-1)); direction = orientation * cameraOrient.inverse() * osg::Vec3f(0,1,0); } else { direction = orientation * osg::Vec3f(0,1,0); x = static_cast(std::ceil(pos.x() / mMapWorldSize) - 1); y = static_cast(std::ceil(pos.y() / mMapWorldSize) - 1); // convert from world coordinates to texture UV coordinates u = std::abs((pos.x() - (mMapWorldSize*x))/mMapWorldSize); v = 1.0f-std::abs((pos.y() - (mMapWorldSize*y))/mMapWorldSize); } // explore radius (squared) const float exploreRadius = (mInterior ? 0.1f : 0.3f) * (sFogOfWarResolution-1); // explore radius from 0 to sFogOfWarResolution-1 const float sqrExploreRadius = square(exploreRadius); const float exploreRadiusUV = exploreRadius / sFogOfWarResolution; // explore radius from 0 to 1 (UV space) // change the affected fog of war textures (in a 3x3 grid around the player) for (int mx = -1; mx<2; ++mx) { for (int my = -1; my<2; ++my) { // is this texture affected at all? bool affected = false; if (mx == 0 && my == 0) // the player is always in the center of the 3x3 grid affected = true; else { bool affectsX = (mx > 0)? (u + exploreRadiusUV > 1) : (u - exploreRadiusUV < 0); bool affectsY = (my > 0)? (v + exploreRadiusUV > 1) : (v - exploreRadiusUV < 0); affected = (affectsX && (my == 0)) || (affectsY && mx == 0) || (affectsX && affectsY); } if (!affected) continue; int texX = x + mx; int texY = y + my*-1; MapSegment& segment = mSegments[std::make_pair(texX, texY)]; if (!segment.mFogOfWarImage || !segment.mMapTexture) continue; unsigned char* data = segment.mFogOfWarImage->data(); for (int texV = 0; texV> 24); alpha = std::min( alpha, (uint8_t) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) ); *(uint32_t*)data = (uint32_t) (alpha << 24); data += 4; } } segment.mHasFogState = true; segment.mFogOfWarImage->dirty(); } } } LocalMap::MapSegment::MapSegment() : mHasFogState(false) { } LocalMap::MapSegment::~MapSegment() { } void LocalMap::MapSegment::createFogOfWarTexture() { if (mFogOfWarTexture) return; mFogOfWarTexture = new osg::Texture2D; // TODO: synchronize access? for now, the worst that could happen is the draw thread jumping a frame ahead. //mFogOfWarTexture->setDataVariance(osg::Object::DYNAMIC); mFogOfWarTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mFogOfWarTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); mFogOfWarTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mFogOfWarTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); mFogOfWarTexture->setUnRefImageDataAfterApply(false); } void LocalMap::MapSegment::initFogOfWar() { mFogOfWarImage = new osg::Image; // Assign a PixelBufferObject for asynchronous transfer of data to the GPU mFogOfWarImage->setPixelBufferObject(new osg::PixelBufferObject); mFogOfWarImage->allocateImage(sFogOfWarResolution, sFogOfWarResolution, 1, GL_RGBA, GL_UNSIGNED_BYTE); assert(mFogOfWarImage->isDataContiguous()); std::vector data; data.resize(sFogOfWarResolution*sFogOfWarResolution, 0xff000000); memcpy(mFogOfWarImage->data(), &data[0], data.size()*4); createFogOfWarTexture(); mFogOfWarTexture->setImage(mFogOfWarImage); } void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm) { const std::vector& data = esm.mImageData; if (!data.size()) { initFogOfWar(); return; } // TODO: deprecate tga and use raw data instead osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); if (!readerwriter) { std::cerr << "Unable to load fog, can't find a tga ReaderWriter" << std::endl; return; } Files::IMemStream in(&data[0], data.size()); osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(in); if (!result.success()) { std::cerr << "Failed to read fog: " << result.message() << " code " << result.status() << std::endl; return; } mFogOfWarImage = result.getImage(); mFogOfWarImage->flipVertical(); mFogOfWarImage->dirty(); createFogOfWarTexture(); mFogOfWarTexture->setImage(mFogOfWarImage); mHasFogState = true; } void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const { if (!mFogOfWarImage) return; std::ostringstream ostream; osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); if (!readerwriter) { std::cerr << "Unable to write fog, can't find a tga ReaderWriter" << std::endl; return; } // extra flips are unfortunate, but required for compatibility with older versions mFogOfWarImage->flipVertical(); osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*mFogOfWarImage, ostream); if (!result.success()) { std::cerr << "Unable to write fog: " << result.message() << " code " << result.status() << std::endl; return; } mFogOfWarImage->flipVertical(); std::string data = ostream.str(); fog.mImageData = std::vector(data.begin(), data.end()); } } openmw-openmw-0.38.0/apps/openmw/mwrender/localmap.hpp000066400000000000000000000112561264522266000230200ustar00rootroot00000000000000#ifndef GAME_RENDER_LOCALMAP_H #define GAME_RENDER_LOCALMAP_H #include #include #include #include #include #include namespace MWWorld { class CellStore; } namespace ESM { struct FogTexture; } namespace osgViewer { class Viewer; } namespace osg { class Texture2D; class Image; class Camera; class Group; class Node; } namespace MWRender { /// /// \brief Local map rendering /// class LocalMap { public: LocalMap(osgViewer::Viewer* viewer); ~LocalMap(); /** * Clear all savegame-specific data (i.e. fog of war textures) */ void clear(); /** * Request a map render for the given cells. Render textures will be immediately created and can be retrieved with the getMapTexture function. */ void requestMap (std::set cells); /** * Remove map and fog textures for the given cell. */ void removeCell (MWWorld::CellStore* cell); osg::ref_ptr getMapTexture (int x, int y); osg::ref_ptr getFogOfWarTexture (int x, int y); /** * Indicates a camera has been queued for rendering and can be cleaned up in the next frame. For internal use only. */ void markForRemoval(osg::Camera* cam); /** * Removes cameras that have already been rendered. Should be called every frame to ensure that * we do not render the same map more than once. Note, this cleanup is difficult to implement in an * automated fashion, since we can't alter the scene graph structure from within an update callback. */ void cleanupCameras(); /** * Set the position & direction of the player, and returns the position in map space through the reference parameters. * @remarks This is used to draw a "fog of war" effect * to hide areas on the map the player has not discovered yet. */ void updatePlayer (const osg::Vec3f& position, const osg::Quat& orientation, float& u, float& v, int& x, int& y, osg::Vec3f& direction); /** * Save the fog of war for this cell to its CellStore. * @remarks This should be called when unloading a cell, and for all active cells prior to saving the game. */ void saveFogOfWar(MWWorld::CellStore* cell); /** * Get the interior map texture index and normalized position on this texture, given a world position */ void worldToInteriorMapPosition (osg::Vec2f pos, float& nX, float& nY, int& x, int& y); osg::Vec2f interiorMapToWorldPosition (float nX, float nY, int x, int y); /** * Check if a given position is explored by the player (i.e. not obscured by fog of war) */ bool isPositionExplored (float nX, float nY, int x, int y, bool interior); osg::Group* getRoot(); private: osg::ref_ptr mViewer; osg::ref_ptr mRoot; osg::ref_ptr mSceneRoot; typedef std::vector< osg::ref_ptr > CameraVector; CameraVector mActiveCameras; CameraVector mCamerasPendingRemoval; struct MapSegment { MapSegment(); ~MapSegment(); void initFogOfWar(); void loadFogOfWar(const ESM::FogTexture& fog); void saveFogOfWar(ESM::FogTexture& fog) const; void createFogOfWarTexture(); osg::ref_ptr mMapTexture; osg::ref_ptr mFogOfWarTexture; osg::ref_ptr mFogOfWarImage; bool mHasFogState; }; typedef std::map, MapSegment> SegmentMap; SegmentMap mSegments; int mMapResolution; // the dynamic texture is a bottleneck, so don't set this too high static const int sFogOfWarResolution = 32; // size of a map segment (for exteriors, 1 cell) float mMapWorldSize; float mAngle; const osg::Vec2f rotatePoint(const osg::Vec2f& point, const osg::Vec2f& center, const float angle); void requestExteriorMap(MWWorld::CellStore* cell); void requestInteriorMap(MWWorld::CellStore* cell); osg::ref_ptr createOrthographicCamera(float left, float top, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax); void setupRenderToTexture(osg::ref_ptr camera, int x, int y); bool mInterior; osg::BoundingBox mBounds; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/npcanimation.cpp000066400000000000000000001203421264522266000237000ustar00rootroot00000000000000#include "npcanimation.hpp" #include #include #include #include #include #include #include #include #include #include #include #include // TextKeyMapHolder #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "camera.hpp" #include "rotatecontroller.hpp" #include "renderbin.hpp" #include "vismask.hpp" namespace { std::string getVampireHead(const std::string& race, bool female) { static std::map , const ESM::BodyPart* > sVampireMapping; std::pair thisCombination = std::make_pair(race, int(female)); if (sVampireMapping.find(thisCombination) == sVampireMapping.end()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); for(MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) { const ESM::BodyPart& bodypart = *it; if (!bodypart.mData.mVampire) continue; if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) continue; if (bodypart.mData.mPart != ESM::BodyPart::MP_Head) continue; if (female != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) continue; if (!Misc::StringUtils::ciEqual(bodypart.mRace, race)) continue; sVampireMapping[thisCombination] = &*it; } } if (sVampireMapping.find(thisCombination) == sVampireMapping.end()) sVampireMapping[thisCombination] = NULL; const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination]; if (!bodyPart) return std::string(); return "meshes\\" + bodyPart->mModel; } } namespace MWRender { class HeadAnimationTime : public SceneUtil::ControllerSource { private: MWWorld::Ptr mReference; float mTalkStart; float mTalkStop; float mBlinkStart; float mBlinkStop; float mBlinkTimer; bool mEnabled; float mValue; private: void resetBlinkTimer(); public: HeadAnimationTime(MWWorld::Ptr reference); void updatePtr(const MWWorld::Ptr& updated); void update(float dt); void setEnabled(bool enabled); void setTalkStart(float value); void setTalkStop(float value); void setBlinkStart(float value); void setBlinkStop(float value); virtual float getValue(osg::NodeVisitor* nv); }; // -------------------------------------------------------------------------------- /// Subclass RotateController to add a Z-offset for sneaking in first person mode. /// @note We use inheritance instead of adding another controller, so that we do not have to compute the worldOrient twice. /// @note Must be set on a MatrixTransform. class NeckController : public RotateController { public: NeckController(osg::Node* relativeTo) : RotateController(relativeTo) { } void setOffset(osg::Vec3f offset) { mOffset = offset; } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::MatrixTransform* transform = static_cast(node); osg::Matrix matrix = transform->getMatrix(); osg::Quat worldOrient = getWorldOrientation(node); osg::Quat orient = worldOrient * mRotate * worldOrient.inverse() * matrix.getRotate(); matrix.setRotate(orient); matrix.setTrans(matrix.getTrans() + worldOrient.inverse() * mOffset); transform->setMatrix(matrix); traverse(node,nv); } private: osg::Vec3f mOffset; }; // -------------------------------------------------------------------------------------------------------------- HeadAnimationTime::HeadAnimationTime(MWWorld::Ptr reference) : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mEnabled(true), mValue(0) { resetBlinkTimer(); } void HeadAnimationTime::updatePtr(const MWWorld::Ptr &updated) { mReference = updated; } void HeadAnimationTime::setEnabled(bool enabled) { mEnabled = enabled; } void HeadAnimationTime::resetBlinkTimer() { mBlinkTimer = -(2.0f + Misc::Rng::rollDice(6)); } void HeadAnimationTime::update(float dt) { if (!mEnabled) return; if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) { mBlinkTimer += dt; float duration = mBlinkStop - mBlinkStart; if (mBlinkTimer >= 0 && mBlinkTimer <= duration) { mValue = mBlinkStart + mBlinkTimer; } else mValue = mBlinkStop; if (mBlinkTimer > duration) resetBlinkTimer(); } else { // FIXME: would be nice to hold on to the SoundPtr so we don't have to retrieve it every frame mValue = mTalkStart + (mTalkStop - mTalkStart) * std::min(1.f, MWBase::Environment::get().getSoundManager()->getSaySoundLoudness(mReference)*2); // Rescale a bit (most voices are not very loud) } } float HeadAnimationTime::getValue(osg::NodeVisitor*) { return mValue; } void HeadAnimationTime::setTalkStart(float value) { mTalkStart = value; } void HeadAnimationTime::setTalkStop(float value) { mTalkStop = value; } void HeadAnimationTime::setBlinkStart(float value) { mBlinkStart = value; } void HeadAnimationTime::setBlinkStop(float value) { mBlinkStop = value; } // ---------------------------------------------------- static NpcAnimation::PartBoneMap createPartListMap() { NpcAnimation::PartBoneMap result; result.insert(std::make_pair(ESM::PRT_Head, "Head")); result.insert(std::make_pair(ESM::PRT_Hair, "Head")); // note it uses "Head" as attach bone, but "Hair" as filter result.insert(std::make_pair(ESM::PRT_Neck, "Neck")); result.insert(std::make_pair(ESM::PRT_Cuirass, "Chest")); result.insert(std::make_pair(ESM::PRT_Groin, "Groin")); result.insert(std::make_pair(ESM::PRT_Skirt, "Groin")); result.insert(std::make_pair(ESM::PRT_RHand, "Right Hand")); result.insert(std::make_pair(ESM::PRT_LHand, "Left Hand")); result.insert(std::make_pair(ESM::PRT_RWrist, "Right Wrist")); result.insert(std::make_pair(ESM::PRT_LWrist, "Left Wrist")); result.insert(std::make_pair(ESM::PRT_Shield, "Shield Bone")); result.insert(std::make_pair(ESM::PRT_RForearm, "Right Forearm")); result.insert(std::make_pair(ESM::PRT_LForearm, "Left Forearm")); result.insert(std::make_pair(ESM::PRT_RUpperarm, "Right Upper Arm")); result.insert(std::make_pair(ESM::PRT_LUpperarm, "Left Upper Arm")); result.insert(std::make_pair(ESM::PRT_RFoot, "Right Foot")); result.insert(std::make_pair(ESM::PRT_LFoot, "Left Foot")); result.insert(std::make_pair(ESM::PRT_RAnkle, "Right Ankle")); result.insert(std::make_pair(ESM::PRT_LAnkle, "Left Ankle")); result.insert(std::make_pair(ESM::PRT_RKnee, "Right Knee")); result.insert(std::make_pair(ESM::PRT_LKnee, "Left Knee")); result.insert(std::make_pair(ESM::PRT_RLeg, "Right Upper Leg")); result.insert(std::make_pair(ESM::PRT_LLeg, "Left Upper Leg")); result.insert(std::make_pair(ESM::PRT_RPauldron, "Right Clavicle")); result.insert(std::make_pair(ESM::PRT_LPauldron, "Left Clavicle")); result.insert(std::make_pair(ESM::PRT_Weapon, "Weapon Bone")); result.insert(std::make_pair(ESM::PRT_Tail, "Tail")); return result; } const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap(); NpcAnimation::~NpcAnimation() { if (!mListenerDisabled // No need to getInventoryStore() to reset, if none exists // This is to avoid triggering the listener via ensureCustomData()->autoEquip()->fireEquipmentChanged() // all from within this destructor. ouch! && mPtr.getRefData().getCustomData() && mPtr.getClass().getInventoryStore(mPtr).getListener() == this) mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr); } NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem, bool disableListener, bool disableSounds, ViewMode viewMode, float firstPersonFieldOfView) : Animation(ptr, parentNode, resourceSystem), mListenerDisabled(disableListener), mViewMode(viewMode), mShowWeapons(false), mShowCarriedLeft(true), mNpcType(Type_Normal), mFirstPersonFieldOfView(firstPersonFieldOfView), mSoundsDisabled(disableSounds), mAccurateAiming(false), mAimingFactor(0.f) { mNpc = mPtr.get()->mBase; mHeadAnimationTime = boost::shared_ptr(new HeadAnimationTime(mPtr)); mWeaponAnimationTime = boost::shared_ptr(new WeaponAnimationTime(this)); for(size_t i = 0;i < ESM::PRT_Count;i++) { mPartslots[i] = -1; //each slot is empty mPartPriorities[i] = 0; } updateNpcBase(); if (!disableListener) mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr); } void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) { assert(viewMode != VM_HeadOnly); if(mViewMode == viewMode) return; mViewMode = viewMode; rebuild(); setRenderBin(); } /// @brief A RenderBin callback to clear the depth buffer before rendering. class DepthClearCallback : public osgUtil::RenderBin::DrawCallback { public: DepthClearCallback() { mDepth = new osg::Depth; mDepth->setWriteMask(true); } virtual void drawImplementation(osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous) { renderInfo.getState()->applyAttribute(mDepth); glClear(GL_DEPTH_BUFFER_BIT); bin->drawImplementation(renderInfo, previous); } osg::ref_ptr mDepth; }; /// Overrides Field of View to given value for rendering the subgraph. /// Must be added as cull callback. class OverrideFieldOfViewCallback : public osg::NodeCallback { public: OverrideFieldOfViewCallback(float fov) : mFov(fov) { } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = static_cast(nv); osg::RefMatrix* projectionMatrix = new osg::RefMatrix(*cv->getProjectionMatrix()); float fov, aspect, zNear, zFar; if (projectionMatrix->getPerspective(fov, aspect, zNear, zFar)) { fov = mFov; projectionMatrix->makePerspective(fov, aspect, zNear, zFar); cv->pushProjectionMatrix(projectionMatrix); traverse(node, nv); cv->popProjectionMatrix(); } else traverse(node, nv); } private: float mFov; }; void NpcAnimation::setRenderBin() { if (mViewMode == VM_FirstPerson) { static bool prototypeAdded = false; if (!prototypeAdded) { osg::ref_ptr depthClearBin (new osgUtil::RenderBin); depthClearBin->setDrawCallback(new DepthClearCallback); osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); prototypeAdded = true; } osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); stateset->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); } else Animation::setRenderBin(); } void NpcAnimation::rebuild() { updateNpcBase(); MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); } int NpcAnimation::getSlot(const osg::NodePath &path) const { for (int i=0; igetNode().get()) != path.end()) { return mPartslots[i]; } } return -1; } void NpcAnimation::updateNpcBase() { clearAnimSources(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); bool isWerewolf = (mNpcType == Type_Werewolf); bool isVampire = (mNpcType == Type_Vampire); if (isWerewolf) { mHeadModel = "meshes\\" + store.get().find("WerewolfHead")->mModel; mHairModel = "meshes\\" + store.get().find("WerewolfHair")->mModel; } else { mHeadModel = ""; if (isVampire) // FIXME: fall back to regular head when getVampireHead fails? mHeadModel = getVampireHead(mNpc->mRace, mNpc->mFlags & ESM::NPC::Female); else if (!mNpc->mHead.empty()) { const ESM::BodyPart* bp = store.get().search(mNpc->mHead); if (bp) mHeadModel = "meshes\\" + bp->mModel; else std::cerr << "Failed to load body part '" << mNpc->mHead << "'" << std::endl; } mHairModel = ""; if (!mNpc->mHair.empty()) { const ESM::BodyPart* bp = store.get().search(mNpc->mHair); if (bp) mHairModel = "meshes\\" + bp->mModel; else std::cerr << "Failed to load body part '" << mNpc->mHair << "'" << std::endl; } } bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; std::string smodel = (mViewMode != VM_FirstPerson) ? (!isWerewolf ? !isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif" : "meshes\\wolf\\skin.nif") : (!isWerewolf ? !isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif" : "meshes\\wolf\\skin.1st.nif"); smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS()); setObjectRoot(smodel, true, true, false); if(mViewMode != VM_FirstPerson) { addAnimSource(smodel); if(!isWerewolf) { if(Misc::StringUtils::lowerCase(mNpc->mRace).find("argonian") != std::string::npos) addAnimSource("meshes\\xargonian_swimkna.nif"); else if(!mNpc->isMale() && !isBeast) addAnimSource("meshes\\xbase_anim_female.nif"); if(mNpc->mModel.length() > 0) addAnimSource("meshes\\x"+mNpc->mModel); } } else { mObjectRoot->setNodeMask(Mask_FirstPerson); mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView)); if(isWerewolf) addAnimSource(smodel); else { // A bit counter-intuitive, but unlike third-person anims, it seems // beast races get both base_anim.1st.nif and base_animkna.1st.nif. addAnimSource("meshes\\xbase_anim.1st.nif"); if(isBeast) addAnimSource("meshes\\xbase_animkna.1st.nif"); if(!mNpc->isMale() && !isBeast) addAnimSource("meshes\\xbase_anim_female.1st.nif"); } } for(size_t i = 0;i < ESM::PRT_Count;i++) removeIndividualPart((ESM::PartReferenceType)i); updateParts(); mWeaponAnimationTime->updateStartTime(); } void NpcAnimation::updateParts() { if (!mObjectRoot.get()) return; const MWWorld::Class &cls = mPtr.getClass(); NpcType curType = Type_Normal; if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0) curType = Type_Vampire; if (cls.getNpcStats(mPtr).isWerewolf()) curType = Type_Werewolf; if (curType != mNpcType) { mNpcType = curType; rebuild(); return; } static const struct { int mSlot; int mBasePriority; } slotlist[] = { // FIXME: Priority is based on the number of reserved slots. There should be a better way. { MWWorld::InventoryStore::Slot_Robe, 12 }, { MWWorld::InventoryStore::Slot_Skirt, 3 }, { MWWorld::InventoryStore::Slot_Helmet, 0 }, { MWWorld::InventoryStore::Slot_Cuirass, 0 }, { MWWorld::InventoryStore::Slot_Greaves, 0 }, { MWWorld::InventoryStore::Slot_LeftPauldron, 0 }, { MWWorld::InventoryStore::Slot_RightPauldron, 0 }, { MWWorld::InventoryStore::Slot_Boots, 0 }, { MWWorld::InventoryStore::Slot_LeftGauntlet, 0 }, { MWWorld::InventoryStore::Slot_RightGauntlet, 0 }, { MWWorld::InventoryStore::Slot_Shirt, 0 }, { MWWorld::InventoryStore::Slot_Pants, 0 }, { MWWorld::InventoryStore::Slot_CarriedLeft, 0 }, { MWWorld::InventoryStore::Slot_CarriedRight, 0 } }; static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); bool wasArrowAttached = (mAmmunition.get() != NULL); MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) { MWWorld::ContainerStoreIterator store = inv.getSlot(slotlist[i].mSlot); removePartGroup(slotlist[i].mSlot); if(store == inv.end()) continue; if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet) removeIndividualPart(ESM::PRT_Hair); int prio = 1; bool enchantedGlow = !store->getClass().getEnchantment(*store).empty(); osg::Vec4f glowColor = getEnchantmentColor(*store); if(store->getTypeName() == typeid(ESM::Clothing).name()) { prio = ((slotlist[i].mBasePriority+1)<<1) + 0; const ESM::Clothing *clothes = store->get()->mBase; addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts, enchantedGlow, &glowColor); } else if(store->getTypeName() == typeid(ESM::Armor).name()) { prio = ((slotlist[i].mBasePriority+1)<<1) + 1; const ESM::Armor *armor = store->get()->mBase; addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts, enchantedGlow, &glowColor); } if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe) { ESM::PartReferenceType parts[] = { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, ESM::PRT_RForearm, ESM::PRT_LForearm }; size_t parts_size = sizeof(parts)/sizeof(parts[0]); for(size_t p = 0;p < parts_size;++p) reserveIndividualPart(parts[p], slotlist[i].mSlot, prio); } else if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Skirt) { reserveIndividualPart(ESM::PRT_Groin, slotlist[i].mSlot, prio); reserveIndividualPart(ESM::PRT_RLeg, slotlist[i].mSlot, prio); reserveIndividualPart(ESM::PRT_LLeg, slotlist[i].mSlot, prio); } } if(mViewMode != VM_FirstPerson) { if(mPartPriorities[ESM::PRT_Head] < 1 && !mHeadModel.empty()) addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, mHeadModel); if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1 && !mHairModel.empty()) addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, mHairModel); } if(mViewMode == VM_HeadOnly) return; if(mPartPriorities[ESM::PRT_Shield] < 1) { MWWorld::ContainerStoreIterator store = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); MWWorld::Ptr part; if(store != inv.end() && (part=*store).getTypeName() == typeid(ESM::Light).name()) { const ESM::Light *light = part.get()->mBase; addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, "meshes\\"+light->mModel); addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), light); } } showWeapons(mShowWeapons); showCarriedLeft(mShowCarriedLeft); // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination static std::map< std::pair,std::vector > sRaceMapping; bool isWerewolf = (mNpcType == Type_Werewolf); int flags = (isWerewolf ? -1 : 0); if(!mNpc->isMale()) { static const int Flag_Female = 1<<0; flags |= Flag_Female; } if(mViewMode == VM_FirstPerson) { static const int Flag_FirstPerson = 1<<1; flags |= Flag_FirstPerson; } std::string race = (isWerewolf ? "werewolf" : Misc::StringUtils::lowerCase(mNpc->mRace)); std::pair thisCombination = std::make_pair(race, flags); if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) { typedef std::multimap BodyPartMapType; static BodyPartMapType sBodyPartMap; if(sBodyPartMap.empty()) { sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg)); sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); } std::vector &parts = sRaceMapping[thisCombination]; parts.resize(ESM::PRT_Count, NULL); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); for(MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) { if(isWerewolf) break; const ESM::BodyPart& bodypart = *it; if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) continue; if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) continue; if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) continue; bool firstPerson = (bodypart.mId.size() >= 3) && bodypart.mId[bodypart.mId.size()-3] == '1' && bodypart.mId[bodypart.mId.size()-2] == 's' && bodypart.mId[bodypart.mId.size()-1] == 't'; if(firstPerson != (mViewMode == VM_FirstPerson)) { if(mViewMode == VM_FirstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand || bodypart.mData.mPart == ESM::BodyPart::MP_Wrist || bodypart.mData.mPart == ESM::BodyPart::MP_Forearm || bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm)) { /* Allow 3rd person skins as a fallback for the arms if 1st person is missing. */ BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) { if(!parts[bIt->second]) parts[bIt->second] = &*it; ++bIt; } } continue; } if ((!mNpc->isMale()) != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) { // Allow opposite gender's parts as fallback if parts for our gender are missing BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) { if(!parts[bIt->second]) parts[bIt->second] = &*it; ++bIt; } continue; } BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) { parts[bIt->second] = &*it; ++bIt; } } } const std::vector &parts = sRaceMapping[thisCombination]; for(int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) { if(mPartPriorities[part] < 1) { const ESM::BodyPart* bodypart = parts[part]; if(bodypart) addOrReplaceIndividualPart((ESM::PartReferenceType)part, -1, 1, "meshes\\"+bodypart->mModel); } } if (wasArrowAttached) attachArrow(); } PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const std::string& bonename, const std::string& bonefilter, bool enchantedGlow, osg::Vec4f* glowColor) { osg::ref_ptr instance = mResourceSystem->getSceneManager()->createInstance(model); osg::ref_ptr attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, bonename); mResourceSystem->getSceneManager()->notifyAttached(attached); if (enchantedGlow) addGlow(attached, *glowColor); return PartHolderPtr(new PartHolder(attached)); } osg::Vec3f NpcAnimation::runAnimation(float timepassed) { osg::Vec3f ret = Animation::runAnimation(timepassed); mHeadAnimationTime->update(timepassed); if (mFirstPersonNeckController) { if (mAccurateAiming) mAimingFactor = 1.f; else mAimingFactor = std::max(0.f, mAimingFactor - timepassed * 0.5f); float rotateFactor = 0.75f + 0.25f * mAimingFactor; mFirstPersonNeckController->setRotate(osg::Quat(mPtr.getRefData().getPosition().rot[0] * rotateFactor, osg::Vec3f(-1,0,0))); mFirstPersonNeckController->setOffset(mFirstPersonOffset); } WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0]); return ret; } void NpcAnimation::removeIndividualPart(ESM::PartReferenceType type) { mPartPriorities[type] = 0; mPartslots[type] = -1; mObjectParts[type].reset(); if (!mSoundIds[type].empty() && !mSoundsDisabled) { MWBase::Environment::get().getSoundManager()->stopSound3D(mPtr, mSoundIds[type]); mSoundIds[type].clear(); } } void NpcAnimation::reserveIndividualPart(ESM::PartReferenceType type, int group, int priority) { if(priority > mPartPriorities[type]) { removeIndividualPart(type); mPartPriorities[type] = priority; mPartslots[type] = group; } } void NpcAnimation::removePartGroup(int group) { for(int i = 0; i < ESM::PRT_Count; i++) { if(mPartslots[i] == group) removeIndividualPart((ESM::PartReferenceType)i); } } bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh, bool enchantedGlow, osg::Vec4f* glowColor) { if(priority <= mPartPriorities[type]) return false; removeIndividualPart(type); mPartslots[type] = group; mPartPriorities[type] = priority; try { const std::string& bonename = sPartList.at(type); // PRT_Hair seems to be the only type that breaks consistency and uses a filter that's different from the attachment bone const std::string bonefilter = (type == ESM::PRT_Hair) ? "hair" : bonename; mObjectParts[type] = insertBoundedPart(mesh, bonename, bonefilter, enchantedGlow, glowColor); } catch (std::exception& e) { std::cerr << "Error adding NPC part: " << e.what() << std::endl; return false; } if (!mSoundsDisabled) { MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); MWWorld::ContainerStoreIterator csi = inv.getSlot(group < 0 ? MWWorld::InventoryStore::Slot_Helmet : group); if (csi != inv.end()) { mSoundIds[type] = csi->getClass().getSound(*csi); if (!mSoundIds[type].empty()) { MWBase::Environment::get().getSoundManager()->playSound3D(mPtr, mSoundIds[type], 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); } } } boost::shared_ptr src; if (type == ESM::PRT_Head) { src = mHeadAnimationTime; osg::Node* node = mObjectParts[type]->getNode(); if (node->getUserDataContainer()) { for (unsigned int i=0; igetUserDataContainer()->getNumUserObjects(); ++i) { osg::Object* obj = node->getUserDataContainer()->getUserObject(i); if (NifOsg::TextKeyMapHolder* keys = dynamic_cast(obj)) { for (NifOsg::TextKeyMap::const_iterator it = keys->mTextKeys.begin(); it != keys->mTextKeys.end(); ++it) { if (Misc::StringUtils::ciEqual(it->second, "talk: start")) mHeadAnimationTime->setTalkStart(it->first); if (Misc::StringUtils::ciEqual(it->second, "talk: stop")) mHeadAnimationTime->setTalkStop(it->first); if (Misc::StringUtils::ciEqual(it->second, "blink: start")) mHeadAnimationTime->setBlinkStart(it->first); if (Misc::StringUtils::ciEqual(it->second, "blink: stop")) mHeadAnimationTime->setBlinkStop(it->first); } break; } } } } else if (type == ESM::PRT_Weapon) src = mWeaponAnimationTime; else src.reset(new NullAnimationTime); SceneUtil::AssignControllerSourcesVisitor assignVisitor(src); mObjectParts[type]->getNode()->accept(assignVisitor); return true; } void NpcAnimation::addPartGroup(int group, int priority, const std::vector &parts, bool enchantedGlow, osg::Vec4f* glowColor) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : ""; std::vector::const_iterator part(parts.begin()); for(;part != parts.end();++part) { const ESM::BodyPart *bodypart = 0; if(!mNpc->isMale() && !part->mFemale.empty()) { bodypart = partStore.search(part->mFemale+ext); if(!bodypart && mViewMode == VM_FirstPerson) { bodypart = partStore.search(part->mFemale); if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) bodypart = NULL; } else if (!bodypart) std::cerr << "Failed to find body part '" << part->mFemale << "'" << std::endl; } if(!bodypart && !part->mMale.empty()) { bodypart = partStore.search(part->mMale+ext); if(!bodypart && mViewMode == VM_FirstPerson) { bodypart = partStore.search(part->mMale); if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) bodypart = NULL; } else if (!bodypart) std::cerr << "Failed to find body part '" << part->mMale << "'" << std::endl; } if(bodypart) addOrReplaceIndividualPart((ESM::PartReferenceType)part->mPart, group, priority, "meshes\\"+bodypart->mModel, enchantedGlow, glowColor); else reserveIndividualPart((ESM::PartReferenceType)part->mPart, group, priority); } } void NpcAnimation::addControllers() { Animation::addControllers(); mFirstPersonNeckController = NULL; WeaponAnimation::deleteControllers(); if (mViewMode == VM_FirstPerson) { NodeMap::iterator found = mNodeMap.find("bip01 neck"); if (found != mNodeMap.end() && dynamic_cast(found->second.get())) { osg::Node* node = found->second; mFirstPersonNeckController = new NeckController(mObjectRoot.get()); node->addUpdateCallback(mFirstPersonNeckController); mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController)); } } else if (mViewMode == VM_Normal) { WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get()); } } void NpcAnimation::showWeapons(bool showWeapon) { mShowWeapons = showWeapon; if(showWeapon) { MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if(weapon != inv.end()) { osg::Vec4f glowColor = getEnchantmentColor(*weapon); std::string mesh = weapon->getClass().getModel(*weapon); addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); // Crossbows start out with a bolt attached if (weapon->getTypeName() == typeid(ESM::Weapon).name() && weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt) attachArrow(); else mAmmunition.reset(); } else mAmmunition.reset(); } } else { removeIndividualPart(ESM::PRT_Weapon); } } void NpcAnimation::showCarriedLeft(bool show) { mShowCarriedLeft = show; MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(show && iter != inv.end()) { osg::Vec4f glowColor = getEnchantmentColor(*iter); std::string mesh = iter->getClass().getModel(*iter); if (addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor)) { if (iter->getTypeName() == typeid(ESM::Light).name()) addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), iter->get()->mBase); } } else removeIndividualPart(ESM::PRT_Shield); } void NpcAnimation::attachArrow() { WeaponAnimation::attachArrow(mPtr); } void NpcAnimation::releaseArrow(float attackStrength) { WeaponAnimation::releaseArrow(mPtr, attackStrength); } osg::Group* NpcAnimation::getArrowBone() { PartHolderPtr part = mObjectParts[ESM::PRT_Weapon]; if (!part) return NULL; SceneUtil::FindByNameVisitor findVisitor ("ArrowBone"); part->getNode()->accept(findVisitor); return findVisitor.mFoundNode; } osg::Node* NpcAnimation::getWeaponNode() { PartHolderPtr part = mObjectParts[ESM::PRT_Weapon]; if (!part) return NULL; return part->getNode(); } Resource::ResourceSystem* NpcAnimation::getResourceSystem() { return mResourceSystem; } void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) { // During first auto equip, we don't play any sounds. // Basically we don't want sounds when the actor is first loaded, // the items should appear as if they'd always been equipped. if (playSound) { static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(!magicEffect->mHitSound.empty()) sndMgr->playSound3D(mPtr, magicEffect->mHitSound, 1.0f, 1.0f); else sndMgr->playSound3D(mPtr, schools[magicEffect->mData.mSchool]+" hit", 1.0f, 1.0f); } if (!magicEffect->mHit.empty()) { const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0; // Don't play particle VFX unless the effect is new or it should be looping. if (isNew || loop) addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); } } void NpcAnimation::enableHeadAnimation(bool enable) { mHeadAnimationTime->setEnabled(enable); } void NpcAnimation::setWeaponGroup(const std::string &group) { mWeaponAnimationTime->setGroup(group); } void NpcAnimation::equipmentChanged() { updateParts(); } void NpcAnimation::setVampire(bool vampire) { if (mNpcType == Type_Werewolf) // we can't have werewolf vampires, can we return; if ((mNpcType == Type_Vampire) != vampire) { if (mPtr == MWMechanics::getPlayer()) MWBase::Environment::get().getWorld()->reattachPlayerCamera(); else rebuild(); } } void NpcAnimation::setFirstPersonOffset(const osg::Vec3f &offset) { mFirstPersonOffset = offset; } void NpcAnimation::updatePtr(const MWWorld::Ptr &updated) { Animation::updatePtr(updated); mHeadAnimationTime->updatePtr(updated); } void NpcAnimation::setAccurateAiming(bool enabled) { mAccurateAiming = enabled; } } openmw-openmw-0.38.0/apps/openmw/mwrender/npcanimation.hpp000066400000000000000000000117161264522266000237110ustar00rootroot00000000000000#ifndef GAME_RENDER_NPCANIMATION_H #define GAME_RENDER_NPCANIMATION_H #include "animation.hpp" #include "../mwworld/inventorystore.hpp" #include "weaponanimation.hpp" namespace ESM { struct NPC; } namespace MWRender { class NeckController; class HeadAnimationTime; class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener { public: virtual void equipmentChanged(); virtual void permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound); public: typedef std::map PartBoneMap; enum ViewMode { VM_Normal, VM_FirstPerson, VM_HeadOnly }; private: static const PartBoneMap sPartList; bool mListenerDisabled; // Bounded Parts PartHolderPtr mObjectParts[ESM::PRT_Count]; std::string mSoundIds[ESM::PRT_Count]; const ESM::NPC *mNpc; std::string mHeadModel; std::string mHairModel; ViewMode mViewMode; bool mShowWeapons; bool mShowCarriedLeft; enum NpcType { Type_Normal, Type_Werewolf, Type_Vampire }; NpcType mNpcType; int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty int mPartPriorities[ESM::PRT_Count]; osg::Vec3f mFirstPersonOffset; // Field of view to use when rendering first person meshes float mFirstPersonFieldOfView; boost::shared_ptr mHeadAnimationTime; boost::shared_ptr mWeaponAnimationTime; bool mSoundsDisabled; bool mAccurateAiming; float mAimingFactor; void updateNpcBase(); PartHolderPtr insertBoundedPart(const std::string &model, const std::string &bonename, const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=NULL); void removeIndividualPart(ESM::PartReferenceType type); void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority); bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh, bool enchantedGlow=false, osg::Vec4f* glowColor=NULL); void removePartGroup(int group); void addPartGroup(int group, int priority, const std::vector &parts, bool enchantedGlow=false, osg::Vec4f* glowColor=NULL); virtual void setRenderBin(); osg::ref_ptr mFirstPersonNeckController; protected: virtual void addControllers(); public: /** * @param ptr * @param disableListener Don't listen for equipment changes and magic effects. InventoryStore only supports * one listener at a time, so you shouldn't do this if creating several NpcAnimations * for the same Ptr, eg preview dolls for the player. * Those need to be manually rendered anyway. * @param disableSounds Same as \a disableListener but for playing items sounds * @param viewMode */ NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem, bool disableListener = false, bool disableSounds = false, ViewMode viewMode=VM_Normal, float firstPersonFieldOfView=55.f); virtual ~NpcAnimation(); virtual void enableHeadAnimation(bool enable); /// 1: the first person meshes follow the camera's rotation completely /// 0: the first person meshes follow the camera with a reduced factor, so you can look down at your own hands virtual void setAccurateAiming(bool enabled); virtual void setWeaponGroup(const std::string& group); virtual osg::Vec3f runAnimation(float timepassed); /// A relative factor (0-1) that decides if and how much the skeleton should be pitched /// to indicate the facing orientation of the character. virtual void setPitchFactor(float factor) { mPitchFactor = factor; } virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool show); virtual void attachArrow(); virtual void releaseArrow(float attackStrength); virtual osg::Group* getArrowBone(); virtual osg::Node* getWeaponNode(); virtual Resource::ResourceSystem* getResourceSystem(); // WeaponAnimation virtual void showWeapon(bool show) { showWeapons(show); } void setViewMode(ViewMode viewMode); void updateParts(); /// Rebuilds the NPC, updating their root model, animation sources, and equipment. void rebuild(); /// Get the inventory slot that the given node path leads into, or -1 if not found. int getSlot(const osg::NodePath& path) const; virtual void setVampire(bool vampire); /// Set a translation offset (in object root space) to apply to meshes when in first person mode. void setFirstPersonOffset(const osg::Vec3f& offset); virtual void updatePtr(const MWWorld::Ptr& updated); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/objects.cpp000066400000000000000000000172321264522266000226540ustar00rootroot00000000000000#include "objects.hpp" #include #include #include #include #include #include #include #include #include #include #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" #include "animation.hpp" #include "npcanimation.hpp" #include "creatureanimation.hpp" #include "vismask.hpp" namespace { /// Removes all particle systems and related nodes in a subgraph. class RemoveParticlesVisitor : public osg::NodeVisitor { public: RemoveParticlesVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { } virtual void apply(osg::Node &node) { if (dynamic_cast(&node)) mToRemove.push_back(&node); traverse(node); } virtual void apply(osg::Geode& geode) { std::vector partsysVector; for (unsigned int i=0; i(drw)) partsysVector.push_back(partsys); } for (std::vector::iterator it = partsysVector.begin(); it != partsysVector.end(); ++it) geode.removeDrawable(*it); } #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) virtual void apply(osg::Drawable& drw) { if (osgParticle::ParticleSystem* partsys = dynamic_cast(&drw)) mToRemove.push_back(partsys); } #endif void remove() { for (std::vector >::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) { // FIXME: a Drawable might have more than one parent osg::Node* node = *it; if (node->getNumParents()) node->getParent(0)->removeChild(node); } mToRemove.clear(); } private: std::vector > mToRemove; }; } namespace MWRender { Objects::Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr rootNode) : mRootNode(rootNode) , mResourceSystem(resourceSystem) { } Objects::~Objects() { for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();++iter) delete iter->second; mObjects.clear(); for (CellMap::iterator iter = mCellSceneNodes.begin(); iter != mCellSceneNodes.end(); ++iter) iter->second->getParent(0)->removeChild(iter->second); mCellSceneNodes.clear(); } void Objects::insertBegin(const MWWorld::Ptr& ptr) { osg::ref_ptr cellnode; CellMap::iterator found = mCellSceneNodes.find(ptr.getCell()); if (found == mCellSceneNodes.end()) { cellnode = new osg::Group; mRootNode->addChild(cellnode); mCellSceneNodes[ptr.getCell()] = cellnode; } else cellnode = found->second; osg::ref_ptr insert (new SceneUtil::PositionAttitudeTransform); cellnode->addChild(insert); insert->getOrCreateUserDataContainer()->addUserObject(new PtrHolder(ptr)); const float *f = ptr.getRefData().getPosition().pos; insert->setPosition(osg::Vec3(f[0], f[1], f[2])); const float scale = ptr.getCellRef().getScale(); osg::Vec3f scaleVec(scale, scale, scale); ptr.getClass().adjustScale(ptr, scaleVec, true); insert->setScale(scaleVec); ptr.getRefData().setBaseNode(insert); } void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool animated, bool allowLight) { insertBegin(ptr); std::auto_ptr anim (new ObjectAnimation(ptr, mesh, mResourceSystem, animated, allowLight)); if (!allowLight) { RemoveParticlesVisitor visitor; anim->getObjectRoot()->accept(visitor); visitor.remove(); } mObjects.insert(std::make_pair(ptr, anim.release())); } void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, bool weaponsShields) { insertBegin(ptr); ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor); // CreatureAnimation std::auto_ptr anim; if (weaponsShields) anim.reset(new CreatureWeaponAnimation(ptr, mesh, mResourceSystem)); else anim.reset(new CreatureAnimation(ptr, mesh, mResourceSystem)); mObjects.insert(std::make_pair(ptr, anim.release())); } void Objects::insertNPC(const MWWorld::Ptr &ptr) { insertBegin(ptr); ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor); std::auto_ptr anim (new NpcAnimation(ptr, osg::ref_ptr(ptr.getRefData().getBaseNode()), mResourceSystem)); mObjects.insert(std::make_pair(ptr, anim.release())); } bool Objects::removeObject (const MWWorld::Ptr& ptr) { if(!ptr.getRefData().getBaseNode()) return true; PtrAnimationMap::iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) { delete iter->second; mObjects.erase(iter); ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode()); ptr.getRefData().setBaseNode(NULL); return true; } return false; } void Objects::removeCell(const MWWorld::CellStore* store) { for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();) { if(iter->first.getCell() == store) { delete iter->second; mObjects.erase(iter++); } else ++iter; } CellMap::iterator cell = mCellSceneNodes.find(store); if(cell != mCellSceneNodes.end()) { cell->second->getParent(0)->removeChild(cell->second); mCellSceneNodes.erase(cell); } } void Objects::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { osg::Node* objectNode = cur.getRefData().getBaseNode(); if (!objectNode) return; MWWorld::CellStore *newCell = cur.getCell(); osg::Group* cellnode; if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { cellnode = new osg::Group; mRootNode->addChild(cellnode); mCellSceneNodes[newCell] = cellnode; } else { cellnode = mCellSceneNodes[newCell]; } osg::UserDataContainer* userDataContainer = objectNode->getUserDataContainer(); if (userDataContainer) for (unsigned int i=0; igetNumUserObjects(); ++i) { if (dynamic_cast(userDataContainer->getUserObject(i))) userDataContainer->setUserObject(i, new PtrHolder(cur)); } if (objectNode->getNumParents()) objectNode->getParent(0)->removeChild(objectNode); cellnode->addChild(objectNode); PtrAnimationMap::iterator iter = mObjects.find(old); if(iter != mObjects.end()) { Animation *anim = iter->second; mObjects.erase(iter); anim->updatePtr(cur); mObjects[cur] = anim; } } Animation* Objects::getAnimation(const MWWorld::Ptr &ptr) { PtrAnimationMap::const_iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) return iter->second; return NULL; } const Animation* Objects::getAnimation(const MWWorld::ConstPtr &ptr) const { PtrAnimationMap::const_iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) return iter->second; return NULL; } } openmw-openmw-0.38.0/apps/openmw/mwrender/objects.hpp000066400000000000000000000041411264522266000226540ustar00rootroot00000000000000#ifndef GAME_RENDER_OBJECTS_H #define GAME_RENDER_OBJECTS_H #include #include #include #include #include #include "../mwworld/ptr.hpp" namespace osg { class Group; } namespace osgUtil { class IncrementalCompileOperation; } namespace Resource { class ResourceSystem; } namespace MWWorld { class CellStore; } namespace MWRender{ class Animation; class PtrHolder : public osg::Object { public: PtrHolder(MWWorld::Ptr ptr) : mPtr(ptr) { } PtrHolder() { } PtrHolder(const PtrHolder& copy, const osg::CopyOp& copyop) : mPtr(copy.mPtr) { } META_Object(MWRender, PtrHolder) MWWorld::Ptr mPtr; }; class Objects{ typedef std::map PtrAnimationMap; typedef std::map > CellMap; CellMap mCellSceneNodes; PtrAnimationMap mObjects; osg::ref_ptr mRootNode; void insertBegin(const MWWorld::Ptr& ptr); Resource::ResourceSystem* mResourceSystem; public: Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr rootNode); ~Objects(); /// @param animated Attempt to load separate keyframes from a .kf file matching the model file? /// @param allowLight If false, no lights will be created, and particles systems will be removed. void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool animated=false, bool allowLight=true); void insertNPC(const MWWorld::Ptr& ptr); void insertCreature (const MWWorld::Ptr& ptr, const std::string& model, bool weaponsShields); Animation* getAnimation(const MWWorld::Ptr &ptr); const Animation* getAnimation(const MWWorld::ConstPtr &ptr) const; bool removeObject (const MWWorld::Ptr& ptr); ///< \return found? void removeCell(const MWWorld::CellStore* store); /// Updates containing cell for object rendering data void updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); private: void operator = (const Objects&); Objects(const Objects&); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/pathgrid.cpp000066400000000000000000000200571264522266000230240ustar00rootroot00000000000000#include "pathgrid.hpp" #include #include #include #include #include #include #include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/pathfinding.hpp" #include "../mwmechanics/coordinateconverter.hpp" #include "vismask.hpp" namespace MWRender { static const int POINT_MESH_BASE = 35; osg::ref_ptr Pathgrid::createPathgridLines(const ESM::Pathgrid *pathgrid) { osg::ref_ptr geom = new osg::Geometry; osg::ref_ptr vertices = new osg::Vec3Array; for(ESM::Pathgrid::EdgeList::const_iterator it = pathgrid->mEdges.begin(); it != pathgrid->mEdges.end(); ++it) { const ESM::Pathgrid::Edge &edge = *it; const ESM::Pathgrid::Point &p1 = pathgrid->mPoints[edge.mV0], &p2 = pathgrid->mPoints[edge.mV1]; osg::Vec3f direction = MWMechanics::PathFinder::MakeOsgVec3(p2) - MWMechanics::PathFinder::MakeOsgVec3(p1); osg::Vec3f lineDisplacement = (direction^osg::Vec3f(0,0,1)); lineDisplacement.normalize(); lineDisplacement = lineDisplacement * POINT_MESH_BASE + osg::Vec3f(0, 0, 10); // move lines up a little, so they will be less covered by meshes/landscape vertices->push_back(MWMechanics::PathFinder::MakeOsgVec3(p1) + lineDisplacement); vertices->push_back(MWMechanics::PathFinder::MakeOsgVec3(p2) + lineDisplacement); } geom->setVertexArray(vertices); geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertices->size())); osg::ref_ptr colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.f, 1.f, 0.f, 1.f)); geom->setColorArray(colors, osg::Array::BIND_OVERALL); return geom; } osg::ref_ptr Pathgrid::createPathgridPoints(const ESM::Pathgrid *pathgrid) { osg::ref_ptr geom = new osg::Geometry; const float height = POINT_MESH_BASE * sqrtf(2); osg::ref_ptr vertices = new osg::Vec3Array; osg::ref_ptr indices = new osg::UShortArray; bool first = true; unsigned short startIndex = 0; for(ESM::Pathgrid::PointList::const_iterator it = pathgrid->mPoints.begin(); it != pathgrid->mPoints.end(); ++it, startIndex += 6) { osg::Vec3f pointPos(MWMechanics::PathFinder::MakeOsgVec3(*it)); if (!first) { // degenerate triangle from previous octahedron indices->push_back(startIndex - 4); // 2nd point of previous octahedron indices->push_back(startIndex); // start point of current octahedron } float pointMeshBase = static_cast(POINT_MESH_BASE); vertices->push_back(pointPos + osg::Vec3f(0, 0, height)); // 0 vertices->push_back(pointPos + osg::Vec3f(-pointMeshBase, -pointMeshBase, 0)); // 1 vertices->push_back(pointPos + osg::Vec3f(pointMeshBase, -pointMeshBase, 0)); // 2 vertices->push_back(pointPos + osg::Vec3f(pointMeshBase, pointMeshBase, 0)); // 3 vertices->push_back(pointPos + osg::Vec3f(-pointMeshBase, pointMeshBase, 0)); // 4 vertices->push_back(pointPos + osg::Vec3f(0, 0, -height)); // 5 indices->push_back(startIndex + 0); indices->push_back(startIndex + 1); indices->push_back(startIndex + 2); indices->push_back(startIndex + 5); indices->push_back(startIndex + 3); indices->push_back(startIndex + 4); // degenerates indices->push_back(startIndex + 4); indices->push_back(startIndex + 5); indices->push_back(startIndex + 5); // end degenerates indices->push_back(startIndex + 1); indices->push_back(startIndex + 4); indices->push_back(startIndex + 0); indices->push_back(startIndex + 3); indices->push_back(startIndex + 2); first = false; } geom->setVertexArray(vertices); geom->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, indices->size(), &(*indices)[0])); osg::ref_ptr colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.f, 0.f, 0.f, 1.f)); geom->setColorArray(colors, osg::Array::BIND_OVERALL); return geom; } Pathgrid::Pathgrid(osg::ref_ptr root) : mPathgridEnabled(false) , mRootNode(root) , mPathGridRoot(NULL) , mInteriorPathgridNode(NULL) { } Pathgrid::~Pathgrid() { if (mPathgridEnabled) { togglePathgrid(); } } bool Pathgrid::toggleRenderMode (int mode){ switch (mode) { case Render_Pathgrid: togglePathgrid(); return mPathgridEnabled; default: return false; } return false; } void Pathgrid::addCell(const MWWorld::CellStore *store) { mActiveCells.push_back(store); if (mPathgridEnabled) enableCellPathgrid(store); } void Pathgrid::removeCell(const MWWorld::CellStore *store) { mActiveCells.erase(std::remove(mActiveCells.begin(), mActiveCells.end(), store), mActiveCells.end()); if (mPathgridEnabled) disableCellPathgrid(store); } void Pathgrid::togglePathgrid() { mPathgridEnabled = !mPathgridEnabled; if (mPathgridEnabled) { // add path grid meshes to already loaded cells mPathGridRoot = new osg::Group; mPathGridRoot->setNodeMask(Mask_Debug); mRootNode->addChild(mPathGridRoot); for(CellList::iterator it = mActiveCells.begin(); it != mActiveCells.end(); ++it) { enableCellPathgrid(*it); } } else { // remove path grid meshes from already loaded cells for(CellList::iterator it = mActiveCells.begin(); it != mActiveCells.end(); ++it) { disableCellPathgrid(*it); } if (mPathGridRoot) { mRootNode->removeChild(mPathGridRoot); mPathGridRoot = NULL; } } } void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store) { MWBase::World* world = MWBase::Environment::get().getWorld(); const ESM::Pathgrid *pathgrid = world->getStore().get().search(*store->getCell()); if (!pathgrid) return; osg::Vec3f cellPathGridPos(0, 0, 0); MWMechanics::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos); osg::ref_ptr cellPathGrid = new osg::PositionAttitudeTransform; cellPathGrid->setPosition(cellPathGridPos); osg::ref_ptr lineGeode = new osg::Geode; osg::ref_ptr lines = createPathgridLines(pathgrid); lineGeode->addDrawable(lines); osg::ref_ptr pointGeode = new osg::Geode; osg::ref_ptr points = createPathgridPoints(pathgrid); pointGeode->addDrawable(points); cellPathGrid->addChild(lineGeode); cellPathGrid->addChild(pointGeode); mPathGridRoot->addChild(cellPathGrid); if (store->getCell()->isExterior()) { mExteriorPathgridNodes[std::make_pair(store->getCell()->getGridX(), store->getCell()->getGridY())] = cellPathGrid; } else { assert(mInteriorPathgridNode == NULL); mInteriorPathgridNode = cellPathGrid; } } void Pathgrid::disableCellPathgrid(const MWWorld::CellStore *store) { if (store->getCell()->isExterior()) { ExteriorPathgridNodes::iterator it = mExteriorPathgridNodes.find(std::make_pair(store->getCell()->getGridX(), store->getCell()->getGridY())); if (it != mExteriorPathgridNodes.end()) { mPathGridRoot->removeChild(it->second); mExteriorPathgridNodes.erase(it); } } else { if (mInteriorPathgridNode) { mPathGridRoot->removeChild(mInteriorPathgridNode); mInteriorPathgridNode = NULL; } } } } openmw-openmw-0.38.0/apps/openmw/mwrender/pathgrid.hpp000066400000000000000000000026101264522266000230240ustar00rootroot00000000000000#ifndef GAME_RENDER_MWSCENE_H #define GAME_RENDER_MWSCENE_H #include #include #include #include #include namespace ESM { struct Pathgrid; } namespace osg { class Group; class Geometry; } namespace MWWorld { class Ptr; class CellStore; } namespace MWRender { class Pathgrid { bool mPathgridEnabled; void togglePathgrid(); typedef std::vector CellList; CellList mActiveCells; osg::ref_ptr mRootNode; osg::ref_ptr mPathGridRoot; typedef std::map, osg::ref_ptr > ExteriorPathgridNodes; ExteriorPathgridNodes mExteriorPathgridNodes; osg::ref_ptr mInteriorPathgridNode; void enableCellPathgrid(const MWWorld::CellStore *store); void disableCellPathgrid(const MWWorld::CellStore *store); // path grid meshes osg::ref_ptr createPathgridLines(const ESM::Pathgrid *pathgrid); osg::ref_ptr createPathgridPoints(const ESM::Pathgrid *pathgrid); public: Pathgrid(osg::ref_ptr root); ~Pathgrid(); bool toggleRenderMode (int mode); void addCell(const MWWorld::CellStore* store); void removeCell(const MWWorld::CellStore* store); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/renderbin.hpp000066400000000000000000000010521264522266000231710ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_RENDERBIN_H #define OPENMW_MWRENDER_RENDERBIN_H namespace MWRender { /// Defines the render bin numbers used in the OpenMW scene graph. The bin with the lowest number is rendered first. enum RenderBins { RenderBin_Sky = -1, RenderBin_Default = 0, // osg::StateSet::OPAQUE_BIN RenderBin_Water = 9, RenderBin_DepthSorted = 10, // osg::StateSet::TRANSPARENT_BIN RenderBin_OcclusionQuery = 11, RenderBin_FirstPerson = 12, RenderBin_SunGlare = 13 }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/renderinginterface.hpp000066400000000000000000000004441264522266000250630ustar00rootroot00000000000000#ifndef GAME_RENDERING_INTERFACE_H #define GAME_RENDERING_INTERFACE_H namespace MWRender { class Objects; class Actors; class RenderingInterface { public: virtual MWRender::Objects& getObjects() = 0; virtual ~RenderingInterface(){} }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/renderingmanager.cpp000066400000000000000000000742101264522266000245320ustar00rootroot00000000000000#include "renderingmanager.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwworld/fallback.hpp" #include "../mwworld/cellstore.hpp" #include "sky.hpp" #include "effectmanager.hpp" #include "npcanimation.hpp" #include "vismask.hpp" #include "pathgrid.hpp" #include "camera.hpp" #include "water.hpp" #include "terrainstorage.hpp" namespace MWRender { class StateUpdater : public SceneUtil::StateSetUpdater { public: StateUpdater() : mFogStart(0.f) , mFogEnd(0.f) , mWireframe(false) { } virtual void setDefaults(osg::StateSet *stateset) { osg::LightModel* lightModel = new osg::LightModel; stateset->setAttribute(lightModel, osg::StateAttribute::ON); osg::Fog* fog = new osg::Fog; fog->setMode(osg::Fog::LINEAR); stateset->setAttributeAndModes(fog, osg::StateAttribute::ON); if (mWireframe) { osg::PolygonMode* polygonmode = new osg::PolygonMode; polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); stateset->setAttributeAndModes(polygonmode, osg::StateAttribute::ON); } else stateset->removeAttribute(osg::StateAttribute::POLYGONMODE); } virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*) { osg::LightModel* lightModel = static_cast(stateset->getAttribute(osg::StateAttribute::LIGHTMODEL)); lightModel->setAmbientIntensity(mAmbientColor); osg::Fog* fog = static_cast(stateset->getAttribute(osg::StateAttribute::FOG)); fog->setColor(mFogColor); fog->setStart(mFogStart); fog->setEnd(mFogEnd); } void setAmbientColor(const osg::Vec4f& col) { mAmbientColor = col; } void setFogColor(const osg::Vec4f& col) { mFogColor = col; } void setFogStart(float start) { mFogStart = start; } void setFogEnd(float end) { mFogEnd = end; } void setWireframe(bool wireframe) { if (mWireframe != wireframe) { mWireframe = wireframe; reset(); } } bool getWireframe() const { return mWireframe; } private: osg::Vec4f mAmbientColor; osg::Vec4f mFogColor; float mFogStart; float mFogEnd; bool mWireframe; }; RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback, const std::string& resourcePath) : mViewer(viewer) , mRootNode(rootNode) , mResourceSystem(resourceSystem) , mFogDepth(0.f) , mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor")) , mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight")) , mUnderwaterFog(0.f) , mUnderwaterIndoorFog(fallback->getFallbackFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) , mFieldOfViewOverride(0.f) , mFieldOfViewOverridden(false) { resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); osg::ref_ptr lightRoot = new SceneUtil::LightManager; lightRoot->setLightingMask(Mask_Lighting); mLightRoot = lightRoot; lightRoot->setStartLight(1); mRootNode->addChild(lightRoot); mPathgrid.reset(new Pathgrid(mRootNode)); mObjects.reset(new Objects(mResourceSystem, lightRoot)); mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation()); mEffectManager.reset(new EffectManager(lightRoot, mResourceSystem)); mWater.reset(new Water(mRootNode, lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); mTerrain.reset(new Terrain::TerrainGrid(lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), new TerrainStorage(mResourceSystem->getVFS(), false), Mask_Terrain)); mCamera.reset(new Camera(mViewer->getCamera())); mViewer->setLightingMode(osgViewer::View::NO_LIGHT); osg::ref_ptr source = new osg::LightSource; source->setNodeMask(Mask_Lighting); mSunLight = new osg::Light; source->setLight(mSunLight); mSunLight->setDiffuse(osg::Vec4f(0,0,0,1)); mSunLight->setAmbient(osg::Vec4f(0,0,0,1)); mSunLight->setSpecular(osg::Vec4f(0,0,0,0)); mSunLight->setConstantAttenuation(1.f); lightRoot->addChild(source); lightRoot->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON); lightRoot->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON); lightRoot->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); lightRoot->setNodeMask(Mask_Scene); lightRoot->setName("Scene Root"); mSky.reset(new SkyManager(lightRoot, resourceSystem->getSceneManager())); source->setStateSetModes(*mRootNode->getOrCreateStateSet(), osg::StateAttribute::ON); mStateUpdater = new StateUpdater; lightRoot->addUpdateCallback(mStateUpdater); osg::Camera::CullingMode cullingMode = osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING; if (!Settings::Manager::getBool("small feature culling", "Camera")) cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING); else cullingMode |= osg::CullStack::SMALL_FEATURE_CULLING; mViewer->getCamera()->setCullingMode( cullingMode ); mViewer->getCamera()->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); mViewer->getCamera()->setCullingMode(cullingMode); mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater)); mNearClip = Settings::Manager::getFloat("near clip", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); mFieldOfView = Settings::Manager::getFloat("field of view", "Camera"); mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera"); updateProjectionMatrix(); mStateUpdater->setFogEnd(mViewDistance); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance)); } RenderingManager::~RenderingManager() { } MWRender::Objects& RenderingManager::getObjects() { return *mObjects.get(); } Resource::ResourceSystem* RenderingManager::getResourceSystem() { return mResourceSystem; } osg::Group* RenderingManager::getLightRoot() { return mLightRoot.get(); } void RenderingManager::setNightEyeFactor(float factor) { if (factor != mNightEyeFactor) { mNightEyeFactor = factor; updateAmbient(); } } void RenderingManager::setAmbientColour(const osg::Vec4f &colour) { mAmbientColor = colour; updateAmbient(); } void RenderingManager::skySetDate(int day, int month) { mSky->setDate(day, month); } int RenderingManager::skyGetMasserPhase() const { return mSky->getMasserPhase(); } int RenderingManager::skyGetSecundaPhase() const { return mSky->getSecundaPhase(); } void RenderingManager::skySetMoonColour(bool red) { mSky->setMoonColour(red); } void RenderingManager::configureAmbient(const ESM::Cell *cell) { setAmbientColour(SceneUtil::colourFromRGB(cell->mAmbi.mAmbient)); mSunLight->setDiffuse(SceneUtil::colourFromRGB(cell->mAmbi.mSunlight)); mSunLight->setDirection(osg::Vec3f(1.f,-1.f,-1.f)); } void RenderingManager::setSunColour(const osg::Vec4f &colour) { // need to wrap this in a StateUpdater? mSunLight->setDiffuse(colour); mSunLight->setSpecular(colour); } void RenderingManager::setSunDirection(const osg::Vec3f &direction) { osg::Vec3 position = direction * -1; // need to wrap this in a StateUpdater? mSunLight->setPosition(osg::Vec4(position.x(), position.y(), position.z(), 0)); mSky->setSunDirection(position); } osg::Vec3f RenderingManager::getEyePos() { osg::Vec3d eye = mViewer->getCameraManipulator()->getMatrix().getTrans(); return eye; } void RenderingManager::addCell(const MWWorld::CellStore *store) { mPathgrid->addCell(store); mWater->changeCell(store); if (store->getCell()->isExterior()) mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY()); } void RenderingManager::removeCell(const MWWorld::CellStore *store) { mPathgrid->removeCell(store); mObjects->removeCell(store); if (store->getCell()->isExterior()) mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY()); mWater->removeCell(store); } void RenderingManager::setSkyEnabled(bool enabled) { mSky->setEnabled(enabled); } bool RenderingManager::toggleRenderMode(RenderMode mode) { if (mode == Render_CollisionDebug || mode == Render_Pathgrid) return mPathgrid->toggleRenderMode(mode); else if (mode == Render_Wireframe) { bool wireframe = !mStateUpdater->getWireframe(); mStateUpdater->setWireframe(wireframe); return wireframe; } else if (mode == Render_Water) { return mWater->toggle(); } else if (mode == Render_Scene) { int mask = mViewer->getCamera()->getCullMask(); bool enabled = mask&Mask_Scene; enabled = !enabled; if (enabled) mask |= Mask_Scene; else mask &= ~Mask_Scene; mViewer->getCamera()->setCullMask(mask); return enabled; } /* else //if (mode == Render_BoundingBoxes) { bool show = !mRendering.getScene()->getShowBoundingBoxes(); mRendering.getScene()->showBoundingBoxes(show); return show; } */ return false; } void RenderingManager::configureFog(const ESM::Cell *cell) { osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); configureFog (cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, color); } void RenderingManager::configureFog(float fogDepth, float underwaterFog, const osg::Vec4f &color) { mFogDepth = fogDepth; mFogColor = color; mUnderwaterFog = underwaterFog; } SkyManager* RenderingManager::getSkyManager() { return mSky.get(); } void RenderingManager::update(float dt, bool paused) { if (!paused) { mEffectManager->update(dt); mSky->update(dt); } mWater->update(dt); mCamera->update(dt, paused); osg::Vec3f focal, cameraPos; mCamera->getPosition(focal, cameraPos); mCurrentCameraPos = cameraPos; if (mWater->isUnderwater(cameraPos)) { setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); mStateUpdater->setFogStart(mViewDistance * (1 - mUnderwaterFog)); mStateUpdater->setFogEnd(mViewDistance); } else { setFogColor(mFogColor); if (mFogDepth == 0.f) { mStateUpdater->setFogStart(0.f); mStateUpdater->setFogEnd(std::numeric_limits::max()); } else { mStateUpdater->setFogStart(mViewDistance * (1 - mFogDepth)); mStateUpdater->setFogEnd(mViewDistance); } } } void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) { if(mPlayerAnimation.get()) mPlayerAnimation->updatePtr(ptr); mCamera->attachTo(ptr); } void RenderingManager::removePlayer(const MWWorld::Ptr &player) { mWater->removeEmitter(player); } void RenderingManager::rotateObject(const MWWorld::Ptr &ptr, const osg::Quat& rot) { if(ptr == mCamera->getTrackingPtr() && !mCamera->isVanityOrPreviewModeEnabled()) { mCamera->rotateCamera(-ptr.getRefData().getPosition().rot[0], -ptr.getRefData().getPosition().rot[2], false); } ptr.getRefData().getBaseNode()->setAttitude(rot); } void RenderingManager::moveObject(const MWWorld::Ptr &ptr, const osg::Vec3f &pos) { ptr.getRefData().getBaseNode()->setPosition(pos); } void RenderingManager::scaleObject(const MWWorld::Ptr &ptr, const osg::Vec3f &scale) { ptr.getRefData().getBaseNode()->setScale(scale); if (ptr == mCamera->getTrackingPtr()) // update height of camera mCamera->processViewChange(); } void RenderingManager::removeObject(const MWWorld::Ptr &ptr) { mObjects->removeObject(ptr); mWater->removeEmitter(ptr); } void RenderingManager::setWaterEnabled(bool enabled) { mWater->setEnabled(enabled); mSky->setWaterEnabled(enabled); } void RenderingManager::setWaterHeight(float height) { mWater->setHeight(height); mSky->setWaterHeight(height); } class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback { public: NotifyDrawCompletedCallback() : mDone(false) { } virtual void operator () (osg::RenderInfo& renderInfo) const { mMutex.lock(); mDone = true; mMutex.unlock(); mCondition.signal(); } void waitTillDone() { mMutex.lock(); if (mDone) return; mCondition.wait(&mMutex); mMutex.unlock(); } mutable OpenThreads::Condition mCondition; mutable OpenThreads::Mutex mMutex; mutable bool mDone; }; class NoTraverseCallback : public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { } }; void RenderingManager::screenshot(osg::Image *image, int w, int h) { osg::ref_ptr rttCamera (new osg::Camera); rttCamera->setNodeMask(Mask_RenderToTexture); rttCamera->attach(osg::Camera::COLOR_BUFFER, image); rttCamera->setRenderOrder(osg::Camera::PRE_RENDER); rttCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance); rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix()); rttCamera->setViewport(0, 0, w, h); osg::ref_ptr texture (new osg::Texture2D); texture->setInternalFormat(GL_RGB); texture->setTextureSize(w, h); texture->setResizeNonPowerOfTwoHint(false); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); rttCamera->attach(osg::Camera::COLOR_BUFFER, texture); image->setDataType(GL_UNSIGNED_BYTE); image->setPixelFormat(texture->getInternalFormat()); rttCamera->setUpdateCallback(new NoTraverseCallback); rttCamera->addChild(mLightRoot); rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI)); mRootNode->addChild(rttCamera); // The draw needs to complete before we can copy back our image. osg::ref_ptr callback (new NotifyDrawCompletedCallback); rttCamera->setFinalDrawCallback(callback); // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); callback->waitTillDone(); // now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); rttCamera->removeChildren(0, rttCamera->getNumChildren()); rttCamera->setGraphicsContext(NULL); mRootNode->removeChild(rttCamera); } osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) { if (!ptr.getRefData().getBaseNode()) return osg::Vec4f(); osg::ComputeBoundsVisitor computeBoundsVisitor; computeBoundsVisitor.setTraversalMask(~(Mask_ParticleSystem|Mask_Effect)); ptr.getRefData().getBaseNode()->accept(computeBoundsVisitor); osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f; for (int i=0; i<8; ++i) { osg::Vec3f corner = computeBoundsVisitor.getBoundingBox().corner(i); corner = corner * viewProj; float x = (corner.x() + 1.f) * 0.5f; float y = (corner.y() - 1.f) * (-0.5f); if (x < min_x) min_x = x; if (x > max_x) max_x = x; if (y < min_y) min_y = y; if (y > max_y) max_y = y; } return osg::Vec4f(min_x, min_y, max_x, max_y); } RenderingManager::RayResult getIntersectionResult (osgUtil::LineSegmentIntersector* intersector) { RenderingManager::RayResult result; result.mHit = false; if (intersector->containsIntersections()) { result.mHit = true; osgUtil::LineSegmentIntersector::Intersection intersection = intersector->getFirstIntersection(); result.mHitPointWorld = intersection.getWorldIntersectPoint(); result.mHitNormalWorld = intersection.getWorldIntersectNormal(); PtrHolder* ptrHolder = NULL; for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) { osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer(); if (!userDataContainer) continue; for (unsigned int i=0; igetNumUserObjects(); ++i) { if (PtrHolder* p = dynamic_cast(userDataContainer->getUserObject(i))) ptrHolder = p; } } if (ptrHolder) result.mHitObject = ptrHolder->mPtr; } return result; } osg::ref_ptr createIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors) { osg::ref_ptr intersectionVisitor( new osgUtil::IntersectionVisitor(intersector)); int mask = intersectionVisitor->getTraversalMask(); mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater); if (ignorePlayer) mask &= ~(Mask_Player); if (ignoreActors) mask &= ~(Mask_Actor|Mask_Player); intersectionVisitor->setTraversalMask(mask); return intersectionVisitor; } RenderingManager::RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors) { osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::MODEL, origin, dest)); intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST); mRootNode->accept(*createIntersectionVisitor(intersector, ignorePlayer, ignoreActors)); return getIntersectionResult(intersector); } RenderingManager::RayResult RenderingManager::castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors) { osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::PROJECTION, nX * 2.f - 1.f, nY * (-2.f) + 1.f)); osg::Vec3d dist (0.f, 0.f, -maxDistance); dist = dist * mViewer->getCamera()->getProjectionMatrix(); osg::Vec3d end = intersector->getEnd(); end.z() = dist.z(); intersector->setEnd(end); intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST); mViewer->getCamera()->accept(*createIntersectionVisitor(intersector, ignorePlayer, ignoreActors)); return getIntersectionResult(intersector); } void RenderingManager::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) { mObjects->updatePtr(old, updated); } void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale) { mEffectManager->addEffect(model, texture, worldPosition, scale); } void RenderingManager::notifyWorldSpaceChanged() { mEffectManager->clear(); mWater->clearRipples(); } void RenderingManager::clear() { mSky->setMoonColour(false); notifyWorldSpaceChanged(); } MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) { if (mPlayerAnimation.get() && ptr == mPlayerAnimation->getPtr()) return mPlayerAnimation.get(); return mObjects->getAnimation(ptr); } const MWRender::Animation* RenderingManager::getAnimation(const MWWorld::ConstPtr &ptr) const { if (mPlayerAnimation.get() && ptr == mPlayerAnimation->getPtr()) return mPlayerAnimation.get(); return mObjects->getAnimation(ptr); } void RenderingManager::setupPlayer(const MWWorld::Ptr &player) { if (!mPlayerNode) { mPlayerNode = new SceneUtil::PositionAttitudeTransform; mPlayerNode->setNodeMask(Mask_Player); mLightRoot->addChild(mPlayerNode); } mPlayerNode->setUserDataContainer(new osg::DefaultUserDataContainer); mPlayerNode->getUserDataContainer()->addUserObject(new PtrHolder(player)); player.getRefData().setBaseNode(mPlayerNode); mWater->addEmitter(player); } void RenderingManager::renderPlayer(const MWWorld::Ptr &player) { mPlayerAnimation.reset(new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, false, NpcAnimation::VM_Normal, mFirstPersonFieldOfView)); mCamera->setAnimation(mPlayerAnimation.get()); mCamera->attachTo(player); } void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) { NpcAnimation *anim = NULL; if(ptr == mPlayerAnimation->getPtr()) anim = mPlayerAnimation.get(); else anim = dynamic_cast(mObjects->getAnimation(ptr)); if(anim) { anim->rebuild(); if(mCamera->getTrackingPtr() == ptr) { mCamera->attachTo(ptr); mCamera->setAnimation(anim); } } } void RenderingManager::addWaterRippleEmitter(const MWWorld::Ptr &ptr) { mWater->addEmitter(ptr); } void RenderingManager::removeWaterRippleEmitter(const MWWorld::Ptr &ptr) { mWater->removeEmitter(ptr); } void RenderingManager::emitWaterRipple(const osg::Vec3f &pos) { mWater->emitRipple(pos); } void RenderingManager::updateProjectionMatrix() { double aspect = mViewer->getCamera()->getViewport()->aspectRatio(); float fov = mFieldOfView; if (mFieldOfViewOverridden) fov = mFieldOfViewOverride; mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); } void RenderingManager::updateTextureFiltering() { mResourceSystem->getTextureManager()->setFilterSettings( Settings::Manager::getString("texture mag filter", "General"), Settings::Manager::getString("texture min filter", "General"), Settings::Manager::getString("texture mipmap", "General"), Settings::Manager::getInt("anisotropy", "General"), mViewer ); } void RenderingManager::updateAmbient() { osg::Vec4f color = mAmbientColor; if (mNightEyeFactor > 0.f) color += osg::Vec4f(0.7, 0.7, 0.7, 0.0) * mNightEyeFactor; mStateUpdater->setAmbientColor(color); } void RenderingManager::setFogColor(const osg::Vec4f &color) { mViewer->getCamera()->setClearColor(color); mStateUpdater->setFogColor(color); } void RenderingManager::processChangedSettings(const Settings::CategorySettingVector &changed) { for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) { if (it->first == "Camera" && it->second == "field of view") { mFieldOfView = Settings::Manager::getFloat("field of view", "Camera"); updateProjectionMatrix(); } else if (it->first == "Camera" && it->second == "viewing distance") { mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); mStateUpdater->setFogEnd(mViewDistance); updateProjectionMatrix(); } else if (it->first == "General" && (it->second == "texture filter" || it->second == "texture mipmap" || it->second == "anisotropy")) updateTextureFiltering(); else if (it->first == "Water") mWater->processChangedSettings(changed); } } float RenderingManager::getNearClipDistance() const { return mNearClip; } float RenderingManager::getTerrainHeightAt(const osg::Vec3f &pos) { return mTerrain->getHeightAt(pos); } bool RenderingManager::vanityRotateCamera(const float *rot) { if(!mCamera->isVanityOrPreviewModeEnabled()) return false; mCamera->rotateCamera(rot[0], rot[2], true); return true; } void RenderingManager::setCameraDistance(float dist, bool adjust, bool override) { if(!mCamera->isVanityOrPreviewModeEnabled() && !mCamera->isFirstPerson()) { if(mCamera->isNearest() && dist > 0.f) mCamera->toggleViewMode(); else mCamera->setCameraDistance(-dist / 120.f * 10, adjust, override); } else if(mCamera->isFirstPerson() && dist < 0.f) { mCamera->toggleViewMode(); mCamera->setCameraDistance(0.f, false, override); } } void RenderingManager::resetCamera() { mCamera->reset(); } float RenderingManager::getCameraDistance() const { return mCamera->getCameraDistance(); } Camera* RenderingManager::getCamera() { return mCamera.get(); } const osg::Vec3f &RenderingManager::getCameraPosition() const { return mCurrentCameraPos; } void RenderingManager::togglePOV() { mCamera->toggleViewMode(); } void RenderingManager::togglePreviewMode(bool enable) { mCamera->togglePreviewMode(enable); } bool RenderingManager::toggleVanityMode(bool enable) { return mCamera->toggleVanityMode(enable); } void RenderingManager::allowVanityMode(bool allow) { mCamera->allowVanityMode(allow); } void RenderingManager::togglePlayerLooking(bool enable) { mCamera->togglePlayerLooking(enable); } void RenderingManager::changeVanityModeScale(float factor) { if(mCamera->isVanityOrPreviewModeEnabled()) mCamera->setCameraDistance(-factor/120.f*10, true, true); } void RenderingManager::overrideFieldOfView(float val) { if (mFieldOfViewOverridden != true || mFieldOfViewOverride != val) { mFieldOfViewOverridden = true; mFieldOfViewOverride = val; updateProjectionMatrix(); } } void RenderingManager::resetFieldOfView() { if (mFieldOfViewOverridden == true) { mFieldOfViewOverridden = false; updateProjectionMatrix(); } } } openmw-openmw-0.38.0/apps/openmw/mwrender/renderingmanager.hpp000066400000000000000000000153411264522266000245370ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_RENDERINGMANAGER_H #define OPENMW_MWRENDER_RENDERINGMANAGER_H #include #include #include #include "objects.hpp" #include "renderinginterface.hpp" #include "rendermode.hpp" namespace osg { class Group; class PositionAttitudeTransform; } namespace Resource { class ResourceSystem; } namespace osgViewer { class Viewer; } namespace ESM { struct Cell; } namespace Terrain { class World; } namespace MWWorld { class Fallback; } namespace MWRender { class StateUpdater; class EffectManager; class SkyManager; class NpcAnimation; class Pathgrid; class Camera; class Water; class RenderingManager : public MWRender::RenderingInterface { public: RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback, const std::string& resourcePath); ~RenderingManager(); MWRender::Objects& getObjects(); Resource::ResourceSystem* getResourceSystem(); osg::Group* getLightRoot(); void setNightEyeFactor(float factor); void setAmbientColour(const osg::Vec4f& colour); void skySetDate(int day, int month); int skyGetMasserPhase() const; int skyGetSecundaPhase() const; void skySetMoonColour(bool red); void setSunDirection(const osg::Vec3f& direction); void setSunColour(const osg::Vec4f& colour); void configureAmbient(const ESM::Cell* cell); void configureFog(const ESM::Cell* cell); void configureFog(float fogDepth, float underwaterFog, const osg::Vec4f& colour); void addCell(const MWWorld::CellStore* store); void removeCell(const MWWorld::CellStore* store); void updatePtr(const MWWorld::Ptr& old, const MWWorld::Ptr& updated); void rotateObject(const MWWorld::Ptr& ptr, const osg::Quat& rot); void moveObject(const MWWorld::Ptr& ptr, const osg::Vec3f& pos); void scaleObject(const MWWorld::Ptr& ptr, const osg::Vec3f& scale); void removeObject(const MWWorld::Ptr& ptr); void setWaterEnabled(bool enabled); void setWaterHeight(float level); /// Take a screenshot of w*h onto the given image, not including the GUI. void screenshot(osg::Image* image, int w, int h); struct RayResult { bool mHit; osg::Vec3f mHitNormalWorld; osg::Vec3f mHitPointWorld; MWWorld::Ptr mHitObject; }; RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors=false); /// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen coordinates, /// where (0,0) is the top left corner. RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false); /// Get the bounding box of the given object in screen coordinates as (minX, minY, maxX, maxY), with (0,0) being the top left corner. osg::Vec4f getScreenBounds(const MWWorld::Ptr& ptr); void setSkyEnabled(bool enabled); bool toggleRenderMode(RenderMode mode); SkyManager* getSkyManager(); osg::Vec3f getEyePos(); void spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale = 1.f); /// Clear all savegame-specific data void clear(); /// Clear all worldspace-specific data void notifyWorldSpaceChanged(); void update(float dt, bool paused); Animation* getAnimation(const MWWorld::Ptr& ptr); const Animation* getAnimation(const MWWorld::ConstPtr& ptr) const; void addWaterRippleEmitter(const MWWorld::Ptr& ptr); void removeWaterRippleEmitter(const MWWorld::Ptr& ptr); void emitWaterRipple(const osg::Vec3f& pos); void updatePlayerPtr(const MWWorld::Ptr &ptr); void removePlayer(const MWWorld::Ptr& player); void setupPlayer(const MWWorld::Ptr& player); void renderPlayer(const MWWorld::Ptr& player); void rebuildPtr(const MWWorld::Ptr& ptr); void processChangedSettings(const Settings::CategorySettingVector& settings); float getNearClipDistance() const; float getTerrainHeightAt(const osg::Vec3f& pos); // camera stuff bool vanityRotateCamera(const float *rot); void setCameraDistance(float dist, bool adjust, bool override); void resetCamera(); float getCameraDistance() const; Camera* getCamera(); const osg::Vec3f& getCameraPosition() const; void togglePOV(); void togglePreviewMode(bool enable); bool toggleVanityMode(bool enable); void allowVanityMode(bool allow); void togglePlayerLooking(bool enable); void changeVanityModeScale(float factor); /// temporarily override the field of view with given value. void overrideFieldOfView(float val); /// reset a previous overrideFieldOfView() call, i.e. revert to field of view specified in the settings file. void resetFieldOfView(); private: void updateProjectionMatrix(); void updateTextureFiltering(); void updateAmbient(); void setFogColor(const osg::Vec4f& color); osg::ref_ptr mViewer; osg::ref_ptr mRootNode; osg::ref_ptr mLightRoot; Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mSunLight; std::auto_ptr mPathgrid; std::auto_ptr mObjects; std::auto_ptr mWater; std::auto_ptr mTerrain; std::auto_ptr mSky; std::auto_ptr mEffectManager; std::auto_ptr mPlayerAnimation; osg::ref_ptr mPlayerNode; std::auto_ptr mCamera; osg::Vec3f mCurrentCameraPos; osg::ref_ptr mStateUpdater; float mFogDepth; osg::Vec4f mUnderwaterColor; float mUnderwaterWeight; float mUnderwaterFog; float mUnderwaterIndoorFog; osg::Vec4f mFogColor; osg::Vec4f mAmbientColor; float mNightEyeFactor; float mNearClip; float mViewDistance; float mFieldOfViewOverride; bool mFieldOfViewOverridden; float mFieldOfView; float mFirstPersonFieldOfView; void operator = (const RenderingManager&); RenderingManager(const RenderingManager&); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/rendermode.hpp000066400000000000000000000004501264522266000233460ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_RENDERMODE_H #define OPENMW_MWRENDER_RENDERMODE_H namespace MWRender { enum RenderMode { Render_CollisionDebug, Render_Wireframe, Render_Pathgrid, Render_BoundingBoxes, Render_Water, Render_Scene }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/ripplesimulation.cpp000066400000000000000000000171041264522266000246210ustar00rootroot00000000000000#include "ripplesimulation.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vismask.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/fallback.hpp" #include "../mwmechanics/actorutil.hpp" namespace { void createWaterRippleStateSet(Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback, osg::Node* node) { int rippleFrameCount = fallback->getFallbackInt("Water_RippleFrameCount"); if (rippleFrameCount <= 0) return; std::string tex = fallback->getFallbackString("Water_RippleTexture"); std::vector > textures; for (int i=0; igetTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT)); } osg::ref_ptr controller (new NifOsg::FlipController(0, 0.3f/rippleFrameCount, textures)); controller->setSource(boost::shared_ptr(new SceneUtil::FrameTimeSource)); node->addUpdateCallback(controller); osg::ref_ptr stateset (new osg::StateSet); stateset->setMode(GL_BLEND, osg::StateAttribute::ON); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON); osg::ref_ptr depth (new osg::Depth); depth->setWriteMask(false); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); osg::ref_ptr polygonOffset (new osg::PolygonOffset); polygonOffset->setUnits(-1); polygonOffset->setFactor(-1); stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); osg::ref_ptr mat (new osg::Material); mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); mat->setColorMode(osg::Material::DIFFUSE); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); node->setStateSet(stateset); } } namespace MWRender { RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback) : mParent(parent) { osg::ref_ptr geode (new osg::Geode); mParticleSystem = new osgParticle::ParticleSystem; geode->addDrawable(mParticleSystem); mParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED); mParticleSystem->setAlignVectorX(osg::Vec3f(1,0,0)); mParticleSystem->setAlignVectorY(osg::Vec3f(0,1,0)); osgParticle::Particle& particleTemplate = mParticleSystem->getDefaultParticleTemplate(); particleTemplate.setSizeRange(osgParticle::rangef(15, 180)); particleTemplate.setColorRange(osgParticle::rangev4(osg::Vec4f(1,1,1,0.7), osg::Vec4f(1,1,1,0.7))); particleTemplate.setAlphaRange(osgParticle::rangef(1.f, 0.f)); particleTemplate.setAngularVelocity(osg::Vec3f(0,0,fallback->getFallbackFloat("Water_RippleRotSpeed"))); particleTemplate.setLifeTime(fallback->getFallbackFloat("Water_RippleLifetime")); osg::ref_ptr updater (new osgParticle::ParticleSystemUpdater); updater->addParticleSystem(mParticleSystem); mParticleNode = new osg::PositionAttitudeTransform; mParticleNode->addChild(updater); mParticleNode->addChild(geode); mParticleNode->setNodeMask(Mask_Effect); createWaterRippleStateSet(resourceSystem, fallback, mParticleNode); mParent->addChild(mParticleNode); } RippleSimulation::~RippleSimulation() { mParent->removeChild(mParticleNode); } void RippleSimulation::update(float dt) { for (std::vector::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it) { if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr()) { // fetch a new ptr (to handle cell change etc) // for non-player actors this is done in updateObjectCell it->mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); } osg::Vec3f currentPos (it->mPtr.getRefData().getPosition().asVec3()); if ( (currentPos - it->mLastEmitPosition).length() > 10 // Only emit when close to the water surface, not above it and not too deep in the water && MWBase::Environment::get().getWorld ()->isUnderwater (it->mPtr.getCell(), it->mPtr.getRefData().getPosition().asVec3()) && !MWBase::Environment::get().getWorld()->isSubmerged(it->mPtr)) { it->mLastEmitPosition = currentPos; currentPos.z() = mParticleNode->getPosition().z(); if (mParticleSystem->numParticles()-mParticleSystem->numDeadParticles() > 500) continue; // TODO: remove the oldest particle to make room? emitRipple(currentPos); } } } void RippleSimulation::addEmitter(const MWWorld::ConstPtr& ptr, float scale, float force) { Emitter newEmitter; newEmitter.mPtr = ptr; newEmitter.mScale = scale; newEmitter.mForce = force; newEmitter.mLastEmitPosition = osg::Vec3f(0,0,0); mEmitters.push_back (newEmitter); } void RippleSimulation::removeEmitter (const MWWorld::ConstPtr& ptr) { for (std::vector::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it) { if (it->mPtr == ptr) { mEmitters.erase(it); return; } } } void RippleSimulation::updateEmitterPtr (const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& ptr) { for (std::vector::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it) { if (it->mPtr == old) { it->mPtr = ptr; return; } } } void RippleSimulation::removeCell(const MWWorld::CellStore *store) { for (std::vector::iterator it = mEmitters.begin(); it != mEmitters.end();) { if ((it->mPtr.isInCell() && it->mPtr.getCell() == store) && it->mPtr != MWMechanics::getPlayer()) { it = mEmitters.erase(it); } else ++it; } } void RippleSimulation::emitRipple(const osg::Vec3f &pos) { if (std::abs(pos.z() - mParticleNode->getPosition().z()) < 20) { osgParticle::Particle* p = mParticleSystem->createParticle(NULL); p->setPosition(osg::Vec3f(pos.x(), pos.y(), 0.f)); p->setAngle(osg::Vec3f(0,0, Misc::Rng::rollProbability() * osg::PI * 2 - osg::PI)); } } void RippleSimulation::setWaterHeight(float height) { mParticleNode->setPosition(osg::Vec3f(0,0,height)); } void RippleSimulation::clear() { for (int i=0; inumParticles(); ++i) mParticleSystem->destroyParticle(i); } } openmw-openmw-0.38.0/apps/openmw/mwrender/ripplesimulation.hpp000066400000000000000000000032041264522266000246220ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_RIPPLESIMULATION_H #define OPENMW_MWRENDER_RIPPLESIMULATION_H #include #include "../mwworld/ptr.hpp" namespace osg { class Group; class PositionAttitudeTransform; } namespace osgParticle { class ParticleSystem; } namespace Resource { class ResourceSystem; } namespace MWWorld { class Fallback; } namespace MWRender { struct Emitter { MWWorld::ConstPtr mPtr; osg::Vec3f mLastEmitPosition; float mScale; float mForce; }; class RippleSimulation { public: RippleSimulation(osg::Group* parent, Resource::ResourceSystem* resourceSystem, const MWWorld::Fallback* fallback); ~RippleSimulation(); /// @param dt Time since the last frame void update(float dt); /// adds an emitter, position will be tracked automatically void addEmitter (const MWWorld::ConstPtr& ptr, float scale = 1.f, float force = 1.f); void removeEmitter (const MWWorld::ConstPtr& ptr); void updateEmitterPtr (const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& ptr); void removeCell(const MWWorld::CellStore* store); void emitRipple(const osg::Vec3f& pos); /// Change the height of the water surface, thus moving all ripples with it void setWaterHeight(float height); /// Remove all active ripples void clear(); private: osg::ref_ptr mParent; osg::ref_ptr mParticleSystem; osg::ref_ptr mParticleNode; std::vector mEmitters; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/rotatecontroller.cpp000066400000000000000000000024221264522266000246200ustar00rootroot00000000000000#include "rotatecontroller.hpp" #include namespace MWRender { RotateController::RotateController(osg::Node *relativeTo) : mEnabled(true) , mRelativeTo(relativeTo) { } void RotateController::setEnabled(bool enabled) { mEnabled = enabled; } void RotateController::setRotate(const osg::Quat &rotate) { mRotate = rotate; } void RotateController::operator()(osg::Node *node, osg::NodeVisitor *nv) { if (!mEnabled) { traverse(node, nv); return; } osg::MatrixTransform* transform = static_cast(node); osg::Matrix matrix = transform->getMatrix(); osg::Quat worldOrient = getWorldOrientation(node); osg::Quat orient = worldOrient * mRotate * worldOrient.inverse() * matrix.getRotate(); matrix.setRotate(orient); transform->setMatrix(matrix); traverse(node,nv); } osg::Quat RotateController::getWorldOrientation(osg::Node *node) { // this could be optimized later, we just need the world orientation, not the full matrix osg::MatrixList worldMats = node->getWorldMatrices(mRelativeTo); osg::Quat worldOrient; if (!worldMats.empty()) { osg::Matrixf worldMat = worldMats[0]; worldOrient = worldMat.getRotate(); } return worldOrient; } } openmw-openmw-0.38.0/apps/openmw/mwrender/rotatecontroller.hpp000066400000000000000000000015061264522266000246270ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_ROTATECONTROLLER_H #define OPENMW_MWRENDER_ROTATECONTROLLER_H #include #include namespace MWRender { /// Applies a rotation in \a relativeTo's space. /// @note Assumes that the node being rotated has its "original" orientation set every frame by a different controller. /// The rotation is then applied on top of that orientation. /// @note Must be set on a MatrixTransform. class RotateController : public osg::NodeCallback { public: RotateController(osg::Node* relativeTo); void setEnabled(bool enabled); void setRotate(const osg::Quat& rotate); virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); protected: osg::Quat getWorldOrientation(osg::Node* node); bool mEnabled; osg::Quat mRotate; osg::Node* mRelativeTo; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/sky.cpp000066400000000000000000001615511264522266000220350ustar00rootroot00000000000000#include "sky.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/fallback.hpp" #include "vismask.hpp" #include "renderbin.hpp" namespace { osg::ref_ptr createAlphaTrackingUnlitMaterial() { osg::ref_ptr mat = new osg::Material; mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); mat->setColorMode(osg::Material::DIFFUSE); return mat; } osg::ref_ptr createUnlitMaterial() { osg::ref_ptr mat = new osg::Material; mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); mat->setColorMode(osg::Material::OFF); return mat; } osg::ref_ptr createTexturedQuad(int numUvSets=1) { osg::ref_ptr geom = new osg::Geometry; osg::ref_ptr verts = new osg::Vec3Array; verts->push_back(osg::Vec3f(-0.5, -0.5, 0)); verts->push_back(osg::Vec3f(-0.5, 0.5, 0)); verts->push_back(osg::Vec3f(0.5, 0.5, 0)); verts->push_back(osg::Vec3f(0.5, -0.5, 0)); geom->setVertexArray(verts); osg::ref_ptr texcoords = new osg::Vec2Array; texcoords->push_back(osg::Vec2f(0, 0)); texcoords->push_back(osg::Vec2f(0, 1)); texcoords->push_back(osg::Vec2f(1, 1)); texcoords->push_back(osg::Vec2f(1, 0)); osg::ref_ptr colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.f, 1.f, 1.f, 1.f)); geom->setColorArray(colors, osg::Array::BIND_OVERALL); for (int i=0; isetTexCoordArray(i, texcoords, osg::Array::BIND_PER_VERTEX); geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4)); return geom; } } namespace MWRender { class AtmosphereUpdater : public SceneUtil::StateSetUpdater { public: void setEmissionColor(const osg::Vec4f& emissionColor) { mEmissionColor = emissionColor; } protected: virtual void setDefaults(osg::StateSet* stateset) { stateset->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) { osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); mat->setEmission(osg::Material::FRONT_AND_BACK, mEmissionColor); } private: osg::Vec4f mEmissionColor; }; class AtmosphereNightUpdater : public SceneUtil::StateSetUpdater { public: AtmosphereNightUpdater(Resource::TextureManager* textureManager) { // we just need a texture, its contents don't really matter mTexture = textureManager->getWarningTexture(); } void setFade(const float fade) { mColor.a() = fade; } protected: virtual void setDefaults(osg::StateSet* stateset) { osg::ref_ptr texEnv (new osg::TexEnvCombine); texEnv->setCombine_Alpha(osg::TexEnvCombine::MODULATE); texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); texEnv->setSource1_Alpha(osg::TexEnvCombine::CONSTANT); texEnv->setCombine_RGB(osg::TexEnvCombine::REPLACE); texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); stateset->setTextureAttributeAndModes(1, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); stateset->setTextureAttributeAndModes(1, texEnv, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) { osg::TexEnvCombine* texEnv = static_cast(stateset->getTextureAttribute(1, osg::StateAttribute::TEXENV)); texEnv->setConstantColor(mColor); } osg::ref_ptr mTexture; osg::Vec4f mColor; }; class CloudUpdater : public SceneUtil::StateSetUpdater { public: CloudUpdater() : mAnimationTimer(0.f) , mOpacity(0.f) { } void setAnimationTimer(float timer) { mAnimationTimer = timer; } void setTexture(osg::ref_ptr texture) { mTexture = texture; } void setEmissionColor(const osg::Vec4f& emissionColor) { mEmissionColor = emissionColor; } void setOpacity(float opacity) { mOpacity = opacity; } protected: virtual void setDefaults(osg::StateSet *stateset) { osg::ref_ptr texmat (new osg::TexMat); stateset->setTextureAttributeAndModes(0, texmat, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(1, texmat, osg::StateAttribute::ON); stateset->setAttribute(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); // need to set opacity on a separate texture unit, diffuse alpha is used by the vertex colors already osg::ref_ptr texEnvCombine (new osg::TexEnvCombine); texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); texEnvCombine->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); texEnvCombine->setSource1_Alpha(osg::TexEnvCombine::CONSTANT); texEnvCombine->setConstantColor(osg::Vec4f(1,1,1,1)); texEnvCombine->setCombine_Alpha(osg::TexEnvCombine::MODULATE); texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE); stateset->setTextureAttributeAndModes(1, texEnvCombine, osg::StateAttribute::ON); stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); stateset->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) { osg::TexMat* texMat = static_cast(stateset->getTextureAttribute(0, osg::StateAttribute::TEXMAT)); texMat->setMatrix(osg::Matrix::translate(osg::Vec3f(0, mAnimationTimer, 0.f))); stateset->setTextureAttribute(0, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); stateset->setTextureAttribute(1, mTexture, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); mat->setEmission(osg::Material::FRONT_AND_BACK, mEmissionColor); osg::TexEnvCombine* texEnvCombine = static_cast(stateset->getTextureAttribute(1, osg::StateAttribute::TEXENV)); texEnvCombine->setConstantColor(osg::Vec4f(1,1,1,mOpacity)); } private: float mAnimationTimer; osg::ref_ptr mTexture; osg::Vec4f mEmissionColor; float mOpacity; }; /// Transform that removes the eyepoint of the modelview matrix, /// i.e. its children are positioned relative to the camera. class CameraRelativeTransform : public osg::Transform { public: CameraRelativeTransform() { // Culling works in node-local space, not in camera space, so we can't cull this node correctly // That's not a problem though, children of this node can be culled just fine // Just make sure you do not place a CameraRelativeTransform deep in the scene graph setCullingActive(false); addCullCallback(new CullCallback); } CameraRelativeTransform(const CameraRelativeTransform& copy, const osg::CopyOp& copyop) : osg::Transform(copy, copyop) { } META_Node(MWRender, CameraRelativeTransform) const osg::Vec3f& getLastEyePoint() const { return mEyePoint; } virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const { if (nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { mEyePoint = static_cast(nv)->getEyePoint(); } if (_referenceFrame==RELATIVE_RF) { matrix.setTrans(osg::Vec3f(0.f,0.f,0.f)); return false; } else // absolute { matrix.makeIdentity(); return true; } } osg::BoundingSphere computeBound() const { return osg::BoundingSphere(osg::Vec3f(0,0,0), 0); } class CullCallback : public osg::NodeCallback { public: virtual void operator() (osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = static_cast(nv); // XXX have to remove unwanted culling plane of the water reflection camera // Remove all planes that aren't from the standard frustum unsigned int numPlanes = 4; if (cv->getCullingMode() & osg::CullSettings::NEAR_PLANE_CULLING) ++numPlanes; if (cv->getCullingMode() & osg::CullSettings::FAR_PLANE_CULLING) ++numPlanes; int mask = 0x1; int resultMask = cv->getProjectionCullingStack().back().getFrustum().getResultMask(); for (unsigned int i=0; igetProjectionCullingStack().back().getFrustum().getPlaneList().size(); ++i) { if (i >= numPlanes) { // turn off this culling plane resultMask &= (~mask); } mask <<= 1; } cv->getProjectionCullingStack().back().getFrustum().setResultMask(resultMask); cv->getCurrentCullingSet().getFrustum().setResultMask(resultMask); cv->getProjectionCullingStack().back().pushCurrentMask(); cv->getCurrentCullingSet().pushCurrentMask(); traverse(node, nv); cv->getProjectionCullingStack().back().popCurrentMask(); cv->getCurrentCullingSet().popCurrentMask(); } }; private: // eyePoint for the current frame mutable osg::Vec3f mEyePoint; }; class ModVertexAlphaVisitor : public osg::NodeVisitor { public: ModVertexAlphaVisitor(int meshType) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mMeshType(meshType) { } void apply(osg::Geode &geode) { for (unsigned int i=0; i colors = new osg::Vec4Array(geom->getVertexArray()->getNumElements()); for (unsigned int i=0; isize(); ++i) { float alpha = 1.f; if (mMeshType == 0) alpha = (i%2) ? 0.f : 1.f; // this is a cylinder, so every second vertex belongs to the bottom-most row else if (mMeshType == 1) { if (i>= 49 && i <= 64) alpha = 0.f; // bottom-most row else if (i>= 33 && i <= 48) alpha = 0.25098; // second row else alpha = 1.f; } else if (mMeshType == 2) { osg::Vec4Array* origColors = static_cast(geom->getColorArray()); alpha = ((*origColors)[i].x() == 1.f) ? 1.f : 0.f; } (*colors)[i] = osg::Vec4f(0.f, 0.f, 0.f, alpha); } geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX); } private: int mMeshType; }; /// @brief Hides the node subgraph if the eye point is below water. /// @note Must be added as cull callback. /// @note Meant to be used on a node that is child of a CameraRelativeTransform. /// The current eye point must be retrieved by the CameraRelativeTransform since we can't get it anymore once we are in camera-relative space. class UnderwaterSwitchCallback : public osg::NodeCallback { public: UnderwaterSwitchCallback(CameraRelativeTransform* cameraRelativeTransform) : mCameraRelativeTransform(cameraRelativeTransform) , mEnabled(true) , mWaterLevel(0.f) { } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::Vec3f eyePoint = mCameraRelativeTransform->getLastEyePoint(); if (mEnabled && eyePoint.z() < mWaterLevel) return; traverse(node, nv); } void setEnabled(bool enabled) { mEnabled = enabled; } void setWaterLevel(float waterLevel) { mWaterLevel = waterLevel; } private: osg::ref_ptr mCameraRelativeTransform; bool mEnabled; float mWaterLevel; }; /// A base class for the sun and moons. class CelestialBody { public: CelestialBody(osg::Group* parentNode, float scaleFactor, int numUvSets) { mGeode = new osg::Geode; osg::ref_ptr geom = createTexturedQuad(numUvSets); mGeode->addDrawable(geom); mTransform = new osg::PositionAttitudeTransform; mTransform->setScale(osg::Vec3f(450,450,450) * scaleFactor); mTransform->addChild(mGeode); parentNode->addChild(mTransform); } virtual ~CelestialBody() {} virtual void adjustTransparency(const float ratio) = 0; void setVisible(bool visible) { mTransform->setNodeMask(visible ? ~0 : 0); } protected: static const float mDistance; osg::ref_ptr mTransform; osg::ref_ptr mGeode; }; const float CelestialBody::mDistance = 1000.0f; class Sun : public CelestialBody { public: Sun(osg::Group* parentNode, Resource::TextureManager& textureManager) : CelestialBody(parentNode, 1.0f, 1) , mUpdater(new Updater) { mTransform->addUpdateCallback(mUpdater); mTransform->setNodeMask(Mask_Sun); osg::ref_ptr sunTex = textureManager.getTexture2D("textures/tx_sun_05.dds", osg::Texture::CLAMP, osg::Texture::CLAMP); mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON); osg::ref_ptr queryNode (new osg::Group); // Need to render after the world geometry so we can correctly test for occlusions queryNode->getOrCreateStateSet()->setRenderBinDetails(RenderBin_OcclusionQuery, "RenderBin"); queryNode->getOrCreateStateSet()->setNestRenderBins(false); // Set up alpha testing on the occlusion testing subgraph, that way we can get the occlusion tested fragments to match the circular shape of the sun osg::ref_ptr alphaFunc (new osg::AlphaFunc); alphaFunc->setFunction(osg::AlphaFunc::GREATER, 0.8); queryNode->getOrCreateStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); queryNode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON); queryNode->getOrCreateStateSet()->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON); mTransform->addChild(queryNode); mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true); mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false); createSunFlash(textureManager); createSunGlare(); } ~Sun() { mTransform->removeUpdateCallback(mUpdater); destroySunFlash(); destroySunGlare(); } void setColor(const osg::Vec4f& color) { mUpdater->mColor.r() = color.r(); mUpdater->mColor.g() = color.g(); mUpdater->mColor.b() = color.b(); } virtual void adjustTransparency(const float ratio) { mUpdater->mColor.a() = ratio; if (mSunGlareCallback) mSunGlareCallback->setGlareView(ratio); if (mSunFlashCallback) mSunFlashCallback->setGlareView(ratio); } void setDirection(const osg::Vec3f& direction) { osg::Vec3f normalizedDirection = direction / direction.length(); mTransform->setPosition(normalizedDirection * mDistance); osg::Quat quat; quat.makeRotate(osg::Vec3f(0.0f, 0.0f, 1.0f), normalizedDirection); mTransform->setAttitude(quat); } void setGlareTimeOfDayFade(float val) { if (mSunGlareCallback) mSunGlareCallback->setTimeOfDayFade(val); } private: /// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels. osg::ref_ptr createOcclusionQueryNode(osg::Group* parent, bool queryVisible) { osg::ref_ptr oqn = new osg::OcclusionQueryNode; oqn->setQueriesEnabled(true); // Make it fast! A DYNAMIC query geometry means we can't break frame until the flare is rendered (which is rendered after all the other geometry, // so that would be pretty bad). STATIC should be safe, since our node's local bounds are static, thus computeBounds() which modifies the queryGeometry // is only called once. // Note the debug geometry setDebugDisplay(true) is always DYNAMIC and that can't be changed, not a big deal. oqn->getQueryGeometry()->setDataVariance(osg::Object::STATIC); osg::ref_ptr queryGeode = osg::clone(mGeode.get(), osg::CopyOp::DEEP_COPY_ALL); // Disable writing to the color buffer. We are using this geode for visibility tests only. osg::ref_ptr colormask (new osg::ColorMask(0, 0, 0, 0)); queryGeode->getOrCreateStateSet()->setAttributeAndModes(colormask, osg::StateAttribute::ON); oqn->addChild(queryGeode); // Remove the default OFF|PROTECTED setting for texturing. We *want* to enable texturing for alpha testing purposes oqn->getQueryStateSet()->removeTextureMode(0, GL_TEXTURE_2D); // Need to add texture coordinates so that texturing works. A bit ugly, relies on the vertex ordering // used within OcclusionQueryNode. osg::ref_ptr texCoordArray (new osg::Vec2Array); for (int i=0; i<8; ++i) { texCoordArray->push_back(osg::Vec2(0,0)); texCoordArray->push_back(osg::Vec2(1,0)); texCoordArray->push_back(osg::Vec2(0,0)); texCoordArray->push_back(osg::Vec2(1,0)); texCoordArray->push_back(osg::Vec2(1,1)); texCoordArray->push_back(osg::Vec2(0,1)); texCoordArray->push_back(osg::Vec2(0,1)); texCoordArray->push_back(osg::Vec2(1,1)); } oqn->getQueryGeometry()->setTexCoordArray(0, texCoordArray, osg::Array::BIND_PER_VERTEX); if (queryVisible) { osg::ref_ptr depth (new osg::Depth); depth->setFunction(osg::Depth::LESS); // This is a trick to make fragments written by the query always use the maximum depth value, // without having to retrieve the current far clipping distance. // We want the sun glare to be "infinitely" far away. depth->setZNear(1.0); depth->setZFar(1.0); oqn->getQueryStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); } else { oqn->getQueryStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); } parent->addChild(oqn); return oqn; } void createSunFlash(Resource::TextureManager& textureManager) { osg::ref_ptr tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds", osg::Texture::CLAMP, osg::Texture::CLAMP); osg::ref_ptr transform (new osg::PositionAttitudeTransform); const float scale = 2.6f; transform->setScale(osg::Vec3f(scale,scale,scale)); mTransform->addChild(transform); osg::ref_ptr geode (new osg::Geode); transform->addChild(geode); geode->addDrawable(createTexturedQuad()); osg::StateSet* stateset = geode->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON); stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin"); stateset->setNestRenderBins(false); mSunFlashNode = transform; mSunFlashCallback = new SunFlashCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels); mSunFlashNode->addCullCallback(mSunFlashCallback); } void destroySunFlash() { if (mSunFlashNode) { mSunFlashNode->removeCullCallback(mSunFlashCallback); mSunFlashCallback = NULL; } } void createSunGlare() { osg::ref_ptr camera (new osg::Camera); camera->setProjectionMatrix(osg::Matrix::identity()); camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); // add to skyRoot instead? camera->setViewMatrix(osg::Matrix::identity()); camera->setClearMask(0); camera->setRenderOrder(osg::Camera::NESTED_RENDER); camera->setAllowEventFocus(false); osg::ref_ptr geode (new osg::Geode); osg::ref_ptr geom = osg::createTexturedQuadGeometry(osg::Vec3f(-1,-1,0), osg::Vec3f(2,0,0), osg::Vec3f(0,2,0)); geode->addDrawable(geom); camera->addChild(geode); osg::StateSet* stateset = geom->getOrCreateStateSet(); stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin"); stateset->setNestRenderBins(false); stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); // set up additive blending osg::ref_ptr blendFunc (new osg::BlendFunc); blendFunc->setSource(osg::BlendFunc::SRC_ALPHA); blendFunc->setDestination(osg::BlendFunc::ONE); stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); mSunGlareCallback = new SunGlareCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels, mTransform); mSunGlareNode = camera; mSunGlareNode->addCullCallback(mSunGlareCallback); mTransform->addChild(camera); } void destroySunGlare() { if (mSunGlareNode) { mSunGlareNode->removeCullCallback(mSunGlareCallback); mSunGlareCallback = NULL; } } class Updater : public SceneUtil::StateSetUpdater { public: osg::Vec4f mColor; Updater() : mColor(1.f, 1.f, 1.f, 1.f) { } virtual void setDefaults(osg::StateSet* stateset) { stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON); } virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*) { osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,mColor.a())); mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(mColor.r(), mColor.g(), mColor.b(), 1)); } }; class OcclusionCallback : public osg::NodeCallback { public: OcclusionCallback(osg::ref_ptr oqnVisible, osg::ref_ptr oqnTotal) : mOcclusionQueryVisiblePixels(oqnVisible) , mOcclusionQueryTotalPixels(oqnTotal) { } protected: float getVisibleRatio (osg::Camera* camera) { int visible = mOcclusionQueryVisiblePixels->getQueryGeometry()->getNumPixels(camera); int total = mOcclusionQueryTotalPixels->getQueryGeometry()->getNumPixels(camera); float visibleRatio = 0.f; if (total > 0) visibleRatio = static_cast(visible) / static_cast(total); float dt = MWBase::Environment::get().getFrameDuration(); float lastRatio = mLastRatio[osg::observer_ptr(camera)]; float change = dt*10; if (visibleRatio > lastRatio) visibleRatio = std::min(visibleRatio, lastRatio + change); else visibleRatio = std::max(visibleRatio, lastRatio - change); mLastRatio[osg::observer_ptr(camera)] = visibleRatio; return visibleRatio; } private: osg::ref_ptr mOcclusionQueryVisiblePixels; osg::ref_ptr mOcclusionQueryTotalPixels; std::map, float> mLastRatio; }; /// SunFlashCallback handles fading/scaling of a node depending on occlusion query result. Must be attached as a cull callback. class SunFlashCallback : public OcclusionCallback { public: SunFlashCallback(osg::ref_ptr oqnVisible, osg::ref_ptr oqnTotal) : OcclusionCallback(oqnVisible, oqnTotal) , mGlareView(1.f) { } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = static_cast(nv); float visibleRatio = getVisibleRatio(cv->getCurrentCamera()); osg::ref_ptr stateset; if (visibleRatio > 0.f) { const float fadeThreshold = 0.1; if (visibleRatio < fadeThreshold) { float fade = 1.f - (fadeThreshold - visibleRatio) / fadeThreshold; osg::ref_ptr mat (createUnlitMaterial()); mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade*mGlareView)); stateset = new osg::StateSet; stateset->setAttributeAndModes(mat, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } const float threshold = 0.6; visibleRatio = visibleRatio * (1.f - threshold) + threshold; } float scale = visibleRatio; if (scale == 0.f) { // no traverse return; } else { osg::Matrix modelView = *cv->getModelViewMatrix(); modelView.preMultScale(osg::Vec3f(visibleRatio, visibleRatio, visibleRatio)); if (stateset) cv->pushStateSet(stateset); cv->pushModelViewMatrix(new osg::RefMatrix(modelView), osg::Transform::RELATIVE_RF); traverse(node, nv); cv->popModelViewMatrix(); if (stateset) cv->popStateSet(); } } void setGlareView(float value) { mGlareView = value; } private: float mGlareView; }; /// SunGlareCallback controls a full-screen glare effect depending on occlusion query result and the angle between sun and camera. /// Must be attached as a cull callback to the node above the glare node. class SunGlareCallback : public OcclusionCallback { public: SunGlareCallback(osg::ref_ptr oqnVisible, osg::ref_ptr oqnTotal, osg::ref_ptr sunTransform) : OcclusionCallback(oqnVisible, oqnTotal) , mSunTransform(sunTransform) , mTimeOfDayFade(1.f) , mGlareView(1.f) { const MWWorld::Fallback* fallback = MWBase::Environment::get().getWorld()->getFallback(); mColor = fallback->getFallbackColour("Weather_Sun_Glare_Fader_Color"); mSunGlareFaderMax = fallback->getFallbackFloat("Weather_Sun_Glare_Fader_Max"); mSunGlareFaderAngleMax = fallback->getFallbackFloat("Weather_Sun_Glare_Fader_Angle_Max"); // Replicating a design flaw in MW. The color was being set on both ambient and emissive properties, which multiplies the result by two, // then finally gets clamped by the fixed function pipeline. With the default INI settings, only the red component gets clamped, // so the resulting color looks more orange than red. mColor *= 2; for (int i=0; i<3; ++i) mColor[i] = std::min(1.f, mColor[i]); } virtual void operator ()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = static_cast(nv); float angleRadians = getAngleToSunInRadians(*cv->getCurrentRenderStage()->getInitialViewMatrix()); float visibleRatio = getVisibleRatio(cv->getCurrentCamera()); const float angleMaxRadians = osg::DegreesToRadians(mSunGlareFaderAngleMax); float value = 1.f - std::min(1.f, angleRadians / angleMaxRadians); float fade = value * mSunGlareFaderMax; fade *= mTimeOfDayFade * mGlareView * visibleRatio; if (fade == 0.f) { // no traverse return; } else { osg::ref_ptr stateset (new osg::StateSet); osg::ref_ptr mat (createUnlitMaterial()); mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade)); mat->setEmission(osg::Material::FRONT_AND_BACK, mColor); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); cv->pushStateSet(stateset); traverse(node, nv); cv->popStateSet(); } } void setTimeOfDayFade(float val) { mTimeOfDayFade = val; } void setGlareView(float glareView) { mGlareView = glareView; } private: float getAngleToSunInRadians(const osg::Matrix& viewMatrix) const { osg::Vec3d eye, center, up; viewMatrix.getLookAt(eye, center, up); osg::Vec3d forward = center - eye; osg::Vec3d sun = mSunTransform->getPosition(); forward.normalize(); sun.normalize(); float angleRadians = std::acos(forward * sun); return angleRadians; } osg::ref_ptr mSunTransform; float mTimeOfDayFade; float mGlareView; osg::Vec4f mColor; float mSunGlareFaderMax; float mSunGlareFaderAngleMax; }; osg::ref_ptr mUpdater; osg::ref_ptr mSunFlashCallback; osg::ref_ptr mSunFlashNode; osg::ref_ptr mSunGlareCallback; osg::ref_ptr mSunGlareNode; osg::ref_ptr mOcclusionQueryVisiblePixels; osg::ref_ptr mOcclusionQueryTotalPixels; }; class Moon : public CelestialBody { public: enum Type { Type_Masser = 0, Type_Secunda }; Moon(osg::Group* parentNode, Resource::TextureManager& textureManager, float scaleFactor, Type type) : CelestialBody(parentNode, scaleFactor, 2) , mType(type) , mPhase(MoonState::Phase_Unspecified) , mUpdater(new Updater(textureManager)) { setPhase(MoonState::Phase_Full); setVisible(true); mGeode->addUpdateCallback(mUpdater); } ~Moon() { mGeode->removeUpdateCallback(mUpdater); } virtual void adjustTransparency(const float ratio) { mUpdater->mTransparency *= ratio; } void setState(const MoonState& state) { float radsX = ((state.mRotationFromHorizon) * M_PI) / 180.0f; float radsZ = ((state.mRotationFromNorth) * M_PI) / 180.0f; osg::Quat rotX(radsX, osg::Vec3f(1.0f, 0.0f, 0.0f)); osg::Quat rotZ(radsZ, osg::Vec3f(0.0f, 0.0f, 1.0f)); osg::Vec3f direction = rotX * rotZ * osg::Vec3f(0.0f, 1.0f, 0.0f); mTransform->setPosition(direction * mDistance); // The moon quad is initially oriented facing down, so we need to offset its X-axis // rotation to rotate it to face the camera when sitting at the horizon. osg::Quat attX((-M_PI / 2.0f) + radsX, osg::Vec3f(1.0f, 0.0f, 0.0f)); mTransform->setAttitude(attX * rotZ); setPhase(state.mPhase); mUpdater->mTransparency = state.mMoonAlpha; mUpdater->mShadowBlend = state.mShadowBlend; } void setAtmosphereColor(const osg::Vec4f& color) { mUpdater->mAtmosphereColor = color; } void setColor(const osg::Vec4f& color) { mUpdater->mMoonColor = color; } unsigned int getPhaseInt() const { if (mPhase == MoonState::Phase_New) return 0; else if (mPhase == MoonState::Phase_WaxingCrescent) return 1; else if (mPhase == MoonState::Phase_WaningCrescent) return 1; else if (mPhase == MoonState::Phase_FirstQuarter) return 2; else if (mPhase == MoonState::Phase_ThirdQuarter) return 2; else if (mPhase == MoonState::Phase_WaxingGibbous) return 3; else if (mPhase == MoonState::Phase_WaningGibbous) return 3; else if (mPhase == MoonState::Phase_Full) return 4; return 0; } private: struct Updater : public SceneUtil::StateSetUpdater { Resource::TextureManager& mTextureManager; osg::ref_ptr mPhaseTex; osg::ref_ptr mCircleTex; float mTransparency; float mShadowBlend; osg::Vec4f mAtmosphereColor; osg::Vec4f mMoonColor; Updater(Resource::TextureManager& textureManager) : mTextureManager(textureManager) , mPhaseTex() , mCircleTex() , mTransparency(1.0f) , mShadowBlend(1.0f) , mAtmosphereColor(1.0f, 1.0f, 1.0f, 1.0f) , mMoonColor(1.0f, 1.0f, 1.0f, 1.0f) { } virtual void setDefaults(osg::StateSet* stateset) { stateset->setTextureAttributeAndModes(0, mPhaseTex, osg::StateAttribute::ON); osg::ref_ptr texEnv = new osg::TexEnvCombine; texEnv->setCombine_RGB(osg::TexEnvCombine::MODULATE); texEnv->setSource0_RGB(osg::TexEnvCombine::CONSTANT); texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); texEnv->setConstantColor(osg::Vec4f(1.f, 0.f, 0.f, 1.f)); // mShadowBlend * mMoonColor stateset->setTextureAttributeAndModes(0, texEnv, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(1, mCircleTex, osg::StateAttribute::ON); osg::ref_ptr texEnv2 = new osg::TexEnvCombine; texEnv2->setCombine_RGB(osg::TexEnvCombine::ADD); texEnv2->setCombine_Alpha(osg::TexEnvCombine::MODULATE); texEnv2->setSource0_Alpha(osg::TexEnvCombine::TEXTURE); texEnv2->setSource1_Alpha(osg::TexEnvCombine::CONSTANT); texEnv2->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); texEnv2->setSource1_RGB(osg::TexEnvCombine::CONSTANT); texEnv2->setConstantColor(osg::Vec4f(0.f, 0.f, 0.f, 1.f)); // mAtmosphereColor.rgb, mTransparency stateset->setTextureAttributeAndModes(1, texEnv2, osg::StateAttribute::ON); stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*) { osg::TexEnvCombine* texEnv = static_cast(stateset->getTextureAttribute(0, osg::StateAttribute::TEXENV)); texEnv->setConstantColor(mMoonColor * mShadowBlend); osg::TexEnvCombine* texEnv2 = static_cast(stateset->getTextureAttribute(1, osg::StateAttribute::TEXENV)); texEnv2->setConstantColor(osg::Vec4f(mAtmosphereColor.x(), mAtmosphereColor.y(), mAtmosphereColor.z(), mTransparency)); } void setTextures(const std::string& phaseTex, const std::string& circleTex) { mPhaseTex = mTextureManager.getTexture2D(phaseTex, osg::Texture::CLAMP, osg::Texture::CLAMP); mCircleTex = mTextureManager.getTexture2D(circleTex, osg::Texture::CLAMP, osg::Texture::CLAMP); reset(); } }; Type mType; MoonState::Phase mPhase; osg::ref_ptr mUpdater; void setPhase(const MoonState::Phase& phase) { if(mPhase == phase) return; mPhase = phase; std::string textureName = "textures/tx_"; if (mType == Moon::Type_Secunda) textureName += "secunda_"; else textureName += "masser_"; if (phase == MoonState::Phase_New) textureName += "new"; else if(phase == MoonState::Phase_WaxingCrescent) textureName += "one_wax"; else if(phase == MoonState::Phase_FirstQuarter) textureName += "half_wax"; else if(phase == MoonState::Phase_WaxingGibbous) textureName += "three_wax"; else if(phase == MoonState::Phase_WaningCrescent) textureName += "one_wan"; else if(phase == MoonState::Phase_ThirdQuarter) textureName += "half_wan"; else if(phase == MoonState::Phase_WaningGibbous) textureName += "three_wan"; else if(phase == MoonState::Phase_Full) textureName += "full"; textureName += ".dds"; if (mType == Moon::Type_Secunda) mUpdater->setTextures(textureName, "textures/tx_mooncircle_full_s.dds"); else mUpdater->setTextures(textureName, "textures/tx_mooncircle_full_m.dds"); } }; SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager) : mSceneManager(sceneManager) , mAtmosphereNightRoll(0.f) , mCreated(false) , mIsStorm(false) , mDay(0) , mMonth(0) , mCloudAnimationTimer(0.f) , mRainTimer(0.f) , mStormDirection(0,-1,0) , mClouds() , mNextClouds() , mCloudBlendFactor(0.0f) , mCloudSpeed(0.0f) , mStarsOpacity(0.0f) , mRemainingTransitionTime(0.0f) , mRainEnabled(false) , mRainSpeed(0) , mRainFrequency(1) , mWindSpeed(0.f) , mEnabled(true) , mSunEnabled(true) { osg::ref_ptr skyroot (new CameraRelativeTransform); skyroot->setNodeMask(Mask_Sky); parentNode->addChild(skyroot); mRootNode = skyroot; mEarlyRenderBinRoot = new osg::Group; // render before the world is rendered mEarlyRenderBinRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Sky, "RenderBin"); // Prevent unwanted clipping by water reflection camera's clipping plane mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_CLIP_PLANE0, osg::StateAttribute::OFF); mRootNode->addChild(mEarlyRenderBinRoot); mUnderwaterSwitch = new UnderwaterSwitchCallback(skyroot); } void SkyManager::create() { assert(!mCreated); mAtmosphereDay = mSceneManager->createInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot); ModVertexAlphaVisitor modAtmosphere(0); mAtmosphereDay->accept(modAtmosphere); mAtmosphereUpdater = new AtmosphereUpdater; mAtmosphereDay->addUpdateCallback(mAtmosphereUpdater); mAtmosphereNightNode = new osg::PositionAttitudeTransform; mAtmosphereNightNode->setNodeMask(0); mEarlyRenderBinRoot->addChild(mAtmosphereNightNode); osg::ref_ptr atmosphereNight; if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif")) atmosphereNight = mSceneManager->createInstance("meshes/sky_night_02.nif", mAtmosphereNightNode); else atmosphereNight = mSceneManager->createInstance("meshes/sky_night_01.nif", mAtmosphereNightNode); atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); ModVertexAlphaVisitor modStars(2); atmosphereNight->accept(modStars); mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getTextureManager()); atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater); mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getTextureManager())); const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Masser_Size")/125, Moon::Type_Masser)); mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda)); mCloudNode = new osg::PositionAttitudeTransform; mEarlyRenderBinRoot->addChild(mCloudNode); mCloudMesh = mSceneManager->createInstance("meshes/sky_clouds_01.nif", mCloudNode); ModVertexAlphaVisitor modClouds(1); mCloudMesh->accept(modClouds); mCloudUpdater = new CloudUpdater; mCloudUpdater->setOpacity(1.f); mCloudMesh->addUpdateCallback(mCloudUpdater); mCloudMesh2 = mSceneManager->createInstance("meshes/sky_clouds_01.nif", mCloudNode); mCloudMesh2->accept(modClouds); mCloudUpdater2 = new CloudUpdater; mCloudUpdater2->setOpacity(0.f); mCloudMesh2->addUpdateCallback(mCloudUpdater2); mCloudMesh2->setNodeMask(0); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(false); mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF); mMoonScriptColor = fallback->getFallbackColour("Moons_Script_Color"); mCreated = true; } class RainShooter : public osgParticle::Shooter { public: RainShooter() : mAngle(0.f) { } virtual void shoot(osgParticle::Particle* particle) const { particle->setVelocity(mVelocity); particle->setAngle(osg::Vec3f(-mAngle, 0, (Misc::Rng::rollProbability() * 2 - 1) * osg::PI)); } void setVelocity(const osg::Vec3f& velocity) { mVelocity = velocity; } void setAngle(float angle) { mAngle = angle; } virtual osg::Object* cloneType() const { return new RainShooter; } virtual osg::Object* clone(const osg::CopyOp &) const { return new RainShooter(*this); } private: osg::Vec3f mVelocity; float mAngle; }; // Updater for alpha value on a node's StateSet. Assumes the node has an existing Material StateAttribute. class AlphaFader : public SceneUtil::StateSetUpdater { public: AlphaFader() : mAlpha(1.f) { } void setAlpha(float alpha) { mAlpha = alpha; } virtual void setDefaults(osg::StateSet* stateset) { // need to create a deep copy of StateAttributes we will modify osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) { osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,mAlpha)); } // Helper for adding AlphaFaders to a subgraph class SetupVisitor : public osg::NodeVisitor { public: SetupVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { } virtual void apply(osg::Node &node) { if (osg::StateSet* stateset = node.getStateSet()) { if (stateset->getAttribute(osg::StateAttribute::MATERIAL)) { SceneUtil::CompositeStateSetUpdater* composite = NULL; #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) osg::Callback* callback = node.getUpdateCallback(); #else osg::NodeCallback* callback = node.getUpdateCallback(); #endif while (callback) { if ((composite = dynamic_cast(callback))) break; callback = callback->getNestedCallback(); } osg::ref_ptr alphaFader (new AlphaFader); if (composite) composite->addController(alphaFader); else node.addUpdateCallback(alphaFader); mAlphaFaders.push_back(alphaFader); } } traverse(node); } std::vector > getAlphaFaders() { return mAlphaFaders; } private: std::vector > mAlphaFaders; }; private: float mAlpha; }; class RainFader : public AlphaFader { public: virtual void setDefaults(osg::StateSet* stateset) { osg::ref_ptr mat (new osg::Material); mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1)); mat->setColorMode(osg::Material::OFF); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); } }; void SkyManager::createRain() { if (mRainNode) return; mRainNode = new osg::Group; mRainParticleSystem = new osgParticle::ParticleSystem; mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED); mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0)); mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,-1)); osg::ref_ptr stateset (mRainParticleSystem->getOrCreateStateSet()); stateset->setTextureAttributeAndModes(0, mSceneManager->getTextureManager()->getTexture2D("textures/tx_raindrop_01.dds", osg::Texture::CLAMP, osg::Texture::CLAMP), osg::StateAttribute::ON); stateset->setNestRenderBins(false); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); stateset->setMode(GL_BLEND, osg::StateAttribute::ON); osgParticle::Particle& particleTemplate = mRainParticleSystem->getDefaultParticleTemplate(); particleTemplate.setSizeRange(osgParticle::rangef(5.f, 15.f)); particleTemplate.setAlphaRange(osgParticle::rangef(1.f, 1.f)); particleTemplate.setLifeTime(1); osg::ref_ptr emitter (new osgParticle::ModularEmitter); emitter->setParticleSystem(mRainParticleSystem); osg::ref_ptr placer (new osgParticle::BoxPlacer); placer->setXRange(-300, 300); // Rain_Diameter placer->setYRange(-300, 300); placer->setZRange(300, 300); emitter->setPlacer(placer); osg::ref_ptr counter (new osgParticle::ConstantRateCounter); counter->setNumberOfParticlesPerSecondToCreate(600.0); emitter->setCounter(counter); osg::ref_ptr shooter (new RainShooter); mRainShooter = shooter; emitter->setShooter(shooter); osg::ref_ptr updater (new osgParticle::ParticleSystemUpdater); updater->addParticleSystem(mRainParticleSystem); osg::ref_ptr geode (new osg::Geode); geode->addDrawable(mRainParticleSystem); mRainNode->addChild(emitter); mRainNode->addChild(geode); mRainNode->addChild(updater); mRainFader = new RainFader; mRainNode->addUpdateCallback(mRainFader); mRainNode->addCullCallback(mUnderwaterSwitch); mRainNode->setNodeMask(Mask_WeatherParticles); mRootNode->addChild(mRainNode); } void SkyManager::destroyRain() { if (!mRainNode) return; mRootNode->removeChild(mRainNode); mRainNode = NULL; mRainParticleSystem = NULL; mRainShooter = NULL; mRainFader = NULL; } SkyManager::~SkyManager() { if (mRootNode) { mRootNode->getParent(0)->removeChild(mRootNode); mRootNode = NULL; } } int SkyManager::getMasserPhase() const { if (!mCreated) return 0; return mMasser->getPhaseInt(); } int SkyManager::getSecundaPhase() const { if (!mCreated) return 0; return mSecunda->getPhaseInt(); } void SkyManager::update(float duration) { if (!mEnabled) return; if (mIsStorm) { osg::Quat quat; quat.makeRotate(osg::Vec3f(0,1,0), mStormDirection); if (mParticleNode) mParticleNode->setAttitude(quat); mCloudNode->setAttitude(quat); } else mCloudNode->setAttitude(osg::Quat()); // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed * 0.003; mCloudUpdater->setAnimationTimer(mCloudAnimationTimer); mCloudUpdater2->setAnimationTimer(mCloudAnimationTimer); // rotate the stars by 360 degrees every 4 days mAtmosphereNightRoll += MWBase::Environment::get().getWorld()->getTimeScaleFactor()*duration*osg::DegreesToRadians(360.f) / (3600*96.f); if (mAtmosphereNightNode->getNodeMask() != 0) mAtmosphereNightNode->setAttitude(osg::Quat(mAtmosphereNightRoll, osg::Vec3f(0,0,1))); } void SkyManager::setEnabled(bool enabled) { if (enabled && !mCreated) create(); mRootNode->setNodeMask(enabled ? Mask_Sky : 0); mEnabled = enabled; } void SkyManager::setMoonColour (bool red) { if (!mCreated) return; mSecunda->setColor(red ? mMoonScriptColor : osg::Vec4f(1,1,1,1)); } void SkyManager::updateRainParameters() { if (mRainShooter) { float windFactor = mWindSpeed/3.f; float angle = windFactor * osg::PI/4; mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed * windFactor, -mRainSpeed)); mRainShooter->setAngle(angle); } } void SkyManager::setWeather(const WeatherResult& weather) { if (!mCreated) return; if (mRainEffect != weather.mRainEffect) { mRainEffect = weather.mRainEffect; if (!mRainEffect.empty()) { createRain(); } else { destroyRain(); } } mRainFrequency = weather.mRainFrequency; mRainSpeed = weather.mRainSpeed; mWindSpeed = weather.mWindSpeed; updateRainParameters(); mIsStorm = weather.mIsStorm; if (mCurrentParticleEffect != weather.mParticleEffect) { mCurrentParticleEffect = weather.mParticleEffect; // cleanup old particles if (mParticleEffect) { mParticleNode->removeChild(mParticleEffect); mParticleEffect = NULL; mParticleFaders.clear(); } if (mCurrentParticleEffect.empty()) { if (mParticleNode) { mRootNode->removeChild(mParticleNode); mParticleNode = NULL; } } else { if (!mParticleNode) { mParticleNode = new osg::PositionAttitudeTransform; mParticleNode->addCullCallback(mUnderwaterSwitch); mParticleNode->setNodeMask(Mask_WeatherParticles); mRootNode->addChild(mParticleNode); } mParticleEffect = mSceneManager->createInstance(mCurrentParticleEffect, mParticleNode); SceneUtil::AssignControllerSourcesVisitor assignVisitor(boost::shared_ptr(new SceneUtil::FrameTimeSource)); mParticleEffect->accept(assignVisitor); AlphaFader::SetupVisitor alphaFaderSetupVisitor; mParticleEffect->accept(alphaFaderSetupVisitor); mParticleFaders = alphaFaderSetupVisitor.getAlphaFaders(); SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; mParticleEffect->accept(disableFreezeOnCullVisitor); } } if (mClouds != weather.mCloudTexture) { mClouds = weather.mCloudTexture; std::string texture = Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS()); mCloudUpdater->setTexture(mSceneManager->getTextureManager()->getTexture2D(texture, osg::Texture::REPEAT, osg::Texture::REPEAT)); } if (mNextClouds != weather.mNextCloudTexture) { mNextClouds = weather.mNextCloudTexture; if (!mNextClouds.empty()) { std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS()); mCloudUpdater2->setTexture(mSceneManager->getTextureManager()->getTexture2D(texture, osg::Texture::REPEAT, osg::Texture::REPEAT)); } } if (mCloudBlendFactor != weather.mCloudBlendFactor) { mCloudBlendFactor = weather.mCloudBlendFactor; mCloudUpdater->setOpacity((1.f-mCloudBlendFactor)); mCloudUpdater2->setOpacity(mCloudBlendFactor); mCloudMesh2->setNodeMask(mCloudBlendFactor > 0.f ? ~0 : 0); } if (mCloudColour != weather.mFogColor) { osg::Vec4f clr (weather.mFogColor); clr += osg::Vec4f(0.13f, 0.13f, 0.13f, 0.f); mCloudUpdater->setEmissionColor(clr); mCloudUpdater2->setEmissionColor(clr); mCloudColour = weather.mFogColor; } if (mSkyColour != weather.mSkyColor) { mSkyColour = weather.mSkyColor; mAtmosphereUpdater->setEmissionColor(mSkyColour); mMasser->setAtmosphereColor(mSkyColour); mSecunda->setAtmosphereColor(mSkyColour); } if (mFogColour != weather.mFogColor) { mFogColour = weather.mFogColor; } mCloudSpeed = weather.mCloudSpeed; mMasser->adjustTransparency(weather.mGlareView); mSecunda->adjustTransparency(weather.mGlareView); mSun->setColor(weather.mSunDiscColor); mSun->adjustTransparency(weather.mGlareView * weather.mSunDiscColor.a()); float nextStarsOpacity = weather.mNightFade * weather.mGlareView; if(weather.mNight && mStarsOpacity != nextStarsOpacity) { mStarsOpacity = nextStarsOpacity; mAtmosphereNightUpdater->setFade(mStarsOpacity); } mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0 : 0); if (mRainFader) mRainFader->setAlpha(weather.mEffectFade * 0.6); // * Rain_Threshold? for (std::vector >::const_iterator it = mParticleFaders.begin(); it != mParticleFaders.end(); ++it) (*it)->setAlpha(weather.mEffectFade); } void SkyManager::sunEnable() { if (!mCreated) return; mSun->setVisible(true); } void SkyManager::sunDisable() { if (!mCreated) return; mSun->setVisible(false); } void SkyManager::setStormDirection(const osg::Vec3f &direction) { mStormDirection = direction; } void SkyManager::setSunDirection(const osg::Vec3f& direction) { if (!mCreated) return; mSun->setDirection(direction); } void SkyManager::setMasserState(const MoonState& state) { if(!mCreated) return; mMasser->setState(state); } void SkyManager::setSecundaState(const MoonState& state) { if(!mCreated) return; mSecunda->setState(state); } void SkyManager::setDate(int day, int month) { mDay = day; mMonth = month; } void SkyManager::setGlareTimeOfDayFade(float val) { mSun->setGlareTimeOfDayFade(val); } void SkyManager::setWaterHeight(float height) { mUnderwaterSwitch->setWaterLevel(height); } void SkyManager::setWaterEnabled(bool enabled) { mUnderwaterSwitch->setEnabled(enabled); } } openmw-openmw-0.38.0/apps/openmw/mwrender/sky.hpp000066400000000000000000000135741264522266000220430ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_SKY_H #define OPENMW_MWRENDER_SKY_H #include #include #include #include #include namespace osg { class Group; class Node; class Material; } namespace osgParticle { class ParticleSystem; } namespace Resource { class SceneManager; } namespace MWRender { class AtmosphereUpdater; class AtmosphereNightUpdater; class CloudUpdater; class Sun; class Moon; class RainShooter; class RainFader; class AlphaFader; class UnderwaterSwitchCallback; struct WeatherResult { std::string mCloudTexture; std::string mNextCloudTexture; float mCloudBlendFactor; osg::Vec4f mFogColor; osg::Vec4f mAmbientColor; osg::Vec4f mSkyColor; // sun light color osg::Vec4f mSunColor; // alpha is the sun transparency osg::Vec4f mSunDiscColor; float mFogDepth; float mWindSpeed; float mCloudSpeed; float mGlareView; bool mNight; // use night skybox float mNightFade; // fading factor for night skybox bool mIsStorm; std::string mAmbientLoopSoundID; float mAmbientSoundVolume; std::string mParticleEffect; std::string mRainEffect; float mEffectFade; float mRainSpeed; float mRainFrequency; }; struct MoonState { enum Phase { Phase_Full = 0, Phase_WaningGibbous, Phase_ThirdQuarter, Phase_WaningCrescent, Phase_New, Phase_WaxingCrescent, Phase_FirstQuarter, Phase_WaxingGibbous, Phase_Unspecified }; float mRotationFromHorizon; float mRotationFromNorth; Phase mPhase; float mShadowBlend; float mMoonAlpha; }; ///@brief The SkyManager handles rendering of the sky domes, celestial bodies as well as other objects that need to be rendered /// relative to the camera (e.g. weather particle effects) class SkyManager { public: SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager); ~SkyManager(); void update(float duration); void setEnabled(bool enabled); void setHour (double hour); ///< will be called even when sky is disabled. void setDate (int day, int month); ///< will be called even when sky is disabled. int getMasserPhase() const; ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, /// 3 waxing or waning gibbous, 4 full moon int getSecundaPhase() const; ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, /// 3 waxing or waning gibbous, 4 full moon void setMoonColour (bool red); ///< change Secunda colour to red void setWeather(const WeatherResult& weather); void sunEnable(); void sunDisable(); void setRainSpeed(float speed); void setStormDirection(const osg::Vec3f& direction); void setSunDirection(const osg::Vec3f& direction); void setMasserState(const MoonState& state); void setSecundaState(const MoonState& state); void setGlareTimeOfDayFade(float val); /// Enable or disable the water plane (used to remove underwater weather particles) void setWaterEnabled(bool enabled); /// Set height of water plane (used to remove underwater weather particles) void setWaterHeight(float height); private: void create(); ///< no need to call this, automatically done on first enable() void createRain(); void destroyRain(); void updateRainParameters(); Resource::SceneManager* mSceneManager; osg::ref_ptr mRootNode; osg::ref_ptr mEarlyRenderBinRoot; osg::ref_ptr mParticleNode; osg::ref_ptr mParticleEffect; std::vector > mParticleFaders; osg::ref_ptr mUnderwaterSwitch; osg::ref_ptr mCloudNode; osg::ref_ptr mCloudUpdater; osg::ref_ptr mCloudUpdater2; osg::ref_ptr mCloudMesh; osg::ref_ptr mCloudMesh2; osg::ref_ptr mAtmosphereDay; osg::ref_ptr mAtmosphereNightNode; float mAtmosphereNightRoll; osg::ref_ptr mAtmosphereNightUpdater; osg::ref_ptr mAtmosphereUpdater; std::auto_ptr mSun; std::auto_ptr mMasser; std::auto_ptr mSecunda; osg::ref_ptr mRainNode; osg::ref_ptr mRainParticleSystem; osg::ref_ptr mRainShooter; osg::ref_ptr mRainFader; bool mCreated; bool mIsStorm; int mDay; int mMonth; float mCloudAnimationTimer; float mRainTimer; osg::Vec3f mStormDirection; // remember some settings so we don't have to apply them again if they didnt change std::string mClouds; std::string mNextClouds; float mCloudBlendFactor; float mCloudSpeed; float mStarsOpacity; osg::Vec4f mCloudColour; osg::Vec4f mSkyColour; osg::Vec4f mFogColour; std::string mCurrentParticleEffect; float mRemainingTransitionTime; bool mRainEnabled; std::string mRainEffect; float mRainSpeed; float mRainFrequency; float mWindSpeed; bool mEnabled; bool mSunEnabled; osg::Vec4f mMoonScriptColor; }; } #endif // GAME_RENDER_SKY_H openmw-openmw-0.38.0/apps/openmw/mwrender/terrainstorage.cpp000066400000000000000000000047511264522266000242560ustar00rootroot00000000000000#include "terrainstorage.hpp" #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" namespace MWRender { TerrainStorage::TerrainStorage(const VFS::Manager* vfs, bool preload) : ESMTerrain::Storage(vfs) { if (preload) { const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); MWWorld::Store::iterator it = esmStore.get().begin(); for (; it != esmStore.get().end(); ++it) { const ESM::Land* land = &*it; land->loadData(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX); } } } void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY) { minX = 0, minY = 0, maxX = 0, maxY = 0; const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); MWWorld::Store::iterator it = esmStore.get().extBegin(); for (; it != esmStore.get().extEnd(); ++it) { if (it->getGridX() < minX) minX = static_cast(it->getGridX()); if (it->getGridX() > maxX) maxX = static_cast(it->getGridX()); if (it->getGridY() < minY) minY = static_cast(it->getGridY()); if (it->getGridY() > maxY) maxY = static_cast(it->getGridY()); } // since grid coords are at cell origin, we need to add 1 cell maxX += 1; maxY += 1; } const ESM::Land* TerrainStorage::getLand(int cellX, int cellY) { const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); ESM::Land* land = esmStore.get().search(cellX, cellY); if (!land) return NULL; const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; if (!land->isDataLoaded(flags)) land->loadData(flags); return land; } const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) { const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); return esmStore.get().search(index, plugin); } } openmw-openmw-0.38.0/apps/openmw/mwrender/terrainstorage.hpp000066400000000000000000000015351264522266000242600ustar00rootroot00000000000000#ifndef MWRENDER_TERRAINSTORAGE_H #define MWRENDER_TERRAINSTORAGE_H #include namespace MWRender { /// @brief Connects the ESM Store used in OpenMW with the ESMTerrain storage. class TerrainStorage : public ESMTerrain::Storage { private: virtual const ESM::Land* getLand (int cellX, int cellY); virtual const ESM::LandTexture* getLandTexture(int index, short plugin); public: ///@param preload Preload all Land records at startup? If using the multithreaded terrain component, this /// should be set to "true" in order to avoid race conditions. TerrainStorage(const VFS::Manager* vfs, bool preload); /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/util.cpp000066400000000000000000000021731264522266000221760ustar00rootroot00000000000000#include "util.hpp" #include #include #include #include namespace MWRender { void overrideTexture(const std::string &texture, Resource::ResourceSystem *resourceSystem, osg::ref_ptr node) { if (texture.empty()) return; std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture, resourceSystem->getVFS()); // Not sure if wrap settings should be pulled from the overridden texture? osg::ref_ptr tex = resourceSystem->getTextureManager()->getTexture2D(correctedTexture, osg::Texture2D::CLAMP, osg::Texture2D::CLAMP); osg::ref_ptr stateset; if (node->getStateSet()) stateset = static_cast(node->getStateSet()->clone(osg::CopyOp::SHALLOW_COPY)); else stateset = new osg::StateSet; stateset->setTextureAttribute(0, tex, osg::StateAttribute::OVERRIDE); node->setStateSet(stateset); } } openmw-openmw-0.38.0/apps/openmw/mwrender/util.hpp000066400000000000000000000005351264522266000222030ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_UTIL_H #define OPENMW_MWRENDER_UTIL_H #include #include namespace osg { class Node; } namespace Resource { class ResourceSystem; } namespace MWRender { void overrideTexture(const std::string& texture, Resource::ResourceSystem* resourceSystem, osg::ref_ptr node); } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/vismask.hpp000066400000000000000000000021431264522266000227000ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_VISMASK_H #define OPENMW_MWRENDER_VISMASK_H namespace MWRender { /// Node masks used for controlling visibility of game objects. enum VisMask { Mask_UpdateVisitor = 0x1, // reserved for separating UpdateVisitors from CullVisitors // child of Scene Mask_Effect = (1<<1), Mask_Debug = (1<<2), Mask_Actor = (1<<3), Mask_Player = (1<<4), Mask_Sky = (1<<5), Mask_Water = (1<<6), // choose Water or SimpleWater depending on detail required Mask_SimpleWater = (1<<7), Mask_Terrain = (1<<8), Mask_FirstPerson = (1<<9), // child of Sky Mask_Sun = (1<<10), Mask_WeatherParticles = (1<<11), // child of Water // top level masks Mask_Scene = (1<<12), Mask_GUI = (1<<13), // Set on a Geode Mask_ParticleSystem = (1<<14), // Set on cameras within the main scene graph Mask_RenderToTexture = (1<<15), // Set on a camera's cull mask to enable the LightManager Mask_Lighting = (1<<16) }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/water.cpp000066400000000000000000000631551264522266000223520ustar00rootroot00000000000000#include "water.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwworld/cellstore.hpp" #include "../mwworld/fallback.hpp" #include "vismask.hpp" #include "ripplesimulation.hpp" #include "renderbin.hpp" namespace { osg::ref_ptr createWaterGeometry(float size, int segments, float textureRepeats) { osg::ref_ptr verts (new osg::Vec3Array); osg::ref_ptr texcoords (new osg::Vec2Array); // some drivers don't like huge triangles, so we do some subdivisons // a paged solution would be even better const float step = size/segments; const float texCoordStep = textureRepeats / segments; for (int x=0; xpush_back(osg::Vec3f(x1, y2, 0.f)); verts->push_back(osg::Vec3f(x1, y1, 0.f)); verts->push_back(osg::Vec3f(x2, y1, 0.f)); verts->push_back(osg::Vec3f(x2, y2, 0.f)); float u1 = x*texCoordStep; float v1 = y*texCoordStep; float u2 = u1 + texCoordStep; float v2 = v1 + texCoordStep; texcoords->push_back(osg::Vec2f(u1, v2)); texcoords->push_back(osg::Vec2f(u1, v1)); texcoords->push_back(osg::Vec2f(u2, v1)); texcoords->push_back(osg::Vec2f(u2, v2)); } } osg::ref_ptr waterGeom (new osg::Geometry); waterGeom->setVertexArray(verts); waterGeom->setTexCoordArray(0, texcoords); osg::ref_ptr normal (new osg::Vec3Array); normal->push_back(osg::Vec3f(0,0,1)); waterGeom->setNormalArray(normal, osg::Array::BIND_OVERALL); waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size())); return waterGeom; } } namespace MWRender { // -------------------------------------------------------------------------------------------------------------------------------- /// @brief Allows to cull and clip meshes that are below a plane. Useful for reflection & refraction camera effects. /// Also handles flipping of the plane when the eye point goes below it. /// To use, simply create the scene as subgraph of this node, then do setPlane(const osg::Plane& plane); class ClipCullNode : public osg::Group { class PlaneCullCallback : public osg::NodeCallback { public: /// @param cullPlane The culling plane (in world space). PlaneCullCallback(const osg::Plane* cullPlane) : osg::NodeCallback() , mCullPlane(cullPlane) { } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = static_cast(nv); osg::Polytope::PlaneList origPlaneList = cv->getProjectionCullingStack().back().getFrustum().getPlaneList(); osg::Plane plane = *mCullPlane; plane.transform(*cv->getCurrentRenderStage()->getInitialViewMatrix()); osg::Vec3d eyePoint = cv->getEyePoint(); if (mCullPlane->intersect(osg::BoundingSphere(osg::Vec3d(0,0,eyePoint.z()), 0)) > 0) plane.flip(); cv->getProjectionCullingStack().back().getFrustum().add(plane); traverse(node, nv); // undo cv->getProjectionCullingStack().back().getFrustum().set(origPlaneList); } private: const osg::Plane* mCullPlane; }; class FlipCallback : public osg::NodeCallback { public: FlipCallback(const osg::Plane* cullPlane) : mCullPlane(cullPlane) { } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = static_cast(nv); osg::Vec3d eyePoint = cv->getEyePoint(); osg::RefMatrix* modelViewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix()); // apply the height of the plane // we can't apply this height in the addClipPlane() since the "flip the below graph" function would otherwise flip the height as well modelViewMatrix->preMultTranslate(mCullPlane->getNormal() * ((*mCullPlane)[3] * -1)); // flip the below graph if the eye point is above the plane if (mCullPlane->intersect(osg::BoundingSphere(osg::Vec3d(0,0,eyePoint.z()), 0)) > 0) { modelViewMatrix->preMultScale(osg::Vec3(1,1,-1)); } // move the plane back along its normal a little bit to prevent bleeding at the water shore const float clipFudge = -5; modelViewMatrix->preMultTranslate(mCullPlane->getNormal() * clipFudge); cv->pushModelViewMatrix(modelViewMatrix, osg::Transform::RELATIVE_RF); traverse(node, nv); cv->popModelViewMatrix(); } private: const osg::Plane* mCullPlane; }; public: ClipCullNode() { addCullCallback (new PlaneCullCallback(&mPlane)); mClipNodeTransform = new osg::Group; mClipNodeTransform->addCullCallback(new FlipCallback(&mPlane)); addChild(mClipNodeTransform); mClipNode = new osg::ClipNode; mClipNodeTransform->addChild(mClipNode); } void setPlane (const osg::Plane& plane) { if (plane == mPlane) return; mPlane = plane; mClipNode->getClipPlaneList().clear(); mClipNode->addClipPlane(new osg::ClipPlane(0, osg::Plane(mPlane.getNormal(), 0))); // mPlane.d() applied in FlipCallback mClipNode->setStateSetModes(*getOrCreateStateSet(), osg::StateAttribute::ON); mClipNode->setCullingActive(false); } private: osg::ref_ptr mClipNodeTransform; osg::ref_ptr mClipNode; osg::Plane mPlane; }; // Node callback to entirely skip the traversal. class NoTraverseCallback : public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { // no traverse() } }; /// Moves water mesh away from the camera slightly if the camera gets too close on the Z axis. /// The offset works around graphics artifacts that occured with the GL_DEPTH_CLAMP when the camera gets extremely close to the mesh (seen on NVIDIA at least). /// Must be added as a Cull callback. class FudgeCallback : public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = static_cast(nv); const float fudge = 0.2; if (std::abs(cv->getEyeLocal().z()) < fudge) { float diff = fudge - cv->getEyeLocal().z(); osg::RefMatrix* modelViewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix()); if (cv->getEyeLocal().z() > 0) modelViewMatrix->preMultTranslate(osg::Vec3f(0,0,-diff)); else modelViewMatrix->preMultTranslate(osg::Vec3f(0,0,diff)); cv->pushModelViewMatrix(modelViewMatrix, osg::Transform::RELATIVE_RF); traverse(node, nv); cv->popModelViewMatrix(); } else traverse(node, nv); } }; osg::ref_ptr readShader (osg::Shader::Type type, const std::string& file, const std::map& defineMap = std::map()) { osg::ref_ptr shader (new osg::Shader(type)); // use boost in favor of osg::Shader::readShaderFile, to handle utf-8 path issues on Windows boost::filesystem::ifstream inStream; inStream.open(boost::filesystem::path(file)); std::stringstream strstream; strstream << inStream.rdbuf(); std::string shaderSource = strstream.str(); for (std::map::const_iterator it = defineMap.begin(); it != defineMap.end(); ++it) { size_t pos = shaderSource.find(it->first); if (pos != std::string::npos) shaderSource.replace(pos, it->first.length(), it->second); } shader->setShaderSource(shaderSource); return shader; } osg::ref_ptr readPngImage (const std::string& file) { // use boost in favor of osgDB::readImage, to handle utf-8 path issues on Windows boost::filesystem::ifstream inStream; inStream.open(file, std::ios_base::in | std::ios_base::binary); if (inStream.fail()) std::cerr << "Failed to open " << file << std::endl; osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!reader) { std::cerr << "Failed to read " << file << ", no png readerwriter found" << std::endl; return osg::ref_ptr(); } osgDB::ReaderWriter::ReadResult result = reader->readImage(inStream); if (!result.success()) std::cerr << "Failed to read " << file << ": " << result.message() << " code " << result.status() << std::endl; return result.getImage(); } class Refraction : public osg::Camera { public: Refraction() { unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water"); setRenderOrder(osg::Camera::PRE_RENDER); setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); setReferenceFrame(osg::Camera::RELATIVE_RF); setCullMask(Mask_Effect|Mask_Scene|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting); setNodeMask(Mask_RenderToTexture); setViewport(0, 0, rttSize, rttSize); // No need for Update traversal since the scene is already updated as part of the main scene graph // A double update would mess with the light collection (in addition to being plain redundant) setUpdateCallback(new NoTraverseCallback); // No need for fog here, we are already applying fog on the water surface itself as well as underwater fog getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE); mClipCullNode = new ClipCullNode; addChild(mClipCullNode); mRefractionTexture = new osg::Texture2D; mRefractionTexture->setTextureSize(rttSize, rttSize); mRefractionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mRefractionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); mRefractionTexture->setInternalFormat(GL_RGB); mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); attach(osg::Camera::COLOR_BUFFER, mRefractionTexture); mRefractionDepthTexture = new osg::Texture2D; mRefractionDepthTexture->setSourceFormat(GL_DEPTH_COMPONENT); mRefractionDepthTexture->setInternalFormat(GL_DEPTH_COMPONENT24); mRefractionDepthTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mRefractionDepthTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT); mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mRefractionDepthTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture); } void setScene(osg::Node* scene) { if (mScene) mClipCullNode->removeChild(mScene); mScene = scene; mClipCullNode->addChild(scene); } void setWaterLevel(float waterLevel) { mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,-1), osg::Vec3d(0,0, waterLevel))); } osg::Texture2D* getRefractionTexture() const { return mRefractionTexture.get(); } osg::Texture2D* getRefractionDepthTexture() const { return mRefractionDepthTexture.get(); } private: osg::ref_ptr mClipCullNode; osg::ref_ptr mRefractionTexture; osg::ref_ptr mRefractionDepthTexture; osg::ref_ptr mScene; }; class Reflection : public osg::Camera { public: Reflection() { setRenderOrder(osg::Camera::PRE_RENDER); setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); setReferenceFrame(osg::Camera::RELATIVE_RF); setCullMask(Mask_Effect|Mask_Scene|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Player|Mask_Lighting); setNodeMask(Mask_RenderToTexture); unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water"); setViewport(0, 0, rttSize, rttSize); // No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph // A double update would mess with the light collection (in addition to being plain redundant) setUpdateCallback(new NoTraverseCallback); mReflectionTexture = new osg::Texture2D; mReflectionTexture->setInternalFormat(GL_RGB); mReflectionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mReflectionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); attach(osg::Camera::COLOR_BUFFER, mReflectionTexture); // XXX: should really flip the FrontFace on each renderable instead of forcing clockwise. osg::ref_ptr frontFace (new osg::FrontFace); frontFace->setMode(osg::FrontFace::CLOCKWISE); getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON); mClipCullNode = new ClipCullNode; addChild(mClipCullNode); } void setWaterLevel(float waterLevel) { setViewMatrix(osg::Matrix::translate(0,0,-waterLevel) * osg::Matrix::scale(1,1,-1) * osg::Matrix::translate(0,0,waterLevel)); mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,1), osg::Vec3d(0,0,waterLevel))); } void setScene(osg::Node* scene) { if (mScene) mClipCullNode->removeChild(mScene); mScene = scene; mClipCullNode->addChild(scene); } osg::Texture2D* getReflectionTexture() const { return mReflectionTexture.get(); } private: osg::ref_ptr mReflectionTexture; osg::ref_ptr mClipCullNode; osg::ref_ptr mScene; }; /// DepthClampCallback enables GL_DEPTH_CLAMP for the current draw, if supported. class DepthClampCallback : public osg::Drawable::DrawCallback { public: virtual void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* drawable) const { static bool supported = osg::isGLExtensionOrVersionSupported(renderInfo.getState()->getContextID(), "GL_ARB_depth_clamp", 3.3); if (!supported) { drawable->drawImplementation(renderInfo); return; } glEnable(GL_DEPTH_CLAMP); drawable->drawImplementation(renderInfo); // restore default glDisable(GL_DEPTH_CLAMP); } }; Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem *resourceSystem, osgUtil::IncrementalCompileOperation *ico, const MWWorld::Fallback* fallback, const std::string& resourcePath) : mParent(parent) , mSceneRoot(sceneRoot) , mResourceSystem(resourceSystem) , mFallback(fallback) , mResourcePath(resourcePath) , mEnabled(true) , mToggled(true) , mTop(0) { mSimulation.reset(new RippleSimulation(parent, resourceSystem, fallback)); osg::ref_ptr waterGeom = createWaterGeometry(CELL_SIZE*150, 40, 900); waterGeom->setDrawCallback(new DepthClampCallback); mWaterGeode = new osg::Geode; mWaterGeode->addDrawable(waterGeom); mWaterGeode->setNodeMask(Mask_Water); if (ico) ico->add(mWaterGeode); mWaterNode = new osg::PositionAttitudeTransform; mWaterNode->addChild(mWaterGeode); mWaterNode->addCullCallback(new FudgeCallback); // simple water fallback for the local map osg::ref_ptr geode2 (osg::clone(mWaterGeode.get(), osg::CopyOp::DEEP_COPY_NODES)); createSimpleWaterStateSet(geode2, mFallback->getFallbackFloat("Water_Map_Alpha")); geode2->setNodeMask(Mask_SimpleWater); mWaterNode->addChild(geode2); mSceneRoot->addChild(mWaterNode); setHeight(mTop); updateWaterMaterial(); } void Water::updateWaterMaterial() { if (mReflection) { mParent->removeChild(mReflection); mReflection = NULL; } if (mRefraction) { mParent->removeChild(mRefraction); mRefraction = NULL; } if (Settings::Manager::getBool("shader", "Water")) { mReflection = new Reflection; mReflection->setWaterLevel(mTop); mReflection->setScene(mSceneRoot); mParent->addChild(mReflection); if (Settings::Manager::getBool("refraction", "Water")) { mRefraction = new Refraction; mRefraction->setWaterLevel(mTop); mRefraction->setScene(mSceneRoot); mParent->addChild(mRefraction); } createShaderWaterStateSet(mWaterGeode, mReflection, mRefraction); } else createSimpleWaterStateSet(mWaterGeode, mFallback->getFallbackFloat("Water_World_Alpha")); updateVisible(); } void Water::createSimpleWaterStateSet(osg::Node* node, float alpha) { osg::ref_ptr stateset (new osg::StateSet); osg::ref_ptr material (new osg::Material); material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, alpha)); material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); material->setColorMode(osg::Material::OFF); stateset->setAttributeAndModes(material, osg::StateAttribute::ON); stateset->setMode(GL_BLEND, osg::StateAttribute::ON); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); osg::ref_ptr depth (new osg::Depth); depth->setWriteMask(false); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin"); node->setStateSet(stateset); std::vector > textures; int frameCount = mFallback->getFallbackInt("Water_SurfaceFrameCount"); std::string texture = mFallback->getFallbackString("Water_SurfaceTexture"); for (int i=0; igetTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT)); } if (!textures.size()) return; float fps = mFallback->getFallbackFloat("Water_SurfaceFPS"); osg::ref_ptr controller (new NifOsg::FlipController(0, 1.f/fps, textures)); controller->setSource(boost::shared_ptr(new SceneUtil::FrameTimeSource)); node->setUpdateCallback(controller); stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON); } void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction) { // use a define map to conditionally compile the shader std::map defineMap; defineMap.insert(std::make_pair(std::string("@refraction_enabled"), std::string(refraction ? "1" : "0"))); osg::ref_ptr vertexShader (readShader(osg::Shader::VERTEX, mResourcePath + "/shaders/water_vertex.glsl", defineMap)); osg::ref_ptr fragmentShader (readShader(osg::Shader::FRAGMENT, mResourcePath + "/shaders/water_fragment.glsl", defineMap)); osg::ref_ptr normalMap (new osg::Texture2D(readPngImage(mResourcePath + "/shaders/water_nm.png"))); normalMap->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); normalMap->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); normalMap->setMaxAnisotropy(16); normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); normalMap->getImage()->flipVertical(); osg::ref_ptr shaderStateset = new osg::StateSet; shaderStateset->addUniform(new osg::Uniform("normalMap", 0)); shaderStateset->addUniform(new osg::Uniform("reflectionMap", 1)); shaderStateset->setTextureAttributeAndModes(0, normalMap, osg::StateAttribute::ON); shaderStateset->setTextureAttributeAndModes(1, reflection->getReflectionTexture(), osg::StateAttribute::ON); if (refraction) { shaderStateset->setTextureAttributeAndModes(2, refraction->getRefractionTexture(), osg::StateAttribute::ON); shaderStateset->setTextureAttributeAndModes(3, refraction->getRefractionDepthTexture(), osg::StateAttribute::ON); shaderStateset->addUniform(new osg::Uniform("refractionMap", 2)); shaderStateset->addUniform(new osg::Uniform("refractionDepthMap", 3)); shaderStateset->setRenderBinDetails(MWRender::RenderBin_Default, "RenderBin"); } else { shaderStateset->setMode(GL_BLEND, osg::StateAttribute::ON); shaderStateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin"); osg::ref_ptr depth (new osg::Depth); depth->setWriteMask(false); shaderStateset->setAttributeAndModes(depth, osg::StateAttribute::ON); } shaderStateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); osg::ref_ptr program (new osg::Program); program->addShader(vertexShader); program->addShader(fragmentShader); shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON); node->setStateSet(shaderStateset); node->setUpdateCallback(NULL); } void Water::processChangedSettings(const Settings::CategorySettingVector& settings) { updateWaterMaterial(); } Water::~Water() { mParent->removeChild(mWaterNode); if (mReflection) { mParent->removeChild(mReflection); mReflection = NULL; } if (mRefraction) { mParent->removeChild(mRefraction); mRefraction = NULL; } } void Water::setEnabled(bool enabled) { mEnabled = enabled; updateVisible(); } void Water::changeCell(const MWWorld::CellStore* store) { if (store->getCell()->isExterior()) mWaterNode->setPosition(getSceneNodeCoordinates(store->getCell()->mData.mX, store->getCell()->mData.mY)); else mWaterNode->setPosition(osg::Vec3f(0,0,mTop)); // create a new StateSet to prevent threading issues osg::ref_ptr nodeStateSet (new osg::StateSet); nodeStateSet->addUniform(new osg::Uniform("nodePosition", osg::Vec3f(mWaterNode->getPosition()))); mWaterNode->setStateSet(nodeStateSet); } void Water::setHeight(const float height) { mTop = height; mSimulation->setWaterHeight(height); osg::Vec3f pos = mWaterNode->getPosition(); pos.z() = height; mWaterNode->setPosition(pos); if (mReflection) mReflection->setWaterLevel(mTop); if (mRefraction) mRefraction->setWaterLevel(mTop); } void Water::update(float dt) { mSimulation->update(dt); } void Water::updateVisible() { bool visible = mEnabled && mToggled; mWaterNode->setNodeMask(visible ? ~0 : 0); if (mRefraction) mRefraction->setNodeMask(visible ? Mask_RenderToTexture : 0); if (mReflection) mReflection->setNodeMask(visible ? Mask_RenderToTexture : 0); } bool Water::toggle() { mToggled = !mToggled; updateVisible(); return mToggled; } bool Water::isUnderwater(const osg::Vec3f &pos) const { return pos.z() < mTop && mToggled && mEnabled; } osg::Vec3f Water::getSceneNodeCoordinates(int gridX, int gridY) { return osg::Vec3f(static_cast(gridX * CELL_SIZE + (CELL_SIZE / 2)), static_cast(gridY * CELL_SIZE + (CELL_SIZE / 2)), mTop); } void Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force) { mSimulation->addEmitter (ptr, scale, force); } void Water::removeEmitter (const MWWorld::Ptr& ptr) { mSimulation->removeEmitter (ptr); } void Water::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr) { mSimulation->updateEmitterPtr(old, ptr); } void Water::emitRipple(const osg::Vec3f &pos) { mSimulation->emitRipple(pos); } void Water::removeCell(const MWWorld::CellStore *store) { mSimulation->removeCell(store); } void Water::clearRipples() { mSimulation->clear(); } } openmw-openmw-0.38.0/apps/openmw/mwrender/water.hpp000066400000000000000000000054441264522266000223540ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_WATER_H #define OPENMW_MWRENDER_WATER_H #include #include #include #include namespace osg { class Group; class PositionAttitudeTransform; class Geode; class Node; } namespace osgUtil { class IncrementalCompileOperation; } namespace Resource { class ResourceSystem; } namespace MWWorld { class Fallback; class CellStore; class Ptr; } namespace MWRender { class Refraction; class Reflection; class RippleSimulation; /// Water rendering class Water { static const int CELL_SIZE = 8192; osg::ref_ptr mParent; osg::ref_ptr mSceneRoot; osg::ref_ptr mWaterNode; osg::ref_ptr mWaterGeode; Resource::ResourceSystem* mResourceSystem; const MWWorld::Fallback* mFallback; osg::ref_ptr mIncrementalCompileOperation; std::auto_ptr mSimulation; osg::ref_ptr mRefraction; osg::ref_ptr mReflection; const std::string mResourcePath; bool mEnabled; bool mToggled; float mTop; osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY); void updateVisible(); void createSimpleWaterStateSet(osg::Node* node, float alpha); /// @param reflection the reflection camera (required) /// @param refraction the refraction camera (optional) void createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction); void updateWaterMaterial(); public: Water(osg::Group* parent, osg::Group* sceneRoot, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, const MWWorld::Fallback* fallback, const std::string& resourcePath); ~Water(); void setEnabled(bool enabled); bool toggle(); bool isUnderwater(const osg::Vec3f& pos) const; /// adds an emitter, position will be tracked automatically using its scene node void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f); void removeEmitter (const MWWorld::Ptr& ptr); void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); void emitRipple(const osg::Vec3f& pos); void removeCell(const MWWorld::CellStore* store); ///< remove all emitters in this cell void clearRipples(); void changeCell(const MWWorld::CellStore* store); void setHeight(const float height); void update(float dt); void processChangedSettings(const Settings::CategorySettingVector& settings); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwrender/weaponanimation.cpp000066400000000000000000000151221264522266000244100ustar00rootroot00000000000000#include "weaponanimation.hpp" #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/combat.hpp" #include "animation.hpp" #include "rotatecontroller.hpp" namespace MWRender { float WeaponAnimationTime::getValue(osg::NodeVisitor*) { if (mWeaponGroup.empty()) return 0; float current = mAnimation->getCurrentTime(mWeaponGroup); if (current == -1) return 0; return current - mStartTime; } void WeaponAnimationTime::setGroup(const std::string &group) { mWeaponGroup = group; mStartTime = mAnimation->getStartTime(mWeaponGroup); } void WeaponAnimationTime::updateStartTime() { setGroup(mWeaponGroup); } WeaponAnimation::WeaponAnimation() : mPitchFactor(0) { } WeaponAnimation::~WeaponAnimation() { } void WeaponAnimation::attachArrow(MWWorld::Ptr actor) { MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weaponSlot == inv.end()) return; if (weaponSlot->getTypeName() != typeid(ESM::Weapon).name()) return; int weaponType = weaponSlot->get()->mBase->mData.mType; if (weaponType == ESM::Weapon::MarksmanThrown) { std::string soundid = weaponSlot->getClass().getUpSoundId(*weaponSlot); if(!soundid.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(actor, soundid, 1.0f, 1.0f); } showWeapon(true); } else if (weaponType == ESM::Weapon::MarksmanBow || weaponType == ESM::Weapon::MarksmanCrossbow) { osg::Group* parent = getArrowBone(); if (!parent) return; MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo == inv.end()) return; std::string model = ammo->getClass().getModel(*ammo); osg::ref_ptr arrow = getResourceSystem()->getSceneManager()->createInstance(model, parent); mAmmunition = PartHolderPtr(new PartHolder(arrow)); } } void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) { MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weapon == inv.end()) return; if (weapon->getTypeName() != typeid(ESM::Weapon).name()) return; // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise. osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1)); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); MWMechanics::applyFatigueLoss(actor, *weapon, attackStrength); if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) { // Thrown weapons get detached now osg::Node* weaponNode = getWeaponNode(); if (!weaponNode) return; osg::MatrixList mats = weaponNode->getWorldMatrices(); if (mats.empty()) return; osg::Vec3f launchPos = mats[0].getTrans(); float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength; MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed, attackStrength); showWeapon(false); inv.remove(*weapon, 1, actor); } else { // With bows and crossbows only the used arrow/bolt gets detached MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo == inv.end()) return; if (!mAmmunition) return; osg::ref_ptr ammoNode = mAmmunition->getNode(); osg::MatrixList mats = ammoNode->getWorldMatrices(); if (mats.empty()) return; osg::Vec3f launchPos = mats[0].getTrans(); float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength; MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed, attackStrength); inv.remove(*ammo, 1, actor); mAmmunition.reset(); } } void WeaponAnimation::addControllers(const std::map >& nodes, std::multimap, osg::ref_ptr > &map, osg::Node* objectRoot) { for (int i=0; i<2; ++i) { mSpineControllers[i] = NULL; std::map >::const_iterator found = nodes.find(i == 0 ? "bip01 spine1" : "bip01 spine2"); if (found != nodes.end()) { osg::Node* node = found->second; mSpineControllers[i] = new RotateController(objectRoot); node->addUpdateCallback(mSpineControllers[i]); map.insert(std::make_pair(node, mSpineControllers[i])); } } } void WeaponAnimation::deleteControllers() { for (int i=0; i<2; ++i) mSpineControllers[i] = NULL; } void WeaponAnimation::configureControllers(float characterPitchRadians) { if (!mSpineControllers[0]) return; if (mPitchFactor == 0.f || characterPitchRadians == 0.f) { for (int i=0; i<2; ++i) mSpineControllers[i]->setEnabled(false); return; } float pitch = characterPitchRadians * mPitchFactor; osg::Quat rotate (pitch/2, osg::Vec3f(-1,0,0)); for (int i=0; i<2; ++i) { mSpineControllers[i]->setRotate(rotate); mSpineControllers[i]->setEnabled(true); } } } openmw-openmw-0.38.0/apps/openmw/mwrender/weaponanimation.hpp000066400000000000000000000042351264522266000244200ustar00rootroot00000000000000#ifndef OPENMW_MWRENDER_WEAPONANIMATION_H #define OPENMW_MWRENDER_WEAPONANIMATION_H #include #include "../mwworld/ptr.hpp" #include "animation.hpp" namespace MWRender { class RotateController; class WeaponAnimationTime : public SceneUtil::ControllerSource { private: Animation* mAnimation; std::string mWeaponGroup; float mStartTime; public: WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {} void setGroup(const std::string& group); void updateStartTime(); virtual float getValue(osg::NodeVisitor* nv); }; /// Handles attach & release of projectiles for ranged weapons class WeaponAnimation { public: WeaponAnimation(); virtual ~WeaponAnimation(); /// @note If no weapon (or an invalid weapon) is equipped, this function is a no-op. void attachArrow(MWWorld::Ptr actor); /// @note If no weapon (or an invalid weapon) is equipped, this function is a no-op. void releaseArrow(MWWorld::Ptr actor, float attackStrength); /// Add WeaponAnimation-related controllers to \a nodes and store the added controllers in \a map. void addControllers(const std::map >& nodes, std::multimap, osg::ref_ptr >& map, osg::Node* objectRoot); void deleteControllers(); /// Configure controllers, should be called every animation frame. void configureControllers(float characterPitchRadians); protected: PartHolderPtr mAmmunition; osg::ref_ptr mSpineControllers[2]; virtual osg::Group* getArrowBone() = 0; virtual osg::Node* getWeaponNode() = 0; virtual Resource::ResourceSystem* getResourceSystem() = 0; virtual void showWeapon(bool show) = 0; /// A relative factor (0-1) that decides if and how much the skeleton should be pitched /// to indicate the facing orientation of the character, for ranged weapon aiming. float mPitchFactor; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/000077500000000000000000000000001264522266000205375ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwscript/aiextensions.cpp000066400000000000000000000577501264522266000237720ustar00rootroot00000000000000#include "aiextensions.hpp" #include #include #include #include #include #include #include #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/aiactivate.hpp" #include "../mwmechanics/aiescort.hpp" #include "../mwmechanics/aifollow.hpp" #include "../mwmechanics/aitravel.hpp" #include "../mwmechanics/aiwander.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace MWScript { namespace Ai { template class OpAiActivate : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string objectID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i class OpAiTravel : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i class OpAiEscort : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i(duration), x, y, z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr); std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration << std::endl; } }; template class OpAiEscortCell : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetStore().get().find(cellID); MWMechanics::AiEscort escortPackage(actorID, cellID, static_cast(duration), x, y, z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr); std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration << std::endl; } }; template class OpGetAiPackageDone : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).getAiSequence().isPackageDone(); runtime.push (value); } }; template class OpAiWander : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer range = static_cast(runtime[0].mFloat); runtime.pop(); Interpreter::Type_Integer duration = static_cast(runtime[0].mFloat); runtime.pop(); Interpreter::Type_Integer time = static_cast(runtime[0].mFloat); runtime.pop(); std::vector idleList; bool repeat = false; for(int i=1; i < 10 && arg0; ++i) { if(!repeat) repeat = true; Interpreter::Type_Integer idleValue = runtime[0].mInteger; idleValue = std::min(255, std::max(0, idleValue)); idleList.push_back(idleValue); runtime.pop(); --arg0; } if(arg0) { repeat = runtime[0].mInteger != 0; runtime.pop(); --arg0; } // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i class OpGetAiSetting : public Interpreter::Opcode0 { int mIndex; public: OpGetAiSetting(int index) : mIndex(index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push(ptr.getClass().getCreatureStats (ptr).getAiSetting ( (MWMechanics::CreatureStats::AiSetting)mIndex).getModified()); } }; template class OpModAiSetting : public Interpreter::Opcode0 { int mIndex; public: OpModAiSetting(int index) : mIndex(index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); MWMechanics::CreatureStats::AiSetting setting = MWMechanics::CreatureStats::AiSetting(mIndex); ptr.getClass().getCreatureStats (ptr).setAiSetting (setting, ptr.getClass().getCreatureStats (ptr).getAiSetting (setting).getBase() + value); } }; template class OpSetAiSetting : public Interpreter::Opcode0 { int mIndex; public: OpSetAiSetting(int index) : mIndex(index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); MWMechanics::CreatureStats::AiSetting setting = (MWMechanics::CreatureStats::AiSetting)mIndex; MWMechanics::Stat stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(setting); stat.setModified(value, 0); ptr.getClass().getCreatureStats(ptr).setAiSetting(setting, stat); } }; template class OpAiFollow : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i class OpAiFollowCell : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i class OpGetCurrentAIPackage : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).getAiSequence().getLastRunTypeId(); runtime.push (value); } }; template class OpGetDetected : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr observer = R()(runtime); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); if(!actor.getClass().isActor() || !observer.getClass().isActor()) { runtime.push(0); return; } Interpreter::Type_Integer value = MWBase::Environment::get().getWorld()->getLOS(observer, actor) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer); runtime.push (value); } }; template class OpGetLineOfSight : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr source = R()(runtime); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->getPtr(actorID,true); bool value = false; if(dest != MWWorld::Ptr() && source.getClass().isActor() && dest.getClass().isActor()) { value = MWBase::Environment::get().getWorld()->getLOS(source,dest); } runtime.push (value); } }; template class OpGetTarget : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime &runtime) { MWWorld::Ptr actor = R()(runtime); std::string testedTargetId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); bool targetsAreEqual = false; MWWorld::Ptr targetPtr; if (creatureStats.getAiSequence().getCombatTarget (targetPtr)) { if (!targetPtr.isEmpty() && targetPtr.getCellRef().getRefId() == testedTargetId) targetsAreEqual = true; } runtime.push(int(targetsAreEqual)); } }; template class OpStartCombat : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime &runtime) { MWWorld::Ptr actor = R()(runtime); std::string targetID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(targetID, true); MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target); } }; template class OpStopCombat : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr actor = R()(runtime); MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); creatureStats.getAiSequence().stopCombat(); } }; template class OpToggleAI : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { bool enabled = MWBase::Environment::get().getMechanicsManager()->toggleAI(); runtime.getContext().report (enabled ? "AI -> On" : "AI -> Off"); } }; template class OpFace : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime& runtime) { /// \todo implement } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment3 (Compiler::Ai::opcodeAIActivate, new OpAiActivate); interpreter.installSegment3 (Compiler::Ai::opcodeAIActivateExplicit, new OpAiActivate); interpreter.installSegment3 (Compiler::Ai::opcodeAiTravel, new OpAiTravel); interpreter.installSegment3 (Compiler::Ai::opcodeAiTravelExplicit, new OpAiTravel); interpreter.installSegment3 (Compiler::Ai::opcodeAiEscort, new OpAiEscort); interpreter.installSegment3 (Compiler::Ai::opcodeAiEscortExplicit, new OpAiEscort); interpreter.installSegment3 (Compiler::Ai::opcodeAiEscortCell, new OpAiEscortCell); interpreter.installSegment3 (Compiler::Ai::opcodeAiEscortCellExplicit, new OpAiEscortCell); interpreter.installSegment3 (Compiler::Ai::opcodeAiWander, new OpAiWander); interpreter.installSegment3 (Compiler::Ai::opcodeAiWanderExplicit, new OpAiWander); interpreter.installSegment3 (Compiler::Ai::opcodeAiFollow, new OpAiFollow); interpreter.installSegment3 (Compiler::Ai::opcodeAiFollowExplicit, new OpAiFollow); interpreter.installSegment3 (Compiler::Ai::opcodeAiFollowCell, new OpAiFollowCell); interpreter.installSegment3 (Compiler::Ai::opcodeAiFollowCellExplicit, new OpAiFollowCell); interpreter.installSegment5 (Compiler::Ai::opcodeGetAiPackageDone, new OpGetAiPackageDone); interpreter.installSegment5 (Compiler::Ai::opcodeGetAiPackageDoneExplicit, new OpGetAiPackageDone); interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage); interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage); interpreter.installSegment5 (Compiler::Ai::opcodeGetDetected, new OpGetDetected); interpreter.installSegment5 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight); interpreter.installSegment5 (Compiler::Ai::opcodeGetTarget, new OpGetTarget); interpreter.installSegment5 (Compiler::Ai::opcodeGetTargetExplicit, new OpGetTarget); interpreter.installSegment5 (Compiler::Ai::opcodeStartCombat, new OpStartCombat); interpreter.installSegment5 (Compiler::Ai::opcodeStartCombatExplicit, new OpStartCombat); interpreter.installSegment5 (Compiler::Ai::opcodeStopCombat, new OpStopCombat); interpreter.installSegment5 (Compiler::Ai::opcodeStopCombatExplicit, new OpStopCombat); interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI); interpreter.installSegment5 (Compiler::Ai::opcodeToggleAIExplicit, new OpToggleAI); interpreter.installSegment5 (Compiler::Ai::opcodeSetHello, new OpSetAiSetting(0)); interpreter.installSegment5 (Compiler::Ai::opcodeSetHelloExplicit, new OpSetAiSetting(0)); interpreter.installSegment5 (Compiler::Ai::opcodeSetFight, new OpSetAiSetting(1)); interpreter.installSegment5 (Compiler::Ai::opcodeSetFightExplicit, new OpSetAiSetting(1)); interpreter.installSegment5 (Compiler::Ai::opcodeSetFlee, new OpSetAiSetting(2)); interpreter.installSegment5 (Compiler::Ai::opcodeSetFleeExplicit, new OpSetAiSetting(2)); interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarm, new OpSetAiSetting(3)); interpreter.installSegment5 (Compiler::Ai::opcodeSetAlarmExplicit, new OpSetAiSetting(3)); interpreter.installSegment5 (Compiler::Ai::opcodeModHello, new OpModAiSetting(0)); interpreter.installSegment5 (Compiler::Ai::opcodeModHelloExplicit, new OpModAiSetting(0)); interpreter.installSegment5 (Compiler::Ai::opcodeModFight, new OpModAiSetting(1)); interpreter.installSegment5 (Compiler::Ai::opcodeModFightExplicit, new OpModAiSetting(1)); interpreter.installSegment5 (Compiler::Ai::opcodeModFlee, new OpModAiSetting(2)); interpreter.installSegment5 (Compiler::Ai::opcodeModFleeExplicit, new OpModAiSetting(2)); interpreter.installSegment5 (Compiler::Ai::opcodeModAlarm, new OpModAiSetting(3)); interpreter.installSegment5 (Compiler::Ai::opcodeModAlarmExplicit, new OpModAiSetting(3)); interpreter.installSegment5 (Compiler::Ai::opcodeGetHello, new OpGetAiSetting(0)); interpreter.installSegment5 (Compiler::Ai::opcodeGetHelloExplicit, new OpGetAiSetting(0)); interpreter.installSegment5 (Compiler::Ai::opcodeGetFight, new OpGetAiSetting(1)); interpreter.installSegment5 (Compiler::Ai::opcodeGetFightExplicit, new OpGetAiSetting(1)); interpreter.installSegment5 (Compiler::Ai::opcodeGetFlee, new OpGetAiSetting(2)); interpreter.installSegment5 (Compiler::Ai::opcodeGetFleeExplicit, new OpGetAiSetting(2)); interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarm, new OpGetAiSetting(3)); interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarmExplicit, new OpGetAiSetting(3)); interpreter.installSegment5 (Compiler::Ai::opcodeFace, new OpFace); interpreter.installSegment5 (Compiler::Ai::opcodeFaceExplicit, new OpFace); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/aiextensions.hpp000066400000000000000000000005271264522266000237650ustar00rootroot00000000000000#ifndef GAME_SCRIPT_AIEXTENSIONS_H #define GAME_SCRIPT_AIEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief AI-related script functionality namespace Ai { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/animationextensions.cpp000066400000000000000000000072041264522266000253450ustar00rootroot00000000000000#include "animationextensions.hpp" #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace MWScript { namespace Animation { template class OpSkipAnim : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWBase::Environment::get().getMechanicsManager()->skipAnimation (ptr); } }; template class OpPlayAnim : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string group = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer mode = 0; if (arg0==1) { mode = runtime[0].mInteger; runtime.pop(); if (mode<0 || mode>2) throw std::runtime_error ("animation mode out of range"); } MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, std::numeric_limits::max()); } }; template class OpLoopAnim : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string group = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer loops = runtime[0].mInteger; runtime.pop(); if (loops<0) throw std::runtime_error ("number of animation loops must be non-negative"); Interpreter::Type_Integer mode = 0; if (arg0==1) { mode = runtime[0].mInteger; runtime.pop(); if (mode<0 || mode>2) throw std::runtime_error ("animation mode out of range"); } MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Animation::opcodeSkipAnim, new OpSkipAnim); interpreter.installSegment5 (Compiler::Animation::opcodeSkipAnimExplicit, new OpSkipAnim); interpreter.installSegment3 (Compiler::Animation::opcodePlayAnim, new OpPlayAnim); interpreter.installSegment3 (Compiler::Animation::opcodePlayAnimExplicit, new OpPlayAnim); interpreter.installSegment3 (Compiler::Animation::opcodeLoopAnim, new OpLoopAnim); interpreter.installSegment3 (Compiler::Animation::opcodeLoopAnimExplicit, new OpLoopAnim); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/animationextensions.hpp000066400000000000000000000006021264522266000253450ustar00rootroot00000000000000#ifndef GAME_SCRIPT_ANIMATIONEXTENSIONS_H #define GAME_SCRIPT_ANIMATIONEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { namespace Animation { void registerExtensions (Compiler::Extensions& extensions); void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/cellextensions.cpp000066400000000000000000000170031264522266000243030ustar00rootroot00000000000000#include "cellextensions.hpp" #include #include "../mwworld/esmstore.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/cellstore.hpp" #include "../mwmechanics/actorutil.hpp" #include "interpretercontext.hpp" namespace MWScript { namespace Cell { class OpCellChanged : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.push (MWBase::Environment::get().getWorld()->hasCellChanged() ? 1 : 0); } }; class OpCOC : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string cell = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); world->getPlayer().setTeleported(true); if (world->findExteriorPosition(cell, pos)) { world->changeToExteriorCell(pos); world->fixPosition(world->getPlayerPtr()); } else { // Change to interior even if findInteriorPosition() // yields false. In this case position will be zero-point. world->findInteriorPosition(cell, pos); world->changeToInteriorCell(cell, pos); } } }; class OpCOE : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { Interpreter::Type_Integer x = runtime[0].mInteger; runtime.pop(); Interpreter::Type_Integer y = runtime[0].mInteger; runtime.pop(); ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); world->getPlayer().setTeleported(true); world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); pos.pos[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; world->changeToExteriorCell (pos); world->fixPosition(world->getPlayerPtr()); } }; class OpGetInterior : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { if (!MWMechanics::getPlayer().isInCell()) { runtime.push (0); return; } bool interior = !MWMechanics::getPlayer().getCell()->getCell()->isExterior(); runtime.push (interior ? 1 : 0); } }; class OpGetPCCell : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); if (!MWMechanics::getPlayer().isInCell()) { runtime.push(0); return; } const MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); std::string current = MWBase::Environment::get().getWorld()->getCellName(cell); Misc::StringUtils::lowerCaseInPlace(current); bool match = current.length()>=name.length() && current.substr (0, name.length())==name; runtime.push (match ? 1 : 0); } }; class OpGetWaterLevel : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { if (!MWMechanics::getPlayer().isInCell()) { runtime.push(0.f); return; } MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); if (cell->isExterior()) runtime.push(0.f); // vanilla oddity, return 0 even though water is actually at -1 else if (cell->getCell()->hasWater()) runtime.push (cell->getWaterLevel()); else runtime.push (-std::numeric_limits::max()); } }; class OpSetWaterLevel : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { Interpreter::Type_Float level = runtime[0].mFloat; if (!MWMechanics::getPlayer().isInCell()) { return; } MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); if (cell->getCell()->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); cell->setWaterLevel (level); MWBase::Environment::get().getWorld()->setWaterHeight (cell->getWaterLevel()); } }; class OpModWaterLevel : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { Interpreter::Type_Float level = runtime[0].mFloat; if (!MWMechanics::getPlayer().isInCell()) { return; } MWWorld::CellStore *cell = MWMechanics::getPlayer().getCell(); if (cell->getCell()->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); cell->setWaterLevel (cell->getWaterLevel()+level); MWBase::Environment::get().getWorld()->setWaterHeight(cell->getWaterLevel()); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Cell::opcodeCellChanged, new OpCellChanged); interpreter.installSegment5 (Compiler::Cell::opcodeCOC, new OpCOC); interpreter.installSegment5 (Compiler::Cell::opcodeCOE, new OpCOE); interpreter.installSegment5 (Compiler::Cell::opcodeGetInterior, new OpGetInterior); interpreter.installSegment5 (Compiler::Cell::opcodeGetPCCell, new OpGetPCCell); interpreter.installSegment5 (Compiler::Cell::opcodeGetWaterLevel, new OpGetWaterLevel); interpreter.installSegment5 (Compiler::Cell::opcodeSetWaterLevel, new OpSetWaterLevel); interpreter.installSegment5 (Compiler::Cell::opcodeModWaterLevel, new OpModWaterLevel); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/cellextensions.hpp000066400000000000000000000005511264522266000243100ustar00rootroot00000000000000#ifndef GAME_SCRIPT_CELLEXTENSIONS_H #define GAME_SCRIPT_CELLEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief cell-related script functionality namespace Cell { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/compilercontext.cpp000066400000000000000000000063261264522266000244710ustar00rootroot00000000000000#include "compilercontext.hpp" #include "../mwworld/esmstore.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" #include "../mwworld/manualref.hpp" namespace MWScript { CompilerContext::CompilerContext (Type type) : mType (type) {} bool CompilerContext::canDeclareLocals() const { return mType==Type_Full; } char CompilerContext::getGlobalType (const std::string& name) const { return MWBase::Environment::get().getWorld()->getGlobalVariableType (name); } std::pair CompilerContext::getMemberType (const std::string& name, const std::string& id) const { std::string script; bool reference = false; if (const ESM::Script *scriptRecord = MWBase::Environment::get().getWorld()->getStore().get().search (id)) { script = scriptRecord->mId; } else { MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); script = ref.getPtr().getClass().getScript (ref.getPtr()); reference = true; } char type = ' '; if (!script.empty()) type = MWBase::Environment::get().getScriptManager()->getLocals (script).getType ( Misc::StringUtils::lowerCase (name)); return std::make_pair (type, reference); } bool CompilerContext::isId (const std::string& name) const { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); return store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name); } bool CompilerContext::isJournalId (const std::string& name) const { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Dialogue *topic = store.get().search (name); return topic && topic->mType==ESM::Dialogue::Journal; } } openmw-openmw-0.38.0/apps/openmw/mwscript/compilercontext.hpp000066400000000000000000000025771264522266000245020ustar00rootroot00000000000000#ifndef GAME_SCRIPT_COMPILERCONTEXT_H #define GAME_SCRIPT_COMPILERCONTEXT_H #include namespace MWScript { class CompilerContext : public Compiler::Context { public: enum Type { Type_Full, // global, local, targetted Type_Dialogue, Type_Console }; private: Type mType; public: CompilerContext (Type type); /// Is the compiler allowed to declare local variables? virtual bool canDeclareLocals() const; /// 'l: long, 's': short, 'f': float, ' ': does not exist. virtual char getGlobalType (const std::string& name) const; virtual std::pair getMemberType (const std::string& name, const std::string& id) const; ///< Return type of member variable \a name in script \a id or in script of reference of /// \a id /// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist. /// second: true: script of reference virtual bool isId (const std::string& name) const; ///< Does \a name match an ID, that can be referenced? virtual bool isJournalId (const std::string& name) const; ///< Does \a name match a journal ID? }; } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/consoleextensions.cpp000066400000000000000000000006351264522266000250310ustar00rootroot00000000000000#include "consoleextensions.hpp" #include #include #include #include #include namespace MWScript { namespace Console { void installOpcodes (Interpreter::Interpreter& interpreter) { } } } openmw-openmw-0.38.0/apps/openmw/mwscript/consoleextensions.hpp000066400000000000000000000005621264522266000250350ustar00rootroot00000000000000#ifndef GAME_SCRIPT_CONSOLEEXTENSIONS_H #define GAME_SCRIPT_CONSOLEEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief Script functionality limited to the console namespace Console { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/containerextensions.cpp000066400000000000000000000372311264522266000253530ustar00rootroot00000000000000#include "containerextensions.hpp" #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/actorutil.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace MWScript { namespace Container { template class OpAddItem : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer count = runtime[0].mInteger; runtime.pop(); if (count<0) throw std::runtime_error ("second argument for AddItem must be non-negative"); // no-op if (count == 0) return; if(::Misc::StringUtils::ciEqual(item, "gold_005") || ::Misc::StringUtils::ciEqual(item, "gold_010") || ::Misc::StringUtils::ciEqual(item, "gold_025") || ::Misc::StringUtils::ciEqual(item, "gold_100")) item = "gold_001"; MWWorld::Ptr itemPtr = *ptr.getClass().getContainerStore (ptr).add (item, count, ptr); // Spawn a messagebox (only for items added to player's inventory and if player is talking to someone) if (ptr == MWBase::Environment::get().getWorld ()->getPlayerPtr() ) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been added to their inventory std::string msgBox; std::string itemName = itemPtr.getClass().getName(itemPtr); if (count == 1) { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage60}"); msgBox = boost::str(boost::format(msgBox) % itemName); } else { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage61}"); msgBox = boost::str(boost::format(msgBox) % count % itemName); } MWBase::Environment::get().getWindowManager()->messageBox(msgBox, MWGui::ShowInDialogueMode_Only); } } }; template class OpGetItemCount : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); if(::Misc::StringUtils::ciEqual(item, "gold_005") || ::Misc::StringUtils::ciEqual(item, "gold_010") || ::Misc::StringUtils::ciEqual(item, "gold_025") || ::Misc::StringUtils::ciEqual(item, "gold_100")) item = "gold_001"; MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); runtime.push (store.count(item)); } }; template class OpRemoveItem : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer count = runtime[0].mInteger; runtime.pop(); if (count<0) throw std::runtime_error ("second argument for RemoveItem must be non-negative"); // no-op if (count == 0) return; if(::Misc::StringUtils::ciEqual(item, "gold_005") || ::Misc::StringUtils::ciEqual(item, "gold_010") || ::Misc::StringUtils::ciEqual(item, "gold_025") || ::Misc::StringUtils::ciEqual(item, "gold_100")) item = "gold_001"; MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); std::string itemName; for (MWWorld::ContainerStoreIterator iter(store.begin()); iter != store.end(); ++iter) if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item)) itemName = iter->getClass().getName(*iter); int numRemoved = store.remove(item, count, ptr); // Spawn a messagebox (only for items removed from player's inventory) if ((numRemoved > 0) && (ptr == MWMechanics::getPlayer())) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been removed from their inventory std::string msgBox; if(numRemoved > 1) { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage63}"); msgBox = boost::str (boost::format(msgBox) % numRemoved % itemName); } else { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage62}"); msgBox = boost::str (boost::format(msgBox) % itemName); } MWBase::Environment::get().getWindowManager()->messageBox(msgBox, MWGui::ShowInDialogueMode_Only); } } }; template class OpEquip : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { if (::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) break; } if (it == invStore.end()) throw std::runtime_error("Item to equip not found"); if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) MWBase::Environment::get().getWindowManager()->useItem(*it); else { boost::shared_ptr action = it->getClass().use(*it); action->execute(ptr); } } }; template class OpGetArmorType : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer location = runtime[0].mInteger; runtime.pop(); int slot; switch (location) { case 0: slot = MWWorld::InventoryStore::Slot_Helmet; break; case 1: slot = MWWorld::InventoryStore::Slot_Cuirass; break; case 2: slot = MWWorld::InventoryStore::Slot_LeftPauldron; break; case 3: slot = MWWorld::InventoryStore::Slot_RightPauldron; break; case 4: slot = MWWorld::InventoryStore::Slot_Greaves; break; case 5: slot = MWWorld::InventoryStore::Slot_Boots; break; case 6: slot = MWWorld::InventoryStore::Slot_LeftGauntlet; break; case 7: slot = MWWorld::InventoryStore::Slot_RightGauntlet; break; case 8: slot = MWWorld::InventoryStore::Slot_CarriedLeft; // shield break; case 9: slot = MWWorld::InventoryStore::Slot_LeftGauntlet; break; case 10: slot = MWWorld::InventoryStore::Slot_RightGauntlet; break; default: throw std::runtime_error ("armor index out of range"); } MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::ContainerStoreIterator it = invStore.getSlot (slot); if (it == invStore.end() || it->getTypeName () != typeid(ESM::Armor).name()) { runtime.push(-1); return; } int skill = it->getClass().getEquipmentSkill (*it) ; if (skill == ESM::Skill::HeavyArmor) runtime.push(2); else if (skill == ESM::Skill::MediumArmor) runtime.push(1); else if (skill == ESM::Skill::LightArmor) runtime.push(0); else runtime.push(-1); } }; template class OpHasItemEquipped : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { MWWorld::ContainerStoreIterator it = invStore.getSlot (slot); if (it != invStore.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) { runtime.push(1); return; } } runtime.push(0); } }; template class OpHasSoulGem : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); int count = 0; MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); for (MWWorld::ContainerStoreIterator it = invStore.begin(MWWorld::ContainerStore::Type_Miscellaneous); it != invStore.end(); ++it) { if (::Misc::StringUtils::ciEqual(it->getCellRef().getSoul(), name)) ++count; } runtime.push(count); } }; template class OpGetWeaponType : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::ContainerStoreIterator it = invStore.getSlot (MWWorld::InventoryStore::Slot_CarriedRight); if (it == invStore.end() || it->getTypeName () != typeid(ESM::Weapon).name()) { runtime.push(-1); return; } runtime.push(it->get()->mBase->mData.mType); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Container::opcodeAddItem, new OpAddItem); interpreter.installSegment5 (Compiler::Container::opcodeAddItemExplicit, new OpAddItem); interpreter.installSegment5 (Compiler::Container::opcodeGetItemCount, new OpGetItemCount); interpreter.installSegment5 (Compiler::Container::opcodeGetItemCountExplicit, new OpGetItemCount); interpreter.installSegment5 (Compiler::Container::opcodeRemoveItem, new OpRemoveItem); interpreter.installSegment5 (Compiler::Container::opcodeRemoveItemExplicit, new OpRemoveItem); interpreter.installSegment5 (Compiler::Container::opcodeEquip, new OpEquip); interpreter.installSegment5 (Compiler::Container::opcodeEquipExplicit, new OpEquip); interpreter.installSegment5 (Compiler::Container::opcodeGetArmorType, new OpGetArmorType); interpreter.installSegment5 (Compiler::Container::opcodeGetArmorTypeExplicit, new OpGetArmorType); interpreter.installSegment5 (Compiler::Container::opcodeHasItemEquipped, new OpHasItemEquipped); interpreter.installSegment5 (Compiler::Container::opcodeHasItemEquippedExplicit, new OpHasItemEquipped); interpreter.installSegment5 (Compiler::Container::opcodeHasSoulGem, new OpHasSoulGem); interpreter.installSegment5 (Compiler::Container::opcodeHasSoulGemExplicit, new OpHasSoulGem); interpreter.installSegment5 (Compiler::Container::opcodeGetWeaponType, new OpGetWeaponType); interpreter.installSegment5 (Compiler::Container::opcodeGetWeaponTypeExplicit, new OpGetWeaponType); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/containerextensions.hpp000066400000000000000000000006151264522266000253540ustar00rootroot00000000000000#ifndef GAME_SCRIPT_CONTAINEREXTENSIONS_H #define GAME_SCRIPT_CONTAINEREXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief Container-related script functionality (chests, NPCs, creatures) namespace Container { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/controlextensions.cpp000066400000000000000000000251341264522266000250500ustar00rootroot00000000000000#include "controlextensions.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" #include "../mwmechanics/npcstats.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace MWScript { namespace Control { class OpSetControl : public Interpreter::Opcode0 { std::string mControl; bool mEnable; public: OpSetControl (const std::string& control, bool enable) : mControl (control), mEnable (enable) {} virtual void execute (Interpreter::Runtime& runtime) { MWBase::Environment::get() .getInputManager() ->toggleControlSwitch(mControl, mEnable); } }; class OpGetDisabled : public Interpreter::Opcode0 { std::string mControl; public: OpGetDisabled (const std::string& control) : mControl (control) {} virtual void execute (Interpreter::Runtime& runtime) { runtime.push(!MWBase::Environment::get().getInputManager()->getControlSwitch (mControl)); } }; class OpToggleCollision : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { bool enabled = MWBase::Environment::get().getWorld()->toggleCollisionMode(); runtime.getContext().report (enabled ? "Collision -> On" : "Collision -> Off"); } }; template class OpClearMovementFlag : public Interpreter::Opcode0 { MWMechanics::CreatureStats::Flag mFlag; public: OpClearMovementFlag (MWMechanics::CreatureStats::Flag flag) : mFlag (flag) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); ptr.getClass().getCreatureStats(ptr).setMovementFlag (mFlag, false); } }; template class OpSetMovementFlag : public Interpreter::Opcode0 { MWMechanics::CreatureStats::Flag mFlag; public: OpSetMovementFlag (MWMechanics::CreatureStats::Flag flag) : mFlag (flag) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); ptr.getClass().getCreatureStats(ptr).setMovementFlag (mFlag, true); } }; template class OpGetForceRun : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun)); } }; template class OpGetForceJump : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump)); } }; template class OpGetForceMoveJump : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump)); } }; template class OpGetForceSneak : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); } }; class OpGetPcRunning : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)); } }; class OpGetPcSneaking : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak)); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { for (int i=0; i (MWMechanics::CreatureStats::Flag_ForceRun)); interpreter.installSegment5 (Compiler::Control::opcodeClearForceRunExplicit, new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun)); interpreter.installSegment5 (Compiler::Control::opcodeForceRun, new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun)); interpreter.installSegment5 (Compiler::Control::opcodeForceRunExplicit, new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun)); //Force Jump interpreter.installSegment5 (Compiler::Control::opcodeClearForceJump, new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump)); interpreter.installSegment5 (Compiler::Control::opcodeClearForceJumpExplicit, new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump)); interpreter.installSegment5 (Compiler::Control::opcodeForceJump, new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump)); interpreter.installSegment5 (Compiler::Control::opcodeForceJumpExplicit, new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump)); //Force MoveJump interpreter.installSegment5 (Compiler::Control::opcodeClearForceMoveJump, new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump)); interpreter.installSegment5 (Compiler::Control::opcodeClearForceMoveJumpExplicit, new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump)); interpreter.installSegment5 (Compiler::Control::opcodeForceMoveJump, new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump)); interpreter.installSegment5 (Compiler::Control::opcodeForceMoveJumpExplicit, new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump)); //Force Sneak interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneak, new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneakExplicit, new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); interpreter.installSegment5 (Compiler::Control::opcodeForceSneak, new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); interpreter.installSegment5 (Compiler::Control::opcodeForceSneakExplicit, new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); interpreter.installSegment5 (Compiler::Control::opcodeGetPcRunning, new OpGetPcRunning); interpreter.installSegment5 (Compiler::Control::opcodeGetPcSneaking, new OpGetPcSneaking); interpreter.installSegment5 (Compiler::Control::opcodeGetForceRun, new OpGetForceRun); interpreter.installSegment5 (Compiler::Control::opcodeGetForceRunExplicit, new OpGetForceRun); interpreter.installSegment5 (Compiler::Control::opcodeGetForceJump, new OpGetForceJump); interpreter.installSegment5 (Compiler::Control::opcodeGetForceJumpExplicit, new OpGetForceJump); interpreter.installSegment5 (Compiler::Control::opcodeGetForceMoveJump, new OpGetForceMoveJump); interpreter.installSegment5 (Compiler::Control::opcodeGetForceMoveJumpExplicit, new OpGetForceMoveJump); interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneak, new OpGetForceSneak); interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneakExplicit, new OpGetForceSneak); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/controlextensions.hpp000066400000000000000000000005631264522266000250540ustar00rootroot00000000000000#ifndef GAME_SCRIPT_CONTROLEXTENSIONS_H #define GAME_SCRIPT_CONTROLEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief player controls-related script functionality namespace Control { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/dialogueextensions.cpp000066400000000000000000000257461264522266000251720ustar00rootroot00000000000000#include "dialogueextensions.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace MWScript { namespace Dialogue { class OpJournal : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string quest = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer index = runtime[0].mInteger; runtime.pop(); // Invoking Journal with a non-existing index is allowed, and triggers no errors. Seriously? :( try { MWBase::Environment::get().getJournal()->addEntry (quest, index); } catch (...) { if (MWBase::Environment::get().getJournal()->getJournalIndex(quest) < index) MWBase::Environment::get().getJournal()->setJournalIndex(quest, index); } } }; class OpSetJournalIndex : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string quest = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer index = runtime[0].mInteger; runtime.pop(); MWBase::Environment::get().getJournal()->setJournalIndex (quest, index); } }; class OpGetJournalIndex : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string quest = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); int index = MWBase::Environment::get().getJournal()->getJournalIndex (quest); runtime.push (index); } }; class OpAddTopic : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string topic = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWBase::Environment::get().getDialogueManager()->addTopic(topic); } }; class OpChoice : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWBase::DialogueManager* dialogue = MWBase::Environment::get().getDialogueManager(); while(arg0>0) { std::string question = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); arg0 = arg0 -1; Interpreter::Type_Integer choice = 1; if(arg0>0) { choice = runtime[0].mInteger; runtime.pop(); arg0 = arg0 -1; } dialogue->askQuestion(question,choice); } } }; template class OpForceGreeting : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); if (!ptr.getRefData().isEnabled()) return; MWBase::Environment::get().getDialogueManager()->startDialogue (ptr); } }; class OpGoodbye : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime& runtime) { MWBase::Environment::get().getDialogueManager()->goodbye(); } }; template class OpModReputation : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); ptr.getClass().getNpcStats (ptr).setReputation (ptr.getClass().getNpcStats (ptr).getReputation () + value); } }; template class OpSetReputation : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); ptr.getClass().getNpcStats (ptr).setReputation (value); } }; template class OpGetReputation : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push (ptr.getClass().getNpcStats (ptr).getReputation ()); } }; template class OpSameFaction : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); player.getClass().getNpcStats (player).isInFaction(ptr.getClass().getPrimaryFaction(ptr)); } }; class OpModFactionReaction : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); int modReaction = runtime[0].mInteger; runtime.pop(); MWBase::Environment::get().getDialogueManager()->modFactionReaction(faction1, faction2, modReaction); } }; class OpGetFactionReaction : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); runtime.push(MWBase::Environment::get().getDialogueManager() ->getFactionReaction(faction1, faction2)); } }; class OpSetFactionReaction : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); int newValue = runtime[0].mInteger; runtime.pop(); MWBase::Environment::get().getDialogueManager()->setFactionReaction(faction1, faction2, newValue); } }; template class OpClearInfoActor : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWBase::Environment::get().getDialogueManager()->clearInfoActor(ptr); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Dialogue::opcodeJournal, new OpJournal); interpreter.installSegment5 (Compiler::Dialogue::opcodeSetJournalIndex, new OpSetJournalIndex); interpreter.installSegment5 (Compiler::Dialogue::opcodeGetJournalIndex, new OpGetJournalIndex); interpreter.installSegment5 (Compiler::Dialogue::opcodeAddTopic, new OpAddTopic); interpreter.installSegment3 (Compiler::Dialogue::opcodeChoice,new OpChoice); interpreter.installSegment5 (Compiler::Dialogue::opcodeForceGreeting, new OpForceGreeting); interpreter.installSegment5 (Compiler::Dialogue::opcodeForceGreetingExplicit, new OpForceGreeting); interpreter.installSegment5 (Compiler::Dialogue::opcodeGoodbye, new OpGoodbye); interpreter.installSegment5 (Compiler::Dialogue::opcodeGetReputation, new OpGetReputation); interpreter.installSegment5 (Compiler::Dialogue::opcodeSetReputation, new OpSetReputation); interpreter.installSegment5 (Compiler::Dialogue::opcodeModReputation, new OpModReputation); interpreter.installSegment5 (Compiler::Dialogue::opcodeSetReputationExplicit, new OpSetReputation); interpreter.installSegment5 (Compiler::Dialogue::opcodeModReputationExplicit, new OpModReputation); interpreter.installSegment5 (Compiler::Dialogue::opcodeGetReputationExplicit, new OpGetReputation); interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeSetFactionReaction, new OpSetFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor); interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/dialogueextensions.hpp000066400000000000000000000005671264522266000251710ustar00rootroot00000000000000#ifndef GAME_SCRIPT_DIALOGUEEXTENSIONS_H #define GAME_SCRIPT_DIALOGUEEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief Dialogue/Journal-related script functionality namespace Dialogue { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/docs/000077500000000000000000000000001264522266000214675ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwscript/docs/vmformat.txt000066400000000000000000000347471264522266000241020ustar00rootroot00000000000000OpenMW Extensions: Segment 0: (not implemented yet) opcodes 0x20-0x3f unused Segment 1: (not implemented yet) opcodes 0x20-0x3f unused Segment 2: (not implemented yet) opcodes 0x200-0x3ff unused Segment 3: op 0x20000: AiTravel op 0x20001: AiTravel, explicit reference op 0x20002: AiEscort op 0x20003: AiEscort, explicit reference op 0x20004: Lock op 0x20005: Lock, explicit reference op 0x20006: PlayAnim op 0x20007: PlayAnim, explicit reference op 0x20008: LoopAnim op 0x20009: LoopAnim, explicit reference op 0x2000a: Choice op 0x2000b: PCRaiseRank op 0x2000c: PCLowerRank op 0x2000d: PCJoinFaction op 0x2000e: PCGetRank implicit op 0x2000f: PCGetRank explicit op 0x20010: AiWander op 0x20011: AiWander, explicit reference op 0x20012: GetPCFacRep op 0x20013: GetPCFacRep, explicit reference op 0x20014: SetPCFacRep op 0x20015: SetPCFacRep, explicit reference op 0x20016: ModPCFacRep op 0x20017: ModPCFacRep, explicit reference op 0x20018: PcExpelled op 0x20019: PcExpelled, explicit op 0x2001a: PcExpell op 0x2001b: PcExpell, explicit op 0x2001c: PcClearExpelled op 0x2001d: PcClearExpelled, explicit op 0x2001e: AIActivate op 0x2001f: AIActivate, explicit reference op 0x20020: AiEscortCell op 0x20021: AiEscortCell, explicit reference op 0x20022: AiFollow op 0x20023: AiFollow, explicit reference op 0x20024: AiFollowCell op 0x20025: AiFollowCell, explicit reference op 0x20026: ModRegion op 0x20027: RemoveSoulGem op 0x20028: RemoveSoulGem, explicit reference op 0x20029: PCRaiseRank, explicit reference op 0x2002a: PCLowerRank, explicit reference op 0x2002b: PCJoinFaction, explicit reference op 0x2002c: MenuTest op 0x2002d: BetaComment op 0x2002e: BetaComment, explicit reference opcodes 0x2002d-0x3ffff unused Segment 4: (not implemented yet) opcodes 0x200-0x3ff unused Segment 5: op 0x2000000: CellChanged op 0x2000001: Say op 0x2000002: SayDone op 0x2000003: StreamMusic op 0x2000004: PlaySound op 0x2000005: PlaySoundVP op 0x2000006: PlaySound3D op 0x2000007: PlaySound3DVP op 0x2000008: PlayLoopSound3D op 0x2000009: PlayLoopSound3DVP op 0x200000a: StopSound op 0x200000b: GetSoundPlaying op 0x200000c: XBox (always 0) op 0x200000d: OnActivate op 0x200000e: EnableBirthMenu op 0x200000f: EnableClassMenu op 0x2000010: EnableNameMenu op 0x2000011: EnableRaceMenu op 0x2000012: EnableStatsReviewMenu op 0x2000013: EnableInventoryMenu op 0x2000014: EnableMagicMenu op 0x2000015: EnableMapMenu op 0x2000016: EnableStatsMenu op 0x2000017: EnableRest op 0x2000018: ShowRestMenu op 0x2000019: Say, explicit reference op 0x200001a: SayDone, explicit reference op 0x200001b: PlaySound3D, explicit reference op 0x200001c: PlaySound3DVP, explicit reference op 0x200001d: PlayLoopSound3D, explicit reference op 0x200001e: PlayLoopSound3DVP, explicit reference op 0x200001f: StopSound, explicit reference op 0x2000020: GetSoundPlaying, explicit reference op 0x2000021: ToggleSky op 0x2000022: TurnMoonWhite op 0x2000023: TurnMoonRed op 0x2000024: GetMasserPhase op 0x2000025: GetSecundaPhase op 0x2000026: COC op 0x2000027-0x200002e: GetAttribute op 0x200002f-0x2000036: GetAttribute, explicit reference op 0x2000037-0x200003e: SetAttribute op 0x200003f-0x2000046: SetAttribute, explicit reference op 0x2000047-0x200004e: ModAttribute op 0x200004f-0x2000056: ModAttribute, explicit reference op 0x2000057-0x2000059: GetDynamic (health, magicka, fatigue) op 0x200005a-0x200005c: GetDynamic (health, magicka, fatigue), explicit reference op 0x200005d-0x200005f: SetDynamic (health, magicka, fatigue) op 0x2000060-0x2000062: SetDynamic (health, magicka, fatigue), explicit reference op 0x2000063-0x2000065: ModDynamic (health, magicka, fatigue) op 0x2000066-0x2000068: ModDynamic (health, magicka, fatigue), explicit reference op 0x2000069-0x200006b: ModDynamic (health, magicka, fatigue) op 0x200006c-0x200006e: ModDynamic (health, magicka, fatigue), explicit reference op 0x200006f-0x2000071: GetDynamic (health, magicka, fatigue) op 0x2000072-0x2000074: GetDynamic (health, magicka, fatigue), explicit reference op 0x2000075: Activate op 0x2000076: AddItem op 0x2000077: AddItem, explicit reference op 0x2000078: GetItemCount op 0x2000079: GetItemCount, explicit reference op 0x200007a: RemoveItem op 0x200007b: RemoveItem, explicit reference op 0x200007c: GetAiPackageDone op 0x200007d: GetAiPackageDone, explicit reference op 0x200007e-0x2000084: Enable Controls op 0x2000085-0x200008b: Disable Controls op 0x200008c: Unlock op 0x200008d: Unlock, explicit reference op 0x200008e-0x20000a8: GetSkill op 0x20000a9-0x20000c3: GetSkill, explicit reference op 0x20000c4-0x20000de: SetSkill op 0x20000df-0x20000f9: SetSkill, explicit reference op 0x20000fa-0x2000114: ModSkill op 0x2000115-0x200012f: ModSKill, explicit reference op 0x2000130: ToggleCollision op 0x2000131: GetInterior op 0x2000132: ToggleCollsionDebug op 0x2000133: Journal op 0x2000134: SetJournalIndex op 0x2000135: GetJournalIndex op 0x2000136: GetPCCell op 0x2000137: GetButtonPressed op 0x2000138: SkipAnim op 0x2000139: SkipAnim, expplicit reference op 0x200013a: AddTopic op 0x200013b: twf op 0x200013c: FadeIn op 0x200013d: FadeOut op 0x200013e: FadeTo op 0x200013f: GetCurrentWeather op 0x2000140: ChangeWeather op 0x2000141: GetWaterLevel op 0x2000142: SetWaterLevel op 0x2000143: ModWaterLevel op 0x2000144: ToggleWater, twa op 0x2000145: ToggleFogOfWar (tfow) op 0x2000146: TogglePathgrid op 0x2000147: AddSpell op 0x2000148: AddSpell, explicit reference op 0x2000149: RemoveSpell op 0x200014a: RemoveSpell, explicit reference op 0x200014b: GetSpell op 0x200014c: GetSpell, explicit reference op 0x200014d: ModDisposition op 0x200014e: ModDisposition, explicit reference op 0x200014f: ForceGreeting op 0x2000150: ForceGreeting, explicit reference op 0x2000151: ToggleFullHelp op 0x2000152: Goodbye op 0x2000153: DontSaveObject (left unimplemented) op 0x2000154: ClearForceRun op 0x2000155: ClearForceRun, explicit reference op 0x2000156: ForceRun op 0x2000157: ForceRun, explicit reference op 0x2000158: ClearForceSneak op 0x2000159: ClearForceSneak, explicit reference op 0x200015a: ForceSneak op 0x200015b: ForceSneak, explicit reference op 0x200015c: SetHello op 0x200015d: SetHello, explicit reference op 0x200015e: SetFight op 0x200015f: SetFight, explicit reference op 0x2000160: SetFlee op 0x2000161: SetFlee, explicit reference op 0x2000162: SetAlarm op 0x2000163: SetAlarm, explicit reference op 0x2000164: SetScale op 0x2000165: SetScale, explicit reference op 0x2000166: SetAngle op 0x2000167: SetAngle, explicit reference op 0x2000168: GetScale op 0x2000169: GetScale, explicit reference op 0x200016a: GetAngle op 0x200016b: GetAngle, explicit reference op 0x200016c: user1 (console only, requires --script-console switch) op 0x200016d: user2 (console only, requires --script-console switch) op 0x200016e: user3, explicit reference (console only, requires --script-console switch) op 0x200016f: user3 (implicit reference, console only, requires --script-console switch) op 0x2000170: user4, explicit reference (console only, requires --script-console switch) op 0x2000171: user4 (implicit reference, console only, requires --script-console switch) op 0x2000172: GetStartingAngle op 0x2000173: GetStartingAngle, explicit reference op 0x2000174: ToggleVanityMode op 0x2000175-0x200018B: Get controls disabled op 0x200018C: GetLevel op 0x200018D: GetLevel, explicit reference op 0x200018E: SetLevel op 0x200018F: SetLevel, explicit reference op 0x2000190: GetPos op 0x2000191: GetPosExplicit op 0x2000192: SetPos op 0x2000193: SetPosExplicit op 0x2000194: GetStartingPos op 0x2000195: GetStartingPosExplicit op 0x2000196: Position op 0x2000197: Position Explicit op 0x2000198: PositionCell op 0x2000199: PositionCell Explicit op 0x200019a: PlaceItemCell op 0x200019b: PlaceItem op 0x200019c: PlaceAtPc op 0x200019d: PlaceAtMe op 0x200019e: PlaceAtMe Explicit op 0x200019f: GetPcSleep op 0x20001a0: ShowMap op 0x20001a1: FillMap op 0x20001a2: WakeUpPc op 0x20001a3: GetDeadCount op 0x20001a4: SetDisposition op 0x20001a5: SetDisposition, Explicit op 0x20001a6: GetDisposition op 0x20001a7: GetDisposition, Explicit op 0x20001a8: CommonDisease op 0x20001a9: CommonDisease, explicit reference op 0x20001aa: BlightDisease op 0x20001ab: BlightDisease, explicit reference op 0x20001ac: ToggleCollisionBoxes op 0x20001ad: SetReputation op 0x20001ae: ModReputation op 0x20001af: SetReputation, explicit op 0x20001b0: ModReputation, explicit op 0x20001b1: GetReputation op 0x20001b2: GetReputation, explicit op 0x20001b3: Equip op 0x20001b4: Equip, explicit op 0x20001b5: SameFaction op 0x20001b6: SameFaction, explicit op 0x20001b7: ModHello op 0x20001b8: ModHello, explicit reference op 0x20001b9: ModFight op 0x20001ba: ModFight, explicit reference op 0x20001bb: ModFlee op 0x20001bc: ModFlee, explicit reference op 0x20001bd: ModAlarm op 0x20001be: ModAlarm, explicit reference op 0x20001bf: GetHello op 0x20001c0: GetHello, explicit reference op 0x20001c1: GetFight op 0x20001c2: GetFight, explicit reference op 0x20001c3: GetFlee op 0x20001c4: GetFlee, explicit reference op 0x20001c5: GetAlarm op 0x20001c6: GetAlarm, explicit reference op 0x20001c7: GetLocked op 0x20001c8: GetLocked, explicit reference op 0x20001c9: GetPcRunning op 0x20001ca: GetPcSneaking op 0x20001cb: GetForceRun op 0x20001cc: GetForceSneak op 0x20001cd: GetForceRun, explicit op 0x20001ce: GetForceSneak, explicit op 0x20001cf: GetEffect op 0x20001d0: GetEffect, explicit op 0x20001d1: GetArmorType op 0x20001d2: GetArmorType, explicit op 0x20001d3: GetAttacked op 0x20001d4: GetAttacked, explicit op 0x20001d5: HasItemEquipped op 0x20001d6: HasItemEquipped, explicit op 0x20001d7: GetWeaponDrawn op 0x20001d8: GetWeaponDrawn, explicit op 0x20001d9: GetRace op 0x20001da: GetRace, explicit op 0x20001db: GetSpellEffects op 0x20001dc: GetSpellEffects, explicit op 0x20001dd: GetCurrentTime op 0x20001de: HasSoulGem op 0x20001df: HasSoulGem, explicit op 0x20001e0: GetWeaponType op 0x20001e1: GetWeaponType, explicit op 0x20001e2: GetWerewolfKills op 0x20001e3: ModScale op 0x20001e4: ModScale, explicit op 0x20001e5: SetDelete op 0x20001e6: SetDelete, explicit op 0x20001e7: GetSquareRoot op 0x20001e8: RaiseRank op 0x20001e9: RaiseRank, explicit op 0x20001ea: LowerRank op 0x20001eb: LowerRank, explicit op 0x20001ec: GetPCCrimeLevel op 0x20001ed: SetPCCrimeLevel op 0x20001ee: ModPCCrimeLevel op 0x20001ef: GetCurrentAIPackage op 0x20001f0: GetCurrentAIPackage, explicit reference op 0x20001f1: GetDetected op 0x20001f2: GetDetected, explicit reference op 0x20001f3: AddSoulGem op 0x20001f4: AddSoulGem, explicit reference op 0x20001f5: unused op 0x20001f6: unused op 0x20001f7: PlayBink op 0x20001f8: Drop op 0x20001f9: Drop, explicit reference op 0x20001fa: DropSoulGem op 0x20001fb: DropSoulGem, explicit reference op 0x20001fc: OnDeath op 0x20001fd: IsWerewolf op 0x20001fe: IsWerewolf, explicit reference op 0x20001ff: Rotate op 0x2000200: Rotate, explicit reference op 0x2000201: RotateWorld op 0x2000202: RotateWorld, explicit reference op 0x2000203: SetAtStart op 0x2000204: SetAtStart, explicit op 0x2000205: OnDeath, explicit op 0x2000206: Move op 0x2000207: Move, explicit op 0x2000208: MoveWorld op 0x2000209: MoveWorld, explicit op 0x200020a: Fall op 0x200020b: Fall, explicit op 0x200020c: GetStandingPC op 0x200020d: GetStandingPC, explicit op 0x200020e: GetStandingActor op 0x200020f: GetStandingActor, explicit op 0x2000210: GetStartingAngle op 0x2000211: GetStartingAngle, explicit op 0x2000212: GetWindSpeed op 0x2000213: HitOnMe op 0x2000214: HitOnMe, explicit op 0x2000215: DisableTeleporting op 0x2000216: EnableTeleporting op 0x2000217: BecomeWerewolf op 0x2000218: BecomeWerewolfExplicit op 0x2000219: UndoWerewolf op 0x200021a: UndoWerewolfExplicit op 0x200021b: SetWerewolfAcrobatics op 0x200021c: SetWerewolfAcrobaticsExplicit op 0x200021d: ShowVars op 0x200021e: ShowVarsExplicit op 0x200021f: ToggleGodMode op 0x2000220: DisableLevitation op 0x2000221: EnableLevitation op 0x2000222: GetLineOfSight op 0x2000223: GetLineOfSightExplicit op 0x2000224: ToggleAI op 0x2000225: ToggleAIExplicit op 0x2000226: COE op 0x2000227: Cast op 0x2000228: Cast, explicit op 0x2000229: ExplodeSpell op 0x200022a: ExplodeSpell, explicit op 0x200022b: RemoveSpellEffects op 0x200022c: RemoveSpellEffects, explicit op 0x200022d: RemoveEffects op 0x200022e: RemoveEffects, explicit op 0x200022f: Resurrect op 0x2000230: Resurrect, explicit op 0x2000231: GetSpellReadied op 0x2000232: GetSpellReadied, explicit op 0x2000233: GetPcJumping op 0x2000234: ShowRestMenu, explicit op 0x2000235: GoToJail op 0x2000236: PayFine op 0x2000237: PayFineThief op 0x2000238: GetTarget op 0x2000239: GetTargetExplicit op 0x200023a: StartCombat op 0x200023b: StartCombatExplicit op 0x200023c: StopCombat op 0x200023d: StopCombatExplicit op 0x200023e: GetPcInJail op 0x200023f: GetPcTraveling op 0x2000240: onKnockout op 0x2000241: onKnockoutExplicit op 0x2000242: ModFactionReaction op 0x2000243: GetFactionReaction op 0x2000244: Activate, explicit op 0x2000245: ClearInfoActor op 0x2000246: ClearInfoActor, explicit op 0x2000247: (unused) op 0x2000248: (unused) op 0x2000249: OnMurder op 0x200024a: OnMurder, explicit op 0x200024b: ToggleMenus op 0x200024c: Face op 0x200024d: Face, explicit op 0x200024e: GetStat (dummy function) op 0x200024f: GetStat (dummy function), explicit op 0x2000250: GetCollidingPC op 0x2000251: GetCollidingPC, explicit op 0x2000252: GetCollidingActor op 0x2000253: GetCollidingActor, explicit op 0x2000254: HurtStandingActor op 0x2000255: HurtStandingActor, explicit op 0x2000256: HurtCollidingActor op 0x2000257: HurtCollidingActor, explicit op 0x2000258: ClearForceJump op 0x2000259: ClearForceJump, explicit reference op 0x200025a: ForceJump op 0x200025b: ForceJump, explicit reference op 0x200025c: ClearForceMoveJump op 0x200025d: ClearForceMoveJump, explicit reference op 0x200025e: ForceMoveJump op 0x200025f: ForceMoveJump, explicit reference op 0x2000260: GetForceJump op 0x2000261: GetForceJump, explicit reference op 0x2000262: GetForceMoveJump op 0x2000263: GetForceMoveJump, explicit reference op 0x2000264-0x200027b: GetMagicEffect op 0x200027c-0x2000293: GetMagicEffect, explicit op 0x2000294-0x20002ab: SetMagicEffect op 0x20002ac-0x20002c3: SetMagicEffect, explicit op 0x20002c4-0x20002db: ModMagicEffect op 0x20002dc-0x20002f3: ModMagicEffect, explicit op 0x20002f4: ResetActors op 0x20002f5: ToggleWorld op 0x20002f6: PCForce1stPerson op 0x20002f7: PCForce3rdPerson op 0x20002f8: PCGet3rdPerson op 0x20002f9: HitAttemptOnMe op 0x20002fa: HitAttemptOnMe, explicit op 0x20002fb: AddToLevCreature op 0x20002fc: RemoveFromLevCreature op 0x20002fd: AddToLevItem op 0x20002fe: RemoveFromLevItem op 0x20002ff: SetFactionReaction op 0x2000300: EnableLevelupMenu op 0x2000301: ToggleScripts opcodes 0x2000302-0x3ffffff unused openmw-openmw-0.38.0/apps/openmw/mwscript/extensions.cpp000066400000000000000000000026461264522266000234520ustar00rootroot00000000000000#include "extensions.hpp" #include #include #include "soundextensions.hpp" #include "cellextensions.hpp" #include "miscextensions.hpp" #include "guiextensions.hpp" #include "skyextensions.hpp" #include "statsextensions.hpp" #include "containerextensions.hpp" #include "aiextensions.hpp" #include "controlextensions.hpp" #include "dialogueextensions.hpp" #include "animationextensions.hpp" #include "transformationextensions.hpp" #include "consoleextensions.hpp" #include "userextensions.hpp" namespace MWScript { void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly) { Interpreter::installOpcodes (interpreter); Cell::installOpcodes (interpreter); Misc::installOpcodes (interpreter); Gui::installOpcodes (interpreter); Sound::installOpcodes (interpreter); Sky::installOpcodes (interpreter); Stats::installOpcodes (interpreter); Container::installOpcodes (interpreter); Ai::installOpcodes (interpreter); Control::installOpcodes (interpreter); Dialogue::installOpcodes (interpreter); Animation::installOpcodes (interpreter); Transformation::installOpcodes (interpreter); if (consoleOnly) { Console::installOpcodes (interpreter); User::installOpcodes (interpreter); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/extensions.hpp000066400000000000000000000005261264522266000234520ustar00rootroot00000000000000#ifndef GAME_SCRIPT_EXTENSIONS_H #define GAME_SCRIPT_EXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly = false); ///< \param consoleOnly include console only opcodes } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/globalscripts.cpp000066400000000000000000000141571264522266000241230ustar00rootroot00000000000000#include "globalscripts.hpp" #include #include #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/scriptmanager.hpp" #include "interpretercontext.hpp" namespace MWScript { GlobalScriptDesc::GlobalScriptDesc() : mRunning (false) {} GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) : mStore (store) {} void GlobalScripts::addScript (const std::string& name, const std::string& targetId) { std::map::iterator iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) { if (const ESM::Script *script = mStore.get().find (name)) { GlobalScriptDesc desc; desc.mRunning = true; desc.mLocals.configure (*script); desc.mId = targetId; mScripts.insert (std::make_pair (name, desc)); } } else if (!iter->second.mRunning) { iter->second.mRunning = true; iter->second.mId = targetId; } } void GlobalScripts::removeScript (const std::string& name) { std::map::iterator iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter!=mScripts.end()) iter->second.mRunning = false; } bool GlobalScripts::isRunning (const std::string& name) const { std::map::const_iterator iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) return false; return iter->second.mRunning; } void GlobalScripts::run() { for (std::map::iterator iter (mScripts.begin()); iter!=mScripts.end(); ++iter) { if (iter->second.mRunning) { MWWorld::Ptr ptr; MWScript::InterpreterContext interpreterContext ( &iter->second.mLocals, MWWorld::Ptr(), iter->second.mId); MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext); } } } void GlobalScripts::clear() { mScripts.clear(); } void GlobalScripts::addStartup() { // make list of global scripts to be added std::vector scripts; scripts.push_back ("main"); for (MWWorld::Store::iterator iter = mStore.get().begin(); iter != mStore.get().end(); ++iter) { scripts.push_back (iter->mId); } // add scripts for (std::vector::const_iterator iter (scripts.begin()); iter!=scripts.end(); ++iter) { try { addScript (*iter); } catch (const std::exception& exception) { std::cerr << "Failed to add start script " << *iter << " because an exception has " << "been thrown: " << exception.what() << std::endl; } } } int GlobalScripts::countSavedGameRecords() const { return mScripts.size(); } void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { for (std::map::const_iterator iter (mScripts.begin()); iter!=mScripts.end(); ++iter) { ESM::GlobalScript script; script.mId = iter->first; iter->second.mLocals.write (script.mLocals, iter->first); script.mRunning = iter->second.mRunning ? 1 : 0; script.mTargetId = iter->second.mId; writer.startRecord (ESM::REC_GSCR); script.save (writer); writer.endRecord (ESM::REC_GSCR); } } bool GlobalScripts::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type==ESM::REC_GSCR) { ESM::GlobalScript script; script.load (reader); std::map::iterator iter = mScripts.find (script.mId); if (iter==mScripts.end()) { if (const ESM::Script *scriptRecord = mStore.get().search (script.mId)) { try { GlobalScriptDesc desc; desc.mLocals.configure (*scriptRecord); iter = mScripts.insert (std::make_pair (script.mId, desc)).first; } catch (const std::exception& exception) { std::cerr << "Failed to add start script " << script.mId << " because an exception has been thrown: " << exception.what() << std::endl; return true; } } else // script does not exist anymore return true; } iter->second.mRunning = script.mRunning!=0; iter->second.mLocals.read (script.mLocals, script.mId); iter->second.mId = script.mTargetId; return true; } return false; } Locals& GlobalScripts::getLocals (const std::string& name) { std::string name2 = ::Misc::StringUtils::lowerCase (name); std::map::iterator iter = mScripts.find (name2); if (iter==mScripts.end()) { const ESM::Script *script = mStore.get().find (name); GlobalScriptDesc desc; desc.mLocals.configure (*script); iter = mScripts.insert (std::make_pair (name2, desc)).first; } return iter->second.mLocals; } } openmw-openmw-0.38.0/apps/openmw/mwscript/globalscripts.hpp000066400000000000000000000032301264522266000241160ustar00rootroot00000000000000#ifndef GAME_SCRIPT_GLOBALSCRIPTS_H #define GAME_SCRIPT_GLOBALSCRIPTS_H #include #include #include #include "locals.hpp" namespace ESM { class ESMWriter; class ESMReader; } namespace Loading { class Listener; } namespace MWWorld { class ESMStore; } namespace MWScript { struct GlobalScriptDesc { bool mRunning; Locals mLocals; std::string mId; // ID used to start targeted script (empty if not a targeted script) GlobalScriptDesc(); }; class GlobalScripts { const MWWorld::ESMStore& mStore; std::map mScripts; public: GlobalScripts (const MWWorld::ESMStore& store); void addScript (const std::string& name, const std::string& targetId = ""); void removeScript (const std::string& name); bool isRunning (const std::string& name) const; void run(); ///< run all active global scripts void clear(); void addStartup(); ///< Add startup script int countSavedGameRecords() const; void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; bool readRecord (ESM::ESMReader& reader, uint32_t type); ///< Records for variables that do not exist are dropped silently. /// /// \return Known type? Locals& getLocals (const std::string& name); ///< If the script \a name has not been added as a global script yet, it is added /// automatically, but is not set to running state. }; } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/guiextensions.cpp000066400000000000000000000237441264522266000241610ustar00rootroot00000000000000#include "guiextensions.hpp" #include #include #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/actorutil.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace MWScript { namespace Gui { class OpEnableWindow : public Interpreter::Opcode0 { MWGui::GuiWindow mWindow; public: OpEnableWindow (MWGui::GuiWindow window) : mWindow (window) {} virtual void execute (Interpreter::Runtime& runtime) { MWBase::Environment::get().getWindowManager()->allow (mWindow); } }; class OpEnableRest : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::Environment::get().getWindowManager()->enableRest(); } }; template class OpShowRestMenu : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr bed = R()(runtime, false); if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWMechanics::getPlayer(), bed)) MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_RestBed); } }; class OpShowDialogue : public Interpreter::Opcode0 { MWGui::GuiMode mDialogue; public: OpShowDialogue (MWGui::GuiMode dialogue) : mDialogue (dialogue) {} virtual void execute (Interpreter::Runtime& runtime) { MWBase::Environment::get().getWindowManager()->pushGuiMode(mDialogue); } }; class OpGetButtonPressed : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.push (MWBase::Environment::get().getWindowManager()->readPressedButton()); } }; class OpToggleFogOfWar : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFogOfWar() ? "Fog of war -> On" : "Fog of war -> Off"); } }; class OpToggleFullHelp : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.getContext().report(MWBase::Environment::get().getWindowManager()->toggleFullHelp() ? "Full help -> On" : "Full help -> Off"); } }; class OpShowMap : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string cell = (runtime.getStringLiteral (runtime[0].mInteger)); ::Misc::StringUtils::lowerCaseInPlace(cell); runtime.pop(); // "Will match complete or partial cells, so ShowMap, "Vivec" will show cells Vivec and Vivec, Fred's House as well." // http://www.uesp.net/wiki/Tes3Mod:ShowMap const MWWorld::Store &cells = MWBase::Environment::get().getWorld()->getStore().get(); MWWorld::Store::iterator it = cells.extBegin(); for (; it != cells.extEnd(); ++it) { std::string name = it->mName; ::Misc::StringUtils::lowerCaseInPlace(name); if (name.find(cell) != std::string::npos) MWBase::Environment::get().getWindowManager()->addVisitedLocation ( it->mName, it->getGridX(), it->getGridY() ); } } }; class OpFillMap : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { const MWWorld::Store &cells = MWBase::Environment::get().getWorld ()->getStore().get(); MWWorld::Store::iterator it = cells.extBegin(); for (; it != cells.extEnd(); ++it) { std::string name = it->mName; if (name != "") MWBase::Environment::get().getWindowManager()->addVisitedLocation ( name, it->getGridX(), it->getGridY() ); } } }; class OpMenuTest : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { int arg=0; if(arg0>0) { arg = runtime[0].mInteger; runtime.pop(); } if (arg == 0) { MWGui::GuiMode modes[] = { MWGui::GM_Inventory, MWGui::GM_Container }; for (int i=0; i<2; ++i) { if (MWBase::Environment::get().getWindowManager()->containsMode(modes[i])) MWBase::Environment::get().getWindowManager()->removeGuiMode(modes[i]); } } else { MWGui::GuiWindow gw = MWGui::GW_None; if (arg == 3) gw = MWGui::GW_Stats; if (arg == 4) gw = MWGui::GW_Inventory; if (arg == 5) gw = MWGui::GW_Magic; if (arg == 6) gw = MWGui::GW_Map; MWBase::Environment::get().getWindowManager()->pinWindow(gw); } } }; class OpToggleMenus : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { bool state = MWBase::Environment::get().getWindowManager()->toggleGui(); runtime.getContext().report(state ? "GUI -> On" : "GUI -> Off"); if (!state) { while (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_None) // don't use isGuiMode, or we get an infinite loop for modal message boxes! MWBase::Environment::get().getWindowManager()->popGuiMode(); } } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Gui::opcodeEnableBirthMenu, new OpShowDialogue (MWGui::GM_Birth)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableClassMenu, new OpShowDialogue (MWGui::GM_Class)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableNameMenu, new OpShowDialogue (MWGui::GM_Name)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableRaceMenu, new OpShowDialogue (MWGui::GM_Race)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableStatsReviewMenu, new OpShowDialogue (MWGui::GM_Review)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableLevelupMenu, new OpShowDialogue (MWGui::GM_Levelup)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableInventoryMenu, new OpEnableWindow (MWGui::GW_Inventory)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableMagicMenu, new OpEnableWindow (MWGui::GW_Magic)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableMapMenu, new OpEnableWindow (MWGui::GW_Map)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableStatsMenu, new OpEnableWindow (MWGui::GW_Stats)); interpreter.installSegment5 (Compiler::Gui::opcodeEnableRest, new OpEnableRest ()); interpreter.installSegment5 (Compiler::Gui::opcodeShowRestMenu, new OpShowRestMenu); interpreter.installSegment5 (Compiler::Gui::opcodeShowRestMenuExplicit, new OpShowRestMenu); interpreter.installSegment5 (Compiler::Gui::opcodeGetButtonPressed, new OpGetButtonPressed); interpreter.installSegment5 (Compiler::Gui::opcodeToggleFogOfWar, new OpToggleFogOfWar); interpreter.installSegment5 (Compiler::Gui::opcodeToggleFullHelp, new OpToggleFullHelp); interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap); interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap); interpreter.installSegment3 (Compiler::Gui::opcodeMenuTest, new OpMenuTest); interpreter.installSegment5 (Compiler::Gui::opcodeToggleMenus, new OpToggleMenus); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/guiextensions.hpp000066400000000000000000000005441264522266000241570ustar00rootroot00000000000000#ifndef GAME_SCRIPT_GUIEXTENSIONS_H #define GAME_SCRIPT_GUIEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief GUI-related script functionality namespace Gui { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/interpretercontext.cpp000066400000000000000000000466731264522266000252330ustar00rootroot00000000000000#include "interpretercontext.hpp" #include #include #include #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/containerstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "locals.hpp" #include "globalscripts.hpp" namespace MWScript { MWWorld::Ptr InterpreterContext::getReferenceImp ( const std::string& id, bool activeOnly, bool doThrow) { if (!id.empty()) { return MWBase::Environment::get().getWorld()->getPtr (id, activeOnly); } else { if (mReference.isEmpty() && !mTargetId.empty()) mReference = MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false); if (mReference.isEmpty() && doThrow) throw std::runtime_error ("no implicit reference"); return mReference; } } const MWWorld::Ptr InterpreterContext::getReferenceImp ( const std::string& id, bool activeOnly, bool doThrow) const { if (!id.empty()) { return MWBase::Environment::get().getWorld()->getPtr (id, activeOnly); } else { if (mReference.isEmpty() && !mTargetId.empty()) mReference = MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false); if (mReference.isEmpty() && doThrow) throw std::runtime_error ("no implicit reference"); return mReference; } } const Locals& InterpreterContext::getMemberLocals (std::string& id, bool global) const { if (global) { return MWBase::Environment::get().getScriptManager()->getGlobalScripts(). getLocals (id); } else { const MWWorld::Ptr ptr = getReferenceImp (id, false); id = ptr.getClass().getScript (ptr); ptr.getRefData().setLocals ( *MWBase::Environment::get().getWorld()->getStore().get().find (id)); return ptr.getRefData().getLocals(); } } Locals& InterpreterContext::getMemberLocals (std::string& id, bool global) { if (global) { return MWBase::Environment::get().getScriptManager()->getGlobalScripts(). getLocals (id); } else { const MWWorld::Ptr ptr = getReferenceImp (id, false); id = ptr.getClass().getScript (ptr); ptr.getRefData().setLocals ( *MWBase::Environment::get().getWorld()->getStore().get().find (id)); return ptr.getRefData().getLocals(); } } int InterpreterContext::findLocalVariableIndex (const std::string& scriptId, const std::string& name, char type) const { int index = MWBase::Environment::get().getScriptManager()->getLocals (scriptId). searchIndex (type, name); if (index!=-1) return index; std::ostringstream stream; stream << "Failed to access "; switch (type) { case 's': stream << "short"; break; case 'l': stream << "long"; break; case 'f': stream << "float"; break; } stream << " member variable " << name << " in script " << scriptId; throw std::runtime_error (stream.str().c_str()); } InterpreterContext::InterpreterContext ( MWScript::Locals *locals, MWWorld::Ptr reference, const std::string& targetId) : mLocals (locals), mReference (reference), mActivationHandled (false), mTargetId (targetId) { // If we run on a reference (local script, dialogue script or console with object // selected), store the ID of that reference store it so it can be inherited by // targeted scripts started from this one. if (targetId.empty() && !reference.isEmpty()) mTargetId = reference.getCellRef().getRefId(); } int InterpreterContext::getLocalShort (int index) const { if (!mLocals) throw std::runtime_error ("local variables not available in this context"); return mLocals->mShorts.at (index); } int InterpreterContext::getLocalLong (int index) const { if (!mLocals) throw std::runtime_error ("local variables not available in this context"); return mLocals->mLongs.at (index); } float InterpreterContext::getLocalFloat (int index) const { if (!mLocals) throw std::runtime_error ("local variables not available in this context"); return mLocals->mFloats.at (index); } void InterpreterContext::setLocalShort (int index, int value) { if (!mLocals) throw std::runtime_error ("local variables not available in this context"); mLocals->mShorts.at (index) = value; } void InterpreterContext::setLocalLong (int index, int value) { if (!mLocals) throw std::runtime_error ("local variables not available in this context"); mLocals->mLongs.at (index) = value; } void InterpreterContext::setLocalFloat (int index, float value) { if (!mLocals) throw std::runtime_error ("local variables not available in this context"); mLocals->mFloats.at (index) = value; } void InterpreterContext::messageBox (const std::string& message, const std::vector& buttons) { if (buttons.empty()) MWBase::Environment::get().getWindowManager()->messageBox (message); else MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons); } void InterpreterContext::report (const std::string& message) { } bool InterpreterContext::menuMode() { return MWBase::Environment::get().getWindowManager()->isGuiMode(); } int InterpreterContext::getGlobalShort (const std::string& name) const { return MWBase::Environment::get().getWorld()->getGlobalInt (name); } int InterpreterContext::getGlobalLong (const std::string& name) const { // a global long is internally a float. return MWBase::Environment::get().getWorld()->getGlobalInt (name); } float InterpreterContext::getGlobalFloat (const std::string& name) const { return MWBase::Environment::get().getWorld()->getGlobalFloat (name); } void InterpreterContext::setGlobalShort (const std::string& name, int value) { MWBase::Environment::get().getWorld()->setGlobalInt (name, value); } void InterpreterContext::setGlobalLong (const std::string& name, int value) { MWBase::Environment::get().getWorld()->setGlobalInt (name, value); } void InterpreterContext::setGlobalFloat (const std::string& name, float value) { MWBase::Environment::get().getWorld()->setGlobalFloat (name, value); } std::vector InterpreterContext::getGlobals() const { std::vector ids; const MWWorld::Store& globals = MWBase::Environment::get().getWorld()->getStore().get(); for (MWWorld::Store::iterator iter = globals.begin(); iter!=globals.end(); ++iter) { ids.push_back (iter->mId); } return ids; } char InterpreterContext::getGlobalType (const std::string& name) const { MWBase::World *world = MWBase::Environment::get().getWorld(); return world->getGlobalVariableType(name); } std::string InterpreterContext::getActionBinding(const std::string& action) const { MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); std::vector actions = input->getActionKeySorting (); for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) { std::string desc = input->getActionDescription (*it); if(desc == "") continue; if(desc == action) { if(input->joystickLastUsed()) return input->getActionControllerBindingName(*it); else return input->getActionKeyBindingName (*it); } } return "None"; } std::string InterpreterContext::getNPCName() const { ESM::NPC npc = *getReferenceImp().get()->mBase; return npc.mName; } std::string InterpreterContext::getNPCRace() const { ESM::NPC npc = *getReferenceImp().get()->mBase; const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mRace); return race->mName; } std::string InterpreterContext::getNPCClass() const { ESM::NPC npc = *getReferenceImp().get()->mBase; const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mClass); return class_->mName; } std::string InterpreterContext::getNPCFaction() const { ESM::NPC npc = *getReferenceImp().get()->mBase; const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mFaction); return faction->mName; } std::string InterpreterContext::getNPCRank() const { const MWWorld::Ptr& ptr = getReferenceImp(); std::string faction = ptr.getClass().getPrimaryFaction(ptr); if (faction.empty()) throw std::runtime_error("getNPCRank(): NPC is not in a faction"); int rank = ptr.getClass().getPrimaryFactionRank(ptr); if (rank < 0 || rank > 9) throw std::runtime_error("getNPCRank(): invalid rank"); MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::ESMStore &store = world->getStore(); const ESM::Faction *fact = store.get().find(faction); return fact->mRanks[rank]; } std::string InterpreterContext::getPCName() const { MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = *world->getPlayerPtr().get()->mBase; return player.mName; } std::string InterpreterContext::getPCRace() const { MWBase::World *world = MWBase::Environment::get().getWorld(); std::string race = world->getPlayerPtr().get()->mBase->mRace; return world->getStore().get().find(race)->mName; } std::string InterpreterContext::getPCClass() const { MWBase::World *world = MWBase::Environment::get().getWorld(); std::string class_ = world->getPlayerPtr().get()->mBase->mClass; return world->getStore().get().find(class_)->mName; } std::string InterpreterContext::getPCRank() const { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp()); if (factionId.empty()) throw std::runtime_error("getPCRank(): NPC is not in a faction"); const std::map& ranks = player.getClass().getNpcStats (player).getFactionRanks(); std::map::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId)); int rank = -1; if (it != ranks.end()) rank = it->second; // If you are not in the faction, PcRank returns the first rank, for whatever reason. // This is used by the dialogue when joining the Thieves Guild in Balmora. if (rank == -1) rank = 0; const MWWorld::ESMStore &store = world->getStore(); const ESM::Faction *faction = store.get().find(factionId); if(rank < 0 || rank > 9) // there are only 10 ranks return ""; return faction->mRanks[rank]; } std::string InterpreterContext::getPCNextRank() const { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp()); if (factionId.empty()) throw std::runtime_error("getPCNextRank(): NPC is not in a faction"); const std::map& ranks = player.getClass().getNpcStats (player).getFactionRanks(); std::map::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId)); int rank = -1; if (it != ranks.end()) rank = it->second; ++rank; // Next rank // if we are already at max rank, there is no next rank if (rank > 9) rank = 9; const MWWorld::ESMStore &store = world->getStore(); const ESM::Faction *faction = store.get().find(factionId); if(rank < 0 || rank > 9) return ""; return faction->mRanks[rank]; } int InterpreterContext::getPCBounty() const { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); return player.getClass().getNpcStats (player).getBounty(); } std::string InterpreterContext::getCurrentCellName() const { return MWBase::Environment::get().getWorld()->getCellName(); } bool InterpreterContext::isScriptRunning (const std::string& name) const { return MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name); } void InterpreterContext::startScript (const std::string& name, const std::string& targetId) { MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, targetId); } void InterpreterContext::stopScript (const std::string& name) { MWBase::Environment::get().getScriptManager()->getGlobalScripts().removeScript (name); } float InterpreterContext::getDistance (const std::string& name, const std::string& id) const { // NOTE: id may be empty, indicating an implicit reference MWWorld::Ptr ref2; if (id.empty()) ref2 = getReferenceImp(); else ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false); if (ref2.getContainerStore()) // is the object contained? { MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(ref2); if (!container.isEmpty()) ref2 = container; else throw std::runtime_error("failed to find container ptr"); } const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->getPtr(name, false); // If the objects are in different worldspaces, return a large value (just like vanilla) if (ref.getCell()->getCell()->getCellId().mWorldspace != ref2.getCell()->getCell()->getCellId().mWorldspace) return std::numeric_limits::max(); double diff[3]; const float* const pos1 = ref.getRefData().getPosition().pos; const float* const pos2 = ref2.getRefData().getPosition().pos; for (int i=0; i<3; ++i) diff[i] = pos1[i] - pos2[i]; return static_cast(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])); } bool InterpreterContext::hasBeenActivated (const MWWorld::Ptr& ptr) { if (!mActivated.isEmpty() && mActivated==ptr) { mActivationHandled = true; return true; } return false; } bool InterpreterContext::hasActivationBeenHandled() const { return mActivationHandled; } void InterpreterContext::activate (const MWWorld::Ptr& ptr) { mActivated = ptr; mActivationHandled = false; } void InterpreterContext::executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor) { boost::shared_ptr action = (ptr.getClass().activate(ptr, actor)); action->execute (actor); if (mActivated == ptr) { mActivationHandled = true; mActivated = MWWorld::Ptr(); } } float InterpreterContext::getSecondsPassed() const { return MWBase::Environment::get().getFrameDuration(); } bool InterpreterContext::isDisabled (const std::string& id) const { const MWWorld::Ptr ref = getReferenceImp (id, false); return !ref.getRefData().isEnabled(); } void InterpreterContext::enable (const std::string& id) { MWWorld::Ptr ref = getReferenceImp (id, false); MWBase::Environment::get().getWorld()->enable (ref); } void InterpreterContext::disable (const std::string& id) { MWWorld::Ptr ref = getReferenceImp (id, false); MWBase::Environment::get().getWorld()->disable (ref); } int InterpreterContext::getMemberShort (const std::string& id, const std::string& name, bool global) const { std::string scriptId (id); const Locals& locals = getMemberLocals (scriptId, global); return locals.mShorts[findLocalVariableIndex (scriptId, name, 's')]; } int InterpreterContext::getMemberLong (const std::string& id, const std::string& name, bool global) const { std::string scriptId (id); const Locals& locals = getMemberLocals (scriptId, global); return locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')]; } float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name, bool global) const { std::string scriptId (id); const Locals& locals = getMemberLocals (scriptId, global); return locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')]; } void InterpreterContext::setMemberShort (const std::string& id, const std::string& name, int value, bool global) { std::string scriptId (id); Locals& locals = getMemberLocals (scriptId, global); locals.mShorts[findLocalVariableIndex (scriptId, name, 's')] = value; } void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value, bool global) { std::string scriptId (id); Locals& locals = getMemberLocals (scriptId, global); locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')] = value; } void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value, bool global) { std::string scriptId (id); Locals& locals = getMemberLocals (scriptId, global); locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')] = value; } MWWorld::Ptr InterpreterContext::getReference(bool required) { return getReferenceImp ("", true, required); } std::string InterpreterContext::getTargetId() const { return mTargetId; } void InterpreterContext::updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated) { if (!mReference.isEmpty() && base == mReference) mReference = updated; } } openmw-openmw-0.38.0/apps/openmw/mwscript/interpretercontext.hpp000066400000000000000000000144071264522266000252260ustar00rootroot00000000000000#ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H #define GAME_SCRIPT_INTERPRETERCONTEXT_H #include #include #include "../mwworld/ptr.hpp" #include "../mwworld/action.hpp" namespace MWSound { class SoundManager; } namespace MWInput { struct MWInputManager; } namespace MWScript { class Locals; class InterpreterContext : public Interpreter::Context { Locals *mLocals; mutable MWWorld::Ptr mReference; MWWorld::Ptr mActivated; bool mActivationHandled; std::string mTargetId; /// If \a id is empty, a reference the script is run from is returned or in case /// of a non-local script the reference derived from the target ID. MWWorld::Ptr getReferenceImp (const std::string& id = "", bool activeOnly = false, bool doThrow=true); /// If \a id is empty, a reference the script is run from is returned or in case /// of a non-local script the reference derived from the target ID. const MWWorld::Ptr getReferenceImp (const std::string& id = "", bool activeOnly = false, bool doThrow=true) const; const Locals& getMemberLocals (std::string& id, bool global) const; ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before Locals& getMemberLocals (std::string& id, bool global); ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before /// Throws an exception if local variable can't be found. int findLocalVariableIndex (const std::string& scriptId, const std::string& name, char type) const; public: InterpreterContext (MWScript::Locals *locals, MWWorld::Ptr reference, const std::string& targetId = ""); ///< The ownership of \a locals is not transferred. 0-pointer allowed. virtual int getLocalShort (int index) const; virtual int getLocalLong (int index) const; virtual float getLocalFloat (int index) const; virtual void setLocalShort (int index, int value); virtual void setLocalLong (int index, int value); virtual void setLocalFloat (int index, float value); using Interpreter::Context::messageBox; virtual void messageBox (const std::string& message, const std::vector& buttons); virtual void report (const std::string& message); ///< By default, do nothing. virtual bool menuMode(); virtual int getGlobalShort (const std::string& name) const; virtual int getGlobalLong (const std::string& name) const; virtual float getGlobalFloat (const std::string& name) const; virtual void setGlobalShort (const std::string& name, int value); virtual void setGlobalLong (const std::string& name, int value); virtual void setGlobalFloat (const std::string& name, float value); virtual std::vector getGlobals () const; virtual char getGlobalType (const std::string& name) const; virtual std::string getActionBinding(const std::string& action) const; virtual std::string getNPCName() const; virtual std::string getNPCRace() const; virtual std::string getNPCClass() const; virtual std::string getNPCFaction() const; virtual std::string getNPCRank() const; virtual std::string getPCName() const; virtual std::string getPCRace() const; virtual std::string getPCClass() const; virtual std::string getPCRank() const; virtual std::string getPCNextRank() const; virtual int getPCBounty() const; virtual std::string getCurrentCellName() const; virtual bool isScriptRunning (const std::string& name) const; virtual void startScript (const std::string& name, const std::string& targetId = ""); virtual void stopScript (const std::string& name); virtual float getDistance (const std::string& name, const std::string& id = "") const; ///< @note if \a id is empty, assumes an implicit reference bool hasBeenActivated (const MWWorld::Ptr& ptr); ///< \attention Calling this function for the right reference will mark the action as /// been handled. bool hasActivationBeenHandled() const; void activate (const MWWorld::Ptr& ptr); ///< Store reference acted upon. The actual execution of the action does not /// take place here. void executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor); ///< Execute the activation action for this ptr. If ptr is mActivated, mark activation as handled. virtual float getSecondsPassed() const; virtual bool isDisabled (const std::string& id = "") const; virtual void enable (const std::string& id = ""); virtual void disable (const std::string& id = ""); virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const; virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const; virtual float getMemberFloat (const std::string& id, const std::string& name, bool global) const; virtual void setMemberShort (const std::string& id, const std::string& name, int value, bool global); virtual void setMemberLong (const std::string& id, const std::string& name, int value, bool global); virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global); MWWorld::Ptr getReference(bool required=true); ///< Reference, that the script is running from (can be empty) void updatePtr(const MWWorld::Ptr& base, const MWWorld::Ptr& updated); ///< Update the Ptr stored in mReference, if there is one stored there. Should be called after the reference has been moved to a new cell. virtual std::string getTargetId() const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/locals.cpp000066400000000000000000000167141264522266000225310ustar00rootroot00000000000000#include "locals.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" #include namespace MWScript { void Locals::ensure (const std::string& scriptName) { if (!mInitialised) { const ESM::Script *script = MWBase::Environment::get().getWorld()->getStore(). get().find (scriptName); configure (*script); } } Locals::Locals() : mInitialised (false) {} bool Locals::configure (const ESM::Script& script) { if (mInitialised) return false; const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals (script.mId); mShorts.clear(); mShorts.resize (locals.get ('s').size(), 0); mLongs.clear(); mLongs.resize (locals.get ('l').size(), 0); mFloats.clear(); mFloats.resize (locals.get ('f').size(), 0); mInitialised = true; return true; } bool Locals::isEmpty() const { return (mShorts.empty() && mLongs.empty() && mFloats.empty()); } bool Locals::hasVar(const std::string &script, const std::string &var) { try { ensure (script); const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script); int index = locals.getIndex(var); return (index != -1); } catch (const Compiler::SourceException&) { return false; } } int Locals::getIntVar(const std::string &script, const std::string &var) { ensure (script); const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script); int index = locals.getIndex(var); char type = locals.getType(var); if(index != -1) { switch(type) { case 's': return mShorts.at (index); case 'l': return mLongs.at (index); case 'f': return static_cast(mFloats.at(index)); default: return 0; } } return 0; } bool Locals::setVarByInt(const std::string& script, const std::string& var, int val) { ensure (script); const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script); int index = locals.getIndex(var); char type = locals.getType(var); if(index != -1) { switch(type) { case 's': mShorts.at (index) = val; break; case 'l': mLongs.at (index) = val; break; case 'f': mFloats.at(index) = static_cast(val); break; } return true; } return false; } bool Locals::write (ESM::Locals& locals, const std::string& script) const { if (!mInitialised) return false; try { const Compiler::Locals& declarations = MWBase::Environment::get().getScriptManager()->getLocals(script); for (int i=0; i<3; ++i) { char type = 0; switch (i) { case 0: type = 's'; break; case 1: type = 'l'; break; case 2: type = 'f'; break; } const std::vector& names = declarations.get (type); for (int i2=0; i2 (names.size()); ++i2) { ESM::Variant value; switch (i) { case 0: value.setType (ESM::VT_Int); value.setInteger (mShorts.at (i2)); break; case 1: value.setType (ESM::VT_Int); value.setInteger (mLongs.at (i2)); break; case 2: value.setType (ESM::VT_Float); value.setFloat (mFloats.at (i2)); break; } locals.mVariables.push_back (std::make_pair (names[i2], value)); } } } catch (const Compiler::SourceException&) { } return true; } void Locals::read (const ESM::Locals& locals, const std::string& script) { ensure (script); try { const Compiler::Locals& declarations = MWBase::Environment::get().getScriptManager()->getLocals(script); int index = 0, numshorts = 0, numlongs = 0; for (unsigned int v=0; v >::const_iterator iter = locals.mVariables.begin(); iter!=locals.mVariables.end(); ++iter,++index) { if (iter->first.empty()) { // no variable names available (this will happen for legacy, i.e. ESS-imported savegames only) try { if (index >= numshorts+numlongs) mFloats.at(index - (numshorts+numlongs)) = iter->second.getFloat(); else if (index >= numshorts) mLongs.at(index - numshorts) = iter->second.getInteger(); else mShorts.at(index) = iter->second.getInteger(); } catch (std::exception& e) { std::cerr << "Failed to read local variable state for script '" << script << "' (legacy format): " << e.what() << "\nNum shorts: " << numshorts << " / " << mShorts.size() << " Num longs: " << numlongs << " / " << mLongs.size() << std::endl; } } else { char type = declarations.getType (iter->first); char index = declarations.getIndex (iter->first); try { switch (type) { case 's': mShorts.at (index) = iter->second.getInteger(); break; case 'l': mLongs.at (index) = iter->second.getInteger(); break; case 'f': mFloats.at (index) = iter->second.getFloat(); break; // silently ignore locals that don't exist anymore } } catch (...) { // ignore type changes /// \todo write to log } } } } catch (const Compiler::SourceException&) { } } } openmw-openmw-0.38.0/apps/openmw/mwscript/locals.hpp000066400000000000000000000040311264522266000225230ustar00rootroot00000000000000#ifndef GAME_SCRIPT_LOCALS_H #define GAME_SCRIPT_LOCALS_H #include #include namespace ESM { class Script; struct Locals; } namespace MWScript { class Locals { bool mInitialised; void ensure (const std::string& scriptName); public: std::vector mShorts; std::vector mLongs; std::vector mFloats; Locals(); /// Are there any locals? /// /// \note Will return false, if locals have not been configured yet. bool isEmpty() const; /// \return Did the state of *this change from uninitialised to initialised? bool configure (const ESM::Script& script); /// @note var needs to be in lowercase /// /// \note Locals will be automatically configured first, if necessary bool setVarByInt(const std::string& script, const std::string& var, int val); /// \note Locals will be automatically configured first, if necessary // // \note If it can not be determined if the variable exists, the error will be // ignored and false will be returned. bool hasVar(const std::string& script, const std::string& var); /// if var does not exist, returns 0 /// @note var needs to be in lowercase /// /// \note Locals will be automatically configured first, if necessary int getIntVar (const std::string& script, const std::string& var); /// \note If locals have not been configured yet, no data is written. /// /// \return Locals written? bool write (ESM::Locals& locals, const std::string& script) const; /// \note Locals will be automatically configured first, if necessary void read (const ESM::Locals& locals, const std::string& script); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/miscextensions.cpp000066400000000000000000001426621264522266000243310ustar00rootroot00000000000000#include "miscextensions.hpp" #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/actorutil.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace { void addToLevList(ESM::LevelledListBase* list, const std::string& itemId, int level) { for (std::vector::iterator it = list->mList.begin(); it != list->mList.end(); ++it) { if (it->mLevel == level && itemId == it->mId) return; } ESM::LevelledListBase::LevelItem item; item.mId = itemId; item.mLevel = level; list->mList.push_back(item); } void removeFromLevList(ESM::LevelledListBase* list, const std::string& itemId, int level) { // level of -1 removes all items with that itemId for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) { if (level != -1 && it->mLevel != level) { ++it; continue; } if (Misc::StringUtils::ciEqual(itemId, it->mId)) it = list->mList.erase(it); else ++it; } } } namespace MWScript { namespace Misc { class OpPlayBink : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); bool allowSkipping = runtime[0].mInteger != 0; runtime.pop(); MWBase::Environment::get().getWindowManager()->playVideo (name, allowSkipping); } }; class OpGetPcSleep : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.push (MWBase::Environment::get().getWindowManager ()->getPlayerSleeping()); } }; class OpGetPcJumping : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); runtime.push (!world->isOnGround(player) && !world->isFlying(player)); } }; class OpWakeUpPc : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::Environment::get().getWindowManager ()->wakeUpPlayer(); } }; class OpXBox : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.push (0); } }; class OpOnActivate : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { InterpreterContext& context = static_cast (runtime.getContext()); MWWorld::Ptr ptr = context.getReference(); runtime.push (context.hasBeenActivated (ptr)); } }; template class OpActivate : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { InterpreterContext& context = static_cast (runtime.getContext()); MWWorld::Ptr ptr = R()(runtime); context.executeActivation(ptr, MWMechanics::getPlayer()); } }; template class OpLock : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer lockLevel = ptr.getCellRef().getLockLevel(); if(lockLevel==0) { //no lock level was ever set, set to 100 as default lockLevel = 100; } if (arg0==1) { lockLevel = runtime[0].mInteger; runtime.pop(); } ptr.getClass().lock (ptr, lockLevel); // Instantly reset door to closed state // This is done when using Lock in scripts, but not when using Lock spells. if (ptr.getTypeName() == typeid(ESM::Door).name() && !ptr.getCellRef().getTeleport()) { MWBase::Environment::get().getWorld()->activateDoor(ptr, 0); float xr = ptr.getCellRef().getPosition().rot[0]; float yr = ptr.getCellRef().getPosition().rot[1]; float zr = ptr.getCellRef().getPosition().rot[2]; MWBase::Environment::get().getWorld()->rotateObject(ptr, xr, yr, zr); } } }; template class OpUnlock : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); ptr.getClass().unlock (ptr); } }; class OpToggleCollisionDebug : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_CollisionDebug); runtime.getContext().report (enabled ? "Collision Mesh Rendering -> On" : "Collision Mesh Rendering -> Off"); } }; class OpToggleCollisionBoxes : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_BoundingBoxes); runtime.getContext().report (enabled ? "Bounding Box Rendering -> On" : "Bounding Box Rendering -> Off"); } }; class OpToggleWireframe : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_Wireframe); runtime.getContext().report (enabled ? "Wireframe Rendering -> On" : "Wireframe Rendering -> Off"); } }; class OpTogglePathgrid : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_Pathgrid); runtime.getContext().report (enabled ? "Path Grid rendering -> On" : "Path Grid Rendering -> Off"); } }; class OpFadeIn : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { Interpreter::Type_Float time = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWindowManager()->fadeScreenIn(time, false); } }; class OpFadeOut : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { Interpreter::Type_Float time = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWindowManager()->fadeScreenOut(time, false); } }; class OpFadeTo : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { Interpreter::Type_Float alpha = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float time = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWindowManager()->fadeScreenTo(static_cast(alpha), time, false); } }; class OpToggleWater : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWater() ? "Water -> On" : "Water -> Off"); } }; class OpToggleWorld : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWorld() ? "World -> On" : "World -> Off"); } }; class OpDontSaveObject : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { // We are ignoring the DontSaveObject statement for now. Probably not worth // bothering with. The incompatibility we are creating should be marginal at most. } }; class OpPcForce1stPerson : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { if (!MWBase::Environment::get().getWorld()->isFirstPerson()) MWBase::Environment::get().getWorld()->togglePOV(); } }; class OpPcForce3rdPerson : public Interpreter::Opcode0 { virtual void execute (Interpreter::Runtime& runtime) { if (MWBase::Environment::get().getWorld()->isFirstPerson()) MWBase::Environment::get().getWorld()->togglePOV(); } }; class OpPcGet3rdPerson : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime& runtime) { runtime.push(!MWBase::Environment::get().getWorld()->isFirstPerson()); } }; class OpToggleVanityMode : public Interpreter::Opcode0 { static bool sActivate; public: virtual void execute(Interpreter::Runtime &runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); if (world->toggleVanityMode(sActivate)) { runtime.getContext().report(sActivate ? "Vanity Mode -> On" : "Vanity Mode -> Off"); sActivate = !sActivate; } else { runtime.getContext().report("Vanity Mode -> No"); } } }; bool OpToggleVanityMode::sActivate = true; template class OpGetLocked : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push (ptr.getCellRef().getLockLevel() > 0); } }; template class OpGetEffect : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string effect = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); char *end; long key = strtol(effect.c_str(), &end, 10); if(key < 0 || key > 32767 || *end != '\0') key = ESM::MagicEffect::effectStringToId(effect); const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); for (MWMechanics::MagicEffects::Collection::const_iterator it = effects.begin(); it != effects.end(); ++it) { if (it->first.mId == key && it->second.getModifier() > 0) { runtime.push(1); return; } } runtime.push(0); } }; template class OpAddSoulGem : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string creature = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string gem = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); store.get().find(creature); // This line throws an exception if it can't find the creature MWWorld::Ptr item = *ptr.getClass().getContainerStore(ptr).add(gem, 1, ptr); item.getCellRef().setSoul(creature); } }; template class OpRemoveSoulGem : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string soul = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); // throw away additional arguments for (unsigned int i=0; igetCellRef().getSoul(), soul)) { store.remove(*it, 1, ptr); return; } } } }; template class OpDrop : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer amount = runtime[0].mInteger; runtime.pop(); if (amount<0) throw std::runtime_error ("amount must be non-negative"); // no-op if (amount == 0) return; MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); int toRemove = amount; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item)) { int removed = store.remove(*iter, toRemove, ptr); MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); dropped.getCellRef().setOwner(""); toRemove -= removed; if (toRemove <= 0) break; } } } }; template class OpDropSoulGem : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string soul = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { if (::Misc::StringUtils::ciEqual(iter->getCellRef().getSoul(), soul)) { MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, 1); store.remove(*iter, 1, ptr); break; } } } }; template class OpGetAttacked : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push(ptr.getClass().getCreatureStats (ptr).getAttacked ()); } }; template class OpGetWeaponDrawn : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push((ptr.getClass().hasInventoryStore(ptr) || ptr.getClass().isBipedal(ptr)) && ptr.getClass().getCreatureStats (ptr).getDrawState () == MWMechanics::DrawState_Weapon); } }; template class OpGetSpellReadied : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push(ptr.getClass().getCreatureStats (ptr).getDrawState () == MWMechanics::DrawState_Spell); } }; template class OpGetSpellEffects : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string id = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); runtime.push(stats.getActiveSpells().isSpellActive(id) || stats.getSpells().isSpellActive(id)); } }; class OpGetCurrentTime : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.push(MWBase::Environment::get().getWorld()->getTimeStamp().getHour()); } }; template class OpSetDelete : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); int parameter = runtime[0].mInteger; runtime.pop(); if (parameter == 1) MWBase::Environment::get().getWorld()->deleteObject(ptr); else if (parameter == 0) MWBase::Environment::get().getWorld()->undeleteObject(ptr); else throw std::runtime_error("SetDelete: unexpected parameter"); } }; class OpGetSquareRoot : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { float param = runtime[0].mFloat; runtime.pop(); runtime.push(std::sqrt (param)); } }; template class OpFall : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { } }; template class OpGetStandingPc : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push (MWBase::Environment::get().getWorld()->getPlayerStandingOn(ptr)); } }; template class OpGetStandingActor : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push (MWBase::Environment::get().getWorld()->getActorStandingOn(ptr)); } }; template class OpGetCollidingPc : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push (MWBase::Environment::get().getWorld()->getPlayerCollidingWith(ptr)); } }; template class OpGetCollidingActor : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push (MWBase::Environment::get().getWorld()->getActorCollidingWith(ptr)); } }; template class OpHurtStandingActor : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); float healthDiffPerSecond = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWorld()->hurtStandingActors(ptr, healthDiffPerSecond); } }; template class OpHurtCollidingActor : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); float healthDiffPerSecond = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWorld()->hurtCollidingActors(ptr, healthDiffPerSecond); } }; class OpGetWindSpeed : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.push(MWBase::Environment::get().getWorld()->getWindSpeed()); } }; template class OpHitOnMe : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string objectID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); runtime.push(::Misc::StringUtils::ciEqual(objectID, stats.getLastHitObject())); stats.setLastHitObject(std::string()); } }; template class OpHitAttemptOnMe : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string objectID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); runtime.push(::Misc::StringUtils::ciEqual(objectID, stats.getLastHitAttemptObject())); stats.setLastHitAttemptObject(std::string()); } }; template class OpEnableTeleporting : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); world->enableTeleporting(Enable); } }; template class OpEnableLevitation : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); world->enableLevitation(Enable); } }; template class OpShowVars : public Interpreter::Opcode0 { void printLocalVars(Interpreter::Runtime &runtime, const MWWorld::Ptr &ptr) { std::stringstream str; const std::string script = ptr.getClass().getScript(ptr); if(script.empty()) str<< ptr.getCellRef().getRefId()<<" does not have a script."; else { str<< "Local variables for "<getLocals(script); const std::vector *names = &complocals.get('s'); for(size_t i = 0;i < names->size();++i) { if(i >= locals.mShorts.size()) break; str<size();++i) { if(i >= locals.mLongs.size()) break; str<size();++i) { if(i >= locals.mFloats.size()) break; str< names = runtime.getContext().getGlobals(); for(size_t i = 0;i < names.size();++i) { char type = world->getGlobalVariableType (names[i]); str << std::endl << " " << names[i] << " = "; switch (type) { case 's': str << runtime.getContext().getGlobalShort (names[i]) << " (short)"; break; case 'l': str << runtime.getContext().getGlobalLong (names[i]) << " (long)"; break; case 'f': str << runtime.getContext().getGlobalFloat (names[i]) << " (float)"; break; default: str << ""; } } runtime.getContext().report (str.str()); } public: virtual void execute(Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime, false); if (!ptr.isEmpty()) printLocalVars(runtime, ptr); else { // No reference, no problem. printGlobalVars(runtime); } } }; class OpToggleScripts : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { bool enabled = MWBase::Environment::get().getWorld()->toggleScripts(); runtime.getContext().report(enabled ? "Scripts -> On" : "Scripts -> Off"); } }; class OpToggleGodMode : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { bool enabled = MWBase::Environment::get().getWorld()->toggleGodMode(); runtime.getContext().report (enabled ? "God Mode -> On" : "God Mode -> Off"); } }; template class OpCast : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string spell = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string targetId = ::Misc::StringUtils::lowerCase(runtime.getStringLiteral (runtime[0].mInteger)); runtime.pop(); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); MWMechanics::CastSpell cast(ptr, target); cast.mHitPosition = target.getRefData().getPosition().asVec3(); cast.mAlwaysSucceed = true; cast.cast(spell); } }; template class OpExplodeSpell : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string spell = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWMechanics::CastSpell cast(ptr, ptr); cast.mHitPosition = ptr.getRefData().getPosition().asVec3(); cast.mAlwaysSucceed = true; cast.cast(spell); } }; class OpGoToJail : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::World* world = MWBase::Environment::get().getWorld(); world->goToJail(); } }; class OpPayFine : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats(player).setBounty(0); MWBase::Environment::get().getWorld()->confiscateStolenItems(player); MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; class OpPayFineThief : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats(player).setBounty(0); MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; class OpGetPcInJail : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime &runtime) { runtime.push (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail)); } }; class OpGetPcTraveling : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime &runtime) { /// \todo implement traveling check runtime.push (0); } }; template class OpBetaComment : public Interpreter::Opcode1 { public: virtual void execute(Interpreter::Runtime &runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::stringstream msg; msg << "Content file: "; if (!ptr.getCellRef().hasContentFile()) msg << "[None]" << std::endl; else { std::vector contentFiles = MWBase::Environment::get().getWorld()->getContentFiles(); msg << contentFiles.at (ptr.getCellRef().getRefNum().mContentFile) << std::endl; msg << "RefNum: " << ptr.getCellRef().getRefNum().mIndex << std::endl; } if (ptr.getRefData().isDeletedByContentFile()) msg << "[Deleted by content file]" << std::endl; if (!ptr.getRefData().getCount()) msg << "[Deleted]" << std::endl; msg << "RefID: " << ptr.getCellRef().getRefId() << std::endl; if (ptr.isInCell()) { MWWorld::CellStore* cell = ptr.getCell(); msg << "Cell: " << MWBase::Environment::get().getWorld()->getCellName(cell) << std::endl; if (cell->getCell()->isExterior()) msg << "Grid: " << cell->getCell()->getGridX() << " " << cell->getCell()->getGridY() << std::endl; osg::Vec3f pos (ptr.getRefData().getPosition().asVec3()); msg << "Coordinates: " << pos.x() << " " << pos.y() << " " << pos.z() << std::endl; msg << "Model: " << ptr.getClass().getModel(ptr) << std::endl; if (!ptr.getClass().getScript(ptr).empty()) msg << "Script: " << ptr.getClass().getScript(ptr) << std::endl; } while (arg0 > 0) { std::string notes = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); if (!notes.empty()) msg << "Notes: " << notes << std::endl; --arg0; } runtime.getContext().report(msg.str()); } }; class OpAddToLevCreature : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); const std::string& creatureId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); int level = runtime[0].mInteger; runtime.pop(); ESM::CreatureLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); addToLevList(&listCopy, creatureId, level); MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); } }; class OpRemoveFromLevCreature : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); const std::string& creatureId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); int level = runtime[0].mInteger; runtime.pop(); ESM::CreatureLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); removeFromLevList(&listCopy, creatureId, level); MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); } }; class OpAddToLevItem : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); const std::string& itemId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); int level = runtime[0].mInteger; runtime.pop(); ESM::ItemLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); addToLevList(&listCopy, itemId, level); MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); } }; class OpRemoveFromLevItem : public Interpreter::Opcode0 { public: virtual void execute(Interpreter::Runtime &runtime) { const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); const std::string& itemId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); int level = runtime[0].mInteger; runtime.pop(); ESM::ItemLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); removeFromLevList(&listCopy, itemId, level); MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); interpreter.installSegment5 (Compiler::Misc::opcodeOnActivate, new OpOnActivate); interpreter.installSegment5 (Compiler::Misc::opcodeActivate, new OpActivate); interpreter.installSegment5 (Compiler::Misc::opcodeActivateExplicit, new OpActivate); interpreter.installSegment3 (Compiler::Misc::opcodeLock, new OpLock); interpreter.installSegment3 (Compiler::Misc::opcodeLockExplicit, new OpLock); interpreter.installSegment5 (Compiler::Misc::opcodeUnlock, new OpUnlock); interpreter.installSegment5 (Compiler::Misc::opcodeUnlockExplicit, new OpUnlock); interpreter.installSegment5 (Compiler::Misc::opcodeToggleCollisionDebug, new OpToggleCollisionDebug); interpreter.installSegment5 (Compiler::Misc::opcodeToggleCollisionBoxes, new OpToggleCollisionBoxes); interpreter.installSegment5 (Compiler::Misc::opcodeToggleWireframe, new OpToggleWireframe); interpreter.installSegment5 (Compiler::Misc::opcodeFadeIn, new OpFadeIn); interpreter.installSegment5 (Compiler::Misc::opcodeFadeOut, new OpFadeOut); interpreter.installSegment5 (Compiler::Misc::opcodeFadeTo, new OpFadeTo); interpreter.installSegment5 (Compiler::Misc::opcodeTogglePathgrid, new OpTogglePathgrid); interpreter.installSegment5 (Compiler::Misc::opcodeToggleWater, new OpToggleWater); interpreter.installSegment5 (Compiler::Misc::opcodeToggleWorld, new OpToggleWorld); interpreter.installSegment5 (Compiler::Misc::opcodeDontSaveObject, new OpDontSaveObject); interpreter.installSegment5 (Compiler::Misc::opcodePcForce1stPerson, new OpPcForce1stPerson); interpreter.installSegment5 (Compiler::Misc::opcodePcForce3rdPerson, new OpPcForce3rdPerson); interpreter.installSegment5 (Compiler::Misc::opcodePcGet3rdPerson, new OpPcGet3rdPerson); interpreter.installSegment5 (Compiler::Misc::opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcSleep, new OpGetPcSleep); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcJumping, new OpGetPcJumping); interpreter.installSegment5 (Compiler::Misc::opcodeWakeUpPc, new OpWakeUpPc); interpreter.installSegment5 (Compiler::Misc::opcodePlayBink, new OpPlayBink); interpreter.installSegment5 (Compiler::Misc::opcodePayFine, new OpPayFine); interpreter.installSegment5 (Compiler::Misc::opcodePayFineThief, new OpPayFineThief); interpreter.installSegment5 (Compiler::Misc::opcodeGoToJail, new OpGoToJail); interpreter.installSegment5 (Compiler::Misc::opcodeGetLocked, new OpGetLocked); interpreter.installSegment5 (Compiler::Misc::opcodeGetLockedExplicit, new OpGetLocked); interpreter.installSegment5 (Compiler::Misc::opcodeGetEffect, new OpGetEffect); interpreter.installSegment5 (Compiler::Misc::opcodeGetEffectExplicit, new OpGetEffect); interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGem, new OpAddSoulGem); interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGemExplicit, new OpAddSoulGem); interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem); interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); interpreter.installSegment5 (Compiler::Misc::opcodeDrop, new OpDrop); interpreter.installSegment5 (Compiler::Misc::opcodeDropExplicit, new OpDrop); interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGem, new OpDropSoulGem); interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGemExplicit, new OpDropSoulGem); interpreter.installSegment5 (Compiler::Misc::opcodeGetAttacked, new OpGetAttacked); interpreter.installSegment5 (Compiler::Misc::opcodeGetAttackedExplicit, new OpGetAttacked); interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawn, new OpGetWeaponDrawn); interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellReadied, new OpGetSpellReadied); interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellReadiedExplicit, new OpGetSpellReadied); interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffects, new OpGetSpellEffects); interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffectsExplicit, new OpGetSpellEffects); interpreter.installSegment5 (Compiler::Misc::opcodeGetCurrentTime, new OpGetCurrentTime); interpreter.installSegment5 (Compiler::Misc::opcodeSetDelete, new OpSetDelete); interpreter.installSegment5 (Compiler::Misc::opcodeSetDeleteExplicit, new OpSetDelete); interpreter.installSegment5 (Compiler::Misc::opcodeGetSquareRoot, new OpGetSquareRoot); interpreter.installSegment5 (Compiler::Misc::opcodeFall, new OpFall); interpreter.installSegment5 (Compiler::Misc::opcodeFallExplicit, new OpFall); interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPc, new OpGetStandingPc); interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPcExplicit, new OpGetStandingPc); interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActor, new OpGetStandingActor); interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActorExplicit, new OpGetStandingActor); interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingPc, new OpGetCollidingPc); interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingPcExplicit, new OpGetCollidingPc); interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingActor, new OpGetCollidingActor); interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingActorExplicit, new OpGetCollidingActor); interpreter.installSegment5 (Compiler::Misc::opcodeHurtStandingActor, new OpHurtStandingActor); interpreter.installSegment5 (Compiler::Misc::opcodeHurtStandingActorExplicit, new OpHurtStandingActor); interpreter.installSegment5 (Compiler::Misc::opcodeHurtCollidingActor, new OpHurtCollidingActor); interpreter.installSegment5 (Compiler::Misc::opcodeHurtCollidingActorExplicit, new OpHurtCollidingActor); interpreter.installSegment5 (Compiler::Misc::opcodeGetWindSpeed, new OpGetWindSpeed); interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMe, new OpHitOnMe); interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMeExplicit, new OpHitOnMe); interpreter.installSegment5 (Compiler::Misc::opcodeHitAttemptOnMe, new OpHitAttemptOnMe); interpreter.installSegment5 (Compiler::Misc::opcodeHitAttemptOnMeExplicit, new OpHitAttemptOnMe); interpreter.installSegment5 (Compiler::Misc::opcodeDisableTeleporting, new OpEnableTeleporting); interpreter.installSegment5 (Compiler::Misc::opcodeEnableTeleporting, new OpEnableTeleporting); interpreter.installSegment5 (Compiler::Misc::opcodeShowVars, new OpShowVars); interpreter.installSegment5 (Compiler::Misc::opcodeShowVarsExplicit, new OpShowVars); interpreter.installSegment5 (Compiler::Misc::opcodeToggleGodMode, new OpToggleGodMode); interpreter.installSegment5 (Compiler::Misc::opcodeToggleScripts, new OpToggleScripts); interpreter.installSegment5 (Compiler::Misc::opcodeDisableLevitation, new OpEnableLevitation); interpreter.installSegment5 (Compiler::Misc::opcodeEnableLevitation, new OpEnableLevitation); interpreter.installSegment5 (Compiler::Misc::opcodeCast, new OpCast); interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast); interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpell, new OpExplodeSpell); interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcInJail, new OpGetPcInJail); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcTraveling, new OpGetPcTraveling); interpreter.installSegment3 (Compiler::Misc::opcodeBetaComment, new OpBetaComment); interpreter.installSegment3 (Compiler::Misc::opcodeBetaCommentExplicit, new OpBetaComment); interpreter.installSegment5 (Compiler::Misc::opcodeAddToLevCreature, new OpAddToLevCreature); interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevCreature, new OpRemoveFromLevCreature); interpreter.installSegment5 (Compiler::Misc::opcodeAddToLevItem, new OpAddToLevItem); interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevItem, new OpRemoveFromLevItem); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/miscextensions.hpp000066400000000000000000000004701264522266000243240ustar00rootroot00000000000000#ifndef GAME_SCRIPT_MISCEXTENSIONS_H #define GAME_SCRIPT_MISCEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { namespace Misc { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/ref.cpp000066400000000000000000000015271264522266000220240ustar00rootroot00000000000000#include "ref.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "interpretercontext.hpp" MWWorld::Ptr MWScript::ExplicitRef::operator() (Interpreter::Runtime& runtime, bool required, bool activeOnly) const { std::string id = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); if (required) return MWBase::Environment::get().getWorld()->getPtr(id, activeOnly); else return MWBase::Environment::get().getWorld()->searchPtr(id, activeOnly); } MWWorld::Ptr MWScript::ImplicitRef::operator() (Interpreter::Runtime& runtime, bool required, bool activeOnly) const { MWScript::InterpreterContext& context = static_cast (runtime.getContext()); return context.getReference(required); } openmw-openmw-0.38.0/apps/openmw/mwscript/ref.hpp000066400000000000000000000011361264522266000220250ustar00rootroot00000000000000#ifndef GAME_MWSCRIPT_REF_H #define GAME_MWSCRIPT_REF_H #include #include "../mwworld/ptr.hpp" namespace Interpreter { class Runtime; } namespace MWScript { struct ExplicitRef { static const bool implicit = false; MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required = true, bool activeOnly = false) const; }; struct ImplicitRef { static const bool implicit = true; MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required = true, bool activeOnly = false) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/scriptmanagerimp.cpp000066400000000000000000000142161264522266000246140ustar00rootroot00000000000000#include "scriptmanagerimp.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "../mwworld/esmstore.hpp" #include "extensions.hpp" namespace MWScript { ScriptManager::ScriptManager (const MWWorld::ESMStore& store, bool verbose, Compiler::Context& compilerContext, int warningsMode, const std::vector& scriptBlacklist) : mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), mOpcodesInstalled (false), mGlobalScripts (store) { mErrorHandler.setWarningsMode (warningsMode); mScriptBlacklist.resize (scriptBlacklist.size()); std::transform (scriptBlacklist.begin(), scriptBlacklist.end(), mScriptBlacklist.begin(), Misc::StringUtils::lowerCase); std::sort (mScriptBlacklist.begin(), mScriptBlacklist.end()); } bool ScriptManager::compile (const std::string& name) { mParser.reset(); mErrorHandler.reset(); if (const ESM::Script *script = mStore.get().find (name)) { if (mVerbose) std::cout << "compiling script: " << name << std::endl; bool Success = true; try { std::istringstream input (script->mScriptText); Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions()); scanner.scan (mParser); if (!mErrorHandler.isGood()) Success = false; } catch (const Compiler::SourceException&) { // error has already been reported via error handler Success = false; } catch (const std::exception& error) { std::cerr << "An exception has been thrown: " << error.what() << std::endl; Success = false; } if (!Success) { std::cerr << "compiling failed: " << name << std::endl; if (mVerbose) std::cerr << script->mScriptText << std::endl << std::endl; } if (Success) { std::vector code; mParser.getCode (code); mScripts.insert (std::make_pair (name, std::make_pair (code, mParser.getLocals()))); return true; } } return false; } void ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext) { // compile script ScriptCollection::iterator iter = mScripts.find (name); if (iter==mScripts.end()) { if (!compile (name)) { // failed -> ignore script from now on. std::vector empty; mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals()))); return; } iter = mScripts.find (name); assert (iter!=mScripts.end()); } // execute script if (!iter->second.first.empty()) try { if (!mOpcodesInstalled) { installOpcodes (mInterpreter); mOpcodesInstalled = true; } mInterpreter.run (&iter->second.first[0], iter->second.first.size(), interpreterContext); } catch (const std::exception& e) { std::cerr << "Execution of script " << name << " failed:" << std::endl; std::cerr << e.what() << std::endl; iter->second.first.clear(); // don't execute again. } } std::pair ScriptManager::compileAll() { int count = 0; int success = 0; const MWWorld::Store& scripts = mStore.get(); for (MWWorld::Store::iterator iter = scripts.begin(); iter != scripts.end(); ++iter) if (!std::binary_search (mScriptBlacklist.begin(), mScriptBlacklist.end(), Misc::StringUtils::lowerCase (iter->mId))) { ++count; if (compile (iter->mId)) ++success; } return std::make_pair (count, success); } const Compiler::Locals& ScriptManager::getLocals (const std::string& name) { std::string name2 = Misc::StringUtils::lowerCase (name); { ScriptCollection::iterator iter = mScripts.find (name2); if (iter!=mScripts.end()) return iter->second.second; } { std::map::iterator iter = mOtherLocals.find (name2); if (iter!=mOtherLocals.end()) return iter->second; } if (const ESM::Script *script = mStore.get().search (name2)) { if (mVerbose) std::cout << "scanning script for local variable declarations: " << name2 << std::endl; Compiler::Locals locals; std::istringstream stream (script->mScriptText); Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals); Compiler::Scanner scanner (mErrorHandler, stream, mCompilerContext.getExtensions()); scanner.scan (parser); std::map::iterator iter = mOtherLocals.insert (std::make_pair (name2, locals)).first; return iter->second; } throw std::logic_error ("script " + name + " does not exist"); } GlobalScripts& ScriptManager::getGlobalScripts() { return mGlobalScripts; } } openmw-openmw-0.38.0/apps/openmw/mwscript/scriptmanagerimp.hpp000066400000000000000000000042241264522266000246170ustar00rootroot00000000000000#ifndef GAME_SCRIPT_SCRIPTMANAGER_H #define GAME_SCRIPT_SCRIPTMANAGER_H #include #include #include #include #include #include #include "../mwbase/scriptmanager.hpp" #include "globalscripts.hpp" namespace MWWorld { class ESMStore; } namespace Compiler { class Context; } namespace Interpreter { class Context; class Interpreter; } namespace MWScript { class ScriptManager : public MWBase::ScriptManager { Compiler::StreamErrorHandler mErrorHandler; const MWWorld::ESMStore& mStore; bool mVerbose; Compiler::Context& mCompilerContext; Compiler::FileParser mParser; Interpreter::Interpreter mInterpreter; bool mOpcodesInstalled; typedef std::pair, Compiler::Locals> CompiledScript; typedef std::map ScriptCollection; ScriptCollection mScripts; GlobalScripts mGlobalScripts; std::map mOtherLocals; std::vector mScriptBlacklist; public: ScriptManager (const MWWorld::ESMStore& store, bool verbose, Compiler::Context& compilerContext, int warningsMode, const std::vector& scriptBlacklist); virtual void run (const std::string& name, Interpreter::Context& interpreterContext); ///< Run the script with the given name (compile first, if not compiled yet) virtual bool compile (const std::string& name); ///< Compile script with the given namen /// \return Success? virtual std::pair compileAll(); ///< Compile all scripts /// \return count, success virtual const Compiler::Locals& getLocals (const std::string& name); ///< Return locals for script \a name. virtual GlobalScripts& getGlobalScripts(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/skyextensions.cpp000066400000000000000000000105511264522266000241730ustar00rootroot00000000000000#include "skyextensions.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "interpretercontext.hpp" namespace MWScript { namespace Sky { class OpToggleSky : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { bool enabled = MWBase::Environment::get().getWorld()->toggleSky(); runtime.getContext().report (enabled ? "Sky -> On" : "Sky -> Off"); } }; class OpTurnMoonWhite : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::Environment::get().getWorld()->setMoonColour (false); } }; class OpTurnMoonRed : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::Environment::get().getWorld()->setMoonColour (true); } }; class OpGetMasserPhase : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.push (MWBase::Environment::get().getWorld()->getMasserPhase()); } }; class OpGetSecundaPhase : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.push (MWBase::Environment::get().getWorld()->getSecundaPhase()); } }; class OpGetCurrentWeather : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.push (MWBase::Environment::get().getWorld()->getCurrentWeather()); } }; class OpChangeWeather : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string region = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer id = runtime[0].mInteger; runtime.pop(); MWBase::Environment::get().getWorld()->changeWeather(region, id); } }; class OpModRegion : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { std::string region = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::vector chances; chances.reserve(10); while(arg0 > 0) { chances.push_back(std::max(0, std::min(127, runtime[0].mInteger))); runtime.pop(); arg0--; } MWBase::Environment::get().getWorld()->modRegion(region, chances); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Sky::opcodeToggleSky, new OpToggleSky); interpreter.installSegment5 (Compiler::Sky::opcodeTurnMoonWhite, new OpTurnMoonWhite); interpreter.installSegment5 (Compiler::Sky::opcodeTurnMoonRed, new OpTurnMoonRed); interpreter.installSegment5 (Compiler::Sky::opcodeGetMasserPhase, new OpGetMasserPhase); interpreter.installSegment5 (Compiler::Sky::opcodeGetSecundaPhase, new OpGetSecundaPhase); interpreter.installSegment5 (Compiler::Sky::opcodeGetCurrentWeather, new OpGetCurrentWeather); interpreter.installSegment5 (Compiler::Sky::opcodeChangeWeather, new OpChangeWeather); interpreter.installSegment3 (Compiler::Sky::opcodeModRegion, new OpModRegion); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/skyextensions.hpp000066400000000000000000000005431264522266000242000ustar00rootroot00000000000000#ifndef GAME_SCRIPT_SKYEXTENSIONS_H #define GAME_SCRIPT_SKYEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief sky-related script functionality namespace Sky { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/soundextensions.cpp000066400000000000000000000212111264522266000245100ustar00rootroot00000000000000#include "extensions.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace MWScript { namespace Sound { template class OpSay : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWScript::InterpreterContext& context = static_cast (runtime.getContext()); std::string file = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string text = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWBase::Environment::get().getSoundManager()->say (ptr, file); if (MWBase::Environment::get().getWindowManager ()->getSubtitlesEnabled()) context.messageBox (text); } }; template class OpSayDone : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push (MWBase::Environment::get().getSoundManager()->sayDone (ptr)); } }; class OpStreamMusic : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWBase::Environment::get().getSoundManager()->streamMusic (sound); } }; class OpPlaySound : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); } }; class OpPlaySoundVP : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float volume = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float pitch = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getSoundManager()->playSound (sound, volume, pitch); } }; template class OpPlaySound3D : public Interpreter::Opcode0 { bool mLoop; public: OpPlaySound3D (bool loop) : mLoop (loop) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWBase::Environment::get().getSoundManager()->playSound3D(ptr, sound, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, mLoop ? MWBase::SoundManager::Play_LoopRemoveAtDistance : MWBase::SoundManager::Play_Normal); } }; template class OpPlaySoundVP3D : public Interpreter::Opcode0 { bool mLoop; public: OpPlaySoundVP3D (bool loop) : mLoop (loop) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float volume = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float pitch = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getSoundManager()->playSound3D(ptr, sound, volume, pitch, MWBase::SoundManager::Play_TypeSfx, mLoop ? MWBase::SoundManager::Play_LoopRemoveAtDistance : MWBase::SoundManager::Play_Normal); } }; template class OpStopSound : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWBase::Environment::get().getSoundManager()->stopSound3D (ptr, sound); } }; template class OpGetSoundPlaying : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); int index = runtime[0].mInteger; runtime.pop(); runtime.push (MWBase::Environment::get().getSoundManager()->getSoundPlaying ( ptr, runtime.getStringLiteral (index))); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Sound::opcodeSay, new OpSay); interpreter.installSegment5 (Compiler::Sound::opcodeSayDone, new OpSayDone); interpreter.installSegment5 (Compiler::Sound::opcodeStreamMusic, new OpStreamMusic); interpreter.installSegment5 (Compiler::Sound::opcodePlaySound, new OpPlaySound); interpreter.installSegment5 (Compiler::Sound::opcodePlaySoundVP, new OpPlaySoundVP); interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3D, new OpPlaySound3D (false)); interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3DVP, new OpPlaySoundVP3D (false)); interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3D, new OpPlaySound3D (true)); interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3DVP, new OpPlaySoundVP3D (true)); interpreter.installSegment5 (Compiler::Sound::opcodeStopSound, new OpStopSound); interpreter.installSegment5 (Compiler::Sound::opcodeGetSoundPlaying, new OpGetSoundPlaying); interpreter.installSegment5 (Compiler::Sound::opcodeSayExplicit, new OpSay); interpreter.installSegment5 (Compiler::Sound::opcodeSayDoneExplicit, new OpSayDone); interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3DExplicit, new OpPlaySound3D (false)); interpreter.installSegment5 (Compiler::Sound::opcodePlaySound3DVPExplicit, new OpPlaySoundVP3D (false)); interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3DExplicit, new OpPlaySound3D (true)); interpreter.installSegment5 (Compiler::Sound::opcodePlayLoopSound3DVPExplicit, new OpPlaySoundVP3D (true)); interpreter.installSegment5 (Compiler::Sound::opcodeStopSoundExplicit, new OpStopSound); interpreter.installSegment5 (Compiler::Sound::opcodeGetSoundPlayingExplicit, new OpGetSoundPlaying); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/soundextensions.hpp000066400000000000000000000005501264522266000245200ustar00rootroot00000000000000#ifndef GAME_SCRIPT_SOUNDEXTENSIONS_H #define GAME_SCRIPT_SOUNDEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { namespace Sound { // Script-extensions related to sound void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/statsextensions.cpp000066400000000000000000001547271264522266000245410ustar00rootroot00000000000000#include "statsextensions.hpp" #include #include #include #include "../mwworld/esmstore.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace { std::string getDialogueActorFaction(MWWorld::ConstPtr actor) { std::string factionId = actor.getClass().getPrimaryFaction(actor); if (factionId.empty()) throw std::runtime_error ( "failed to determine dialogue actors faction (because actor is factionless)"); return factionId; } } namespace MWScript { namespace Stats { template class OpGetLevel : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass() .getCreatureStats (ptr) .getLevel(); runtime.push (value); } }; template class OpSetLevel : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); ptr.getClass() .getCreatureStats (ptr) .setLevel(value); } }; template class OpGetAttribute : public Interpreter::Opcode0 { int mIndex; public: OpGetAttribute (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass() .getCreatureStats (ptr) .getAttribute(mIndex) .getModified(); runtime.push (value); } }; template class OpSetAttribute : public Interpreter::Opcode0 { int mIndex; public: OpSetAttribute (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); attribute.setBase (value - (attribute.getModified() - attribute.getBase())); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; template class OpModAttribute : public Interpreter::Opcode0 { int mIndex; public: OpModAttribute (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass() .getCreatureStats(ptr) .getAttribute(mIndex); attribute.setBase (std::min(100, attribute.getBase() + value)); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; template class OpGetDynamic : public Interpreter::Opcode0 { int mIndex; public: OpGetDynamic (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value; if (mIndex==0 && ptr.getClass().hasItemHealth (ptr)) { // health is a special case value = static_cast(ptr.getClass().getItemMaxHealth(ptr)); } else { value = ptr.getClass() .getCreatureStats(ptr) .getDynamic(mIndex) .getCurrent(); } runtime.push (value); } }; template class OpSetDynamic : public Interpreter::Opcode0 { int mIndex; public: OpSetDynamic (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::DynamicStat stat (ptr.getClass().getCreatureStats (ptr) .getDynamic (mIndex)); stat.setModified (value, 0); stat.setCurrent(value); ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat); } }; template class OpModDynamic : public Interpreter::Opcode0 { int mIndex; public: OpModDynamic (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { int peek = R::implicit ? 0 : runtime[0].mInteger; MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float diff = runtime[0].mFloat; runtime.pop(); // workaround broken endgame scripts that kill dagoth ur if (!R::implicit && ::Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "dagoth_ur_1")) { runtime.push (peek); if (R()(runtime, false, true).isEmpty()) { std::cerr << "Compensating for broken script in Morrowind.esm by " << "ignoring remote access to dagoth_ur_1" << std::endl; return; } } MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); Interpreter::Type_Float current = stats.getDynamic(mIndex).getCurrent(); MWMechanics::DynamicStat stat (ptr.getClass().getCreatureStats (ptr) .getDynamic (mIndex)); stat.setModified (diff + stat.getModified(), 0); stat.setCurrent (diff + current); ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat); } }; template class OpModCurrentDynamic : public Interpreter::Opcode0 { int mIndex; public: OpModCurrentDynamic (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float diff = runtime[0].mFloat; runtime.pop(); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); Interpreter::Type_Float current = stats.getDynamic(mIndex).getCurrent(); MWMechanics::DynamicStat stat (ptr.getClass().getCreatureStats (ptr) .getDynamic (mIndex)); // for fatigue, a negative current value is allowed and means the actor will be knocked down bool allowDecreaseBelowZero = (mIndex == 2); stat.setCurrent (diff + current, allowDecreaseBelowZero); ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat); } }; template class OpGetDynamicGetRatio : public Interpreter::Opcode0 { int mIndex; public: OpGetDynamicGetRatio (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); Interpreter::Type_Float value = 0; Interpreter::Type_Float max = stats.getDynamic(mIndex).getModified(); if (max>0) value = stats.getDynamic(mIndex).getCurrent() / max; runtime.push (value); } }; template class OpGetSkill : public Interpreter::Opcode0 { int mIndex; public: OpGetSkill (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass().getSkill(ptr, mIndex); runtime.push (value); } }; template class OpSetSkill : public Interpreter::Opcode0 { int mIndex; public: OpSetSkill (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr); int newLevel = value - (stats.getSkill(mIndex).getModified() - stats.getSkill(mIndex).getBase()); if (newLevel<0) newLevel = 0; stats.getSkill (mIndex).setBase (newLevel); } }; template class OpModSkill : public Interpreter::Opcode0 { int mIndex; public: OpModSkill (int index) : mIndex (index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats(ptr); stats.getSkill(mIndex). setBase (std::min(100, stats.getSkill(mIndex).getBase() + value)); } }; class OpGetPCCrimeLevel : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); runtime.push (static_cast (player.getClass().getNpcStats (player).getBounty())); } }; class OpSetPCCrimeLevel : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); int bounty = static_cast(runtime[0].mFloat); runtime.pop(); player.getClass().getNpcStats (player).setBounty(bounty); if (bounty == 0) MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; class OpModPCCrimeLevel : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(static_cast(runtime[0].mFloat) + player.getClass().getNpcStats(player).getBounty()); runtime.pop(); } }; template class OpAddSpell : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); // make sure a spell with this ID actually exists. MWBase::Environment::get().getWorld()->getStore().get().find (id); ptr.getClass().getCreatureStats (ptr).getSpells().add (id); } }; template class OpRemoveSpell : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); ptr.getClass().getCreatureStats (ptr).getSpells().remove (id); MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); if (ptr == MWMechanics::getPlayer() && id == wm->getSelectedSpell()) { wm->unsetSelectedSpell(); } } }; template class OpRemoveSpellEffects : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string spellid = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); ptr.getClass().getCreatureStats (ptr).getActiveSpells().removeEffects(spellid); } }; template class OpRemoveEffects : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer effectId = runtime[0].mInteger; runtime.pop(); ptr.getClass().getCreatureStats (ptr).getActiveSpells().purgeEffect(effectId); } }; template class OpGetSpell : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer value = 0; if (ptr.getClass().getCreatureStats(ptr).getSpells().hasSpell(id)) value = 1; runtime.push (value); } }; template class OpPCJoinFaction : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr actor = R()(runtime, false); std::string factionID = ""; if(arg0==0) { factionID = getDialogueActorFaction(actor); } else { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); if(factionID != "") { MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats(player).joinFaction(factionID); } } }; template class OpPCRaiseRank : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr actor = R()(runtime, false); std::string factionID = ""; if(arg0==0) { factionID = getDialogueActorFaction(actor); } else { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); if(factionID != "") { MWWorld::Ptr player = MWMechanics::getPlayer(); if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) == player.getClass().getNpcStats(player).getFactionRanks().end()) { player.getClass().getNpcStats(player).joinFaction(factionID); } else { player.getClass().getNpcStats(player).raiseRank(factionID); } } } }; template class OpPCLowerRank : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr actor = R()(runtime, false); std::string factionID = ""; if(arg0==0) { factionID = getDialogueActorFaction(actor); } else { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); if(factionID != "") { MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats(player).lowerRank(factionID); } } }; template class OpGetPCRank : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0) { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionID = ptr.getClass().getPrimaryFaction(ptr); } ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); MWWorld::Ptr player = MWMechanics::getPlayer(); if(factionID!="") { if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) != player.getClass().getNpcStats(player).getFactionRanks().end()) { runtime.push(player.getClass().getNpcStats(player).getFactionRanks().at(factionID)); } else { runtime.push(-1); } } else { runtime.push(-1); } } }; template class OpModDisposition : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); if (ptr.getClass().isNpc()) ptr.getClass().getNpcStats (ptr).setBaseDisposition (ptr.getClass().getNpcStats (ptr).getBaseDisposition() + value); // else: must not throw exception (used by an Almalexia dialogue script) } }; template class OpSetDisposition : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); if (ptr.getClass().isNpc()) ptr.getClass().getNpcStats (ptr).setBaseDisposition (value); } }; template class OpGetDisposition : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); if (!ptr.getClass().isNpc()) runtime.push(0); else runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr)); } }; class OpGetDeadCount : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime[0].mInteger = MWBase::Environment::get().getMechanicsManager()->countDeaths (id); } }; template class OpGetPCFacRep : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionId; if (arg0==1) { factionId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionId = getDialogueActorFaction(ptr); } if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); ::Misc::StringUtils::lowerCaseInPlace (factionId); MWWorld::Ptr player = MWMechanics::getPlayer(); runtime.push ( player.getClass().getNpcStats (player).getFactionReputation (factionId)); } }; template class OpSetPCFacRep : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr ptr = R()(runtime, false); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); std::string factionId; if (arg0==1) { factionId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionId = getDialogueActorFaction(ptr); } if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); ::Misc::StringUtils::lowerCaseInPlace (factionId); MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats (player).setFactionReputation (factionId, value); } }; template class OpModPCFacRep : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr ptr = R()(runtime, false); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); std::string factionId; if (arg0==1) { factionId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionId = getDialogueActorFaction(ptr); } if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); ::Misc::StringUtils::lowerCaseInPlace (factionId); MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats (player).setFactionReputation (factionId, player.getClass().getNpcStats (player).getFactionReputation (factionId)+ value); } }; template class OpGetCommonDisease : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push (ptr.getClass().getCreatureStats (ptr).hasCommonDisease()); } }; template class OpGetBlightDisease : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push (ptr.getClass().getCreatureStats (ptr).hasBlightDisease()); } }; template class OpGetRace : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::ConstPtr ptr = R()(runtime); std::string race = runtime.getStringLiteral(runtime[0].mInteger); ::Misc::StringUtils::lowerCaseInPlace(race); runtime.pop(); std::string npcRace = ptr.get()->mBase->mRace; ::Misc::StringUtils::lowerCaseInPlace(npcRace); runtime.push (npcRace == race); } }; class OpGetWerewolfKills : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (ptr.getClass().getNpcStats (ptr).getWerewolfKills ()); } }; template class OpPcExpelled : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0 ) { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionID = ptr.getClass().getPrimaryFaction(ptr); } ::Misc::StringUtils::lowerCaseInPlace(factionID); MWWorld::Ptr player = MWMechanics::getPlayer(); if(factionID!="") { runtime.push(player.getClass().getNpcStats(player).getExpelled(factionID)); } else { runtime.push(0); } } }; template class OpPcExpell : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0 ) { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionID = ptr.getClass().getPrimaryFaction(ptr); } MWWorld::Ptr player = MWMechanics::getPlayer(); if(factionID!="") { player.getClass().getNpcStats(player).expell(factionID); } } }; template class OpPcClearExpelled : public Interpreter::Opcode1 { public: virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0 ) { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionID = ptr.getClass().getPrimaryFaction(ptr); } MWWorld::Ptr player = MWMechanics::getPlayer(); if(factionID!="") player.getClass().getNpcStats(player).clearExpelled(factionID); } }; template class OpRaiseRank : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string factionID = ptr.getClass().getPrimaryFaction(ptr); if(factionID.empty()) return; MWWorld::Ptr player = MWMechanics::getPlayer(); // no-op when executed on the player if (ptr == player) return; ptr.getClass().getNpcStats(ptr).raiseRank(factionID); } }; template class OpLowerRank : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string factionID = ptr.getClass().getPrimaryFaction(ptr); if(factionID.empty()) return; MWWorld::Ptr player = MWMechanics::getPlayer(); // no-op when executed on the player if (ptr == player) return; ptr.getClass().getNpcStats(ptr).lowerRank(factionID); } }; template class OpOnDeath : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).hasDied(); if (value) ptr.getClass().getCreatureStats (ptr).clearHasDied(); runtime.push (value); } }; template class OpOnMurder : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).hasBeenMurdered(); if (value) ptr.getClass().getCreatureStats (ptr).clearHasBeenMurdered(); runtime.push (value); } }; template class OpOnKnockout : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).getKnockedDownOneFrame(); runtime.push (value); } }; template class OpIsWerewolf : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push(ptr.getClass().getNpcStats(ptr).isWerewolf()); } }; template class OpSetWerewolf : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, set); } }; template class OpSetWerewolfAcrobatics : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); MWBase::Environment::get().getMechanicsManager()->applyWerewolfAcrobatics(ptr); } }; template class OpResurrect : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); if (ptr == MWMechanics::getPlayer()) ptr.getClass().getCreatureStats(ptr).resurrect(); else if (ptr.getClass().getCreatureStats(ptr).isDead()) { MWBase::Environment::get().getWorld()->undeleteObject(ptr); // resets runtime state such as inventory, stats and AI. does not reset position in the world MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); ptr.getRefData().setCustomData(NULL); } } }; template class OpGetStat : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { // dummy runtime.push(0); } }; template class OpGetMagicEffect : public Interpreter::Opcode0 { int mPositiveEffect; int mNegativeEffect; public: OpGetMagicEffect (int positiveEffect, int negativeEffect) : mPositiveEffect(positiveEffect) , mNegativeEffect(negativeEffect) { } virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); float currentValue = effects.get(mPositiveEffect).getMagnitude(); if (mNegativeEffect != -1) currentValue -= effects.get(mNegativeEffect).getMagnitude(); int ret = static_cast(currentValue); runtime.push(ret); } }; template class OpSetMagicEffect : public Interpreter::Opcode0 { int mPositiveEffect; int mNegativeEffect; public: OpSetMagicEffect (int positiveEffect, int negativeEffect) : mPositiveEffect(positiveEffect) , mNegativeEffect(negativeEffect) { } virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); float currentValue = effects.get(mPositiveEffect).getMagnitude(); if (mNegativeEffect != -1) currentValue -= effects.get(mNegativeEffect).getMagnitude(); int arg = runtime[0].mInteger; runtime.pop(); effects.modifyBase(mPositiveEffect, (arg - static_cast(currentValue))); } }; template class OpModMagicEffect : public Interpreter::Opcode0 { int mPositiveEffect; int mNegativeEffect; public: OpModMagicEffect (int positiveEffect, int negativeEffect) : mPositiveEffect(positiveEffect) , mNegativeEffect(negativeEffect) { } virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); int arg = runtime[0].mInteger; runtime.pop(); stats.getMagicEffects().modifyBase(mPositiveEffect, arg); } }; struct MagicEffect { int mPositiveEffect; int mNegativeEffect; }; void installOpcodes (Interpreter::Interpreter& interpreter) { for (int i=0; i (i)); interpreter.installSegment5 (Compiler::Stats::opcodeGetAttributeExplicit+i, new OpGetAttribute (i)); interpreter.installSegment5 (Compiler::Stats::opcodeSetAttribute+i, new OpSetAttribute (i)); interpreter.installSegment5 (Compiler::Stats::opcodeSetAttributeExplicit+i, new OpSetAttribute (i)); interpreter.installSegment5 (Compiler::Stats::opcodeModAttribute+i, new OpModAttribute (i)); interpreter.installSegment5 (Compiler::Stats::opcodeModAttributeExplicit+i, new OpModAttribute (i)); } for (int i=0; i (i)); interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamicExplicit+i, new OpGetDynamic (i)); interpreter.installSegment5 (Compiler::Stats::opcodeSetDynamic+i, new OpSetDynamic (i)); interpreter.installSegment5 (Compiler::Stats::opcodeSetDynamicExplicit+i, new OpSetDynamic (i)); interpreter.installSegment5 (Compiler::Stats::opcodeModDynamic+i, new OpModDynamic (i)); interpreter.installSegment5 (Compiler::Stats::opcodeModDynamicExplicit+i, new OpModDynamic (i)); interpreter.installSegment5 (Compiler::Stats::opcodeModCurrentDynamic+i, new OpModCurrentDynamic (i)); interpreter.installSegment5 (Compiler::Stats::opcodeModCurrentDynamicExplicit+i, new OpModCurrentDynamic (i)); interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamicGetRatio+i, new OpGetDynamicGetRatio (i)); interpreter.installSegment5 (Compiler::Stats::opcodeGetDynamicGetRatioExplicit+i, new OpGetDynamicGetRatio (i)); } for (int i=0; i (i)); interpreter.installSegment5 (Compiler::Stats::opcodeGetSkillExplicit+i, new OpGetSkill (i)); interpreter.installSegment5 (Compiler::Stats::opcodeSetSkill+i, new OpSetSkill (i)); interpreter.installSegment5 (Compiler::Stats::opcodeSetSkillExplicit+i, new OpSetSkill (i)); interpreter.installSegment5 (Compiler::Stats::opcodeModSkill+i, new OpModSkill (i)); interpreter.installSegment5 (Compiler::Stats::opcodeModSkillExplicit+i, new OpModSkill (i)); } interpreter.installSegment5 (Compiler::Stats::opcodeGetPCCrimeLevel, new OpGetPCCrimeLevel); interpreter.installSegment5 (Compiler::Stats::opcodeSetPCCrimeLevel, new OpSetPCCrimeLevel); interpreter.installSegment5 (Compiler::Stats::opcodeModPCCrimeLevel, new OpModPCCrimeLevel); interpreter.installSegment5 (Compiler::Stats::opcodeAddSpell, new OpAddSpell); interpreter.installSegment5 (Compiler::Stats::opcodeAddSpellExplicit, new OpAddSpell); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpell, new OpRemoveSpell); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellExplicit, new OpRemoveSpell); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffects, new OpRemoveSpellEffects); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffectsExplicit, new OpRemoveSpellEffects); interpreter.installSegment5 (Compiler::Stats::opcodeResurrect, new OpResurrect); interpreter.installSegment5 (Compiler::Stats::opcodeResurrectExplicit, new OpResurrect); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffects, new OpRemoveEffects); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffectsExplicit, new OpRemoveEffects); interpreter.installSegment5 (Compiler::Stats::opcodeGetSpell, new OpGetSpell); interpreter.installSegment5 (Compiler::Stats::opcodeGetSpellExplicit, new OpGetSpell); interpreter.installSegment3(Compiler::Stats::opcodePCRaiseRank,new OpPCRaiseRank); interpreter.installSegment3(Compiler::Stats::opcodePCLowerRank,new OpPCLowerRank); interpreter.installSegment3(Compiler::Stats::opcodePCJoinFaction,new OpPCJoinFaction); interpreter.installSegment3(Compiler::Stats::opcodePCRaiseRankExplicit,new OpPCRaiseRank); interpreter.installSegment3(Compiler::Stats::opcodePCLowerRankExplicit,new OpPCLowerRank); interpreter.installSegment3(Compiler::Stats::opcodePCJoinFactionExplicit,new OpPCJoinFaction); interpreter.installSegment3(Compiler::Stats::opcodeGetPCRank,new OpGetPCRank); interpreter.installSegment3(Compiler::Stats::opcodeGetPCRankExplicit,new OpGetPCRank); interpreter.installSegment5(Compiler::Stats::opcodeModDisposition,new OpModDisposition); interpreter.installSegment5(Compiler::Stats::opcodeModDispositionExplicit,new OpModDisposition); interpreter.installSegment5(Compiler::Stats::opcodeSetDisposition,new OpSetDisposition); interpreter.installSegment5(Compiler::Stats::opcodeSetDispositionExplicit,new OpSetDisposition); interpreter.installSegment5(Compiler::Stats::opcodeGetDisposition,new OpGetDisposition); interpreter.installSegment5(Compiler::Stats::opcodeGetDispositionExplicit,new OpGetDisposition); interpreter.installSegment5 (Compiler::Stats::opcodeGetLevel, new OpGetLevel); interpreter.installSegment5 (Compiler::Stats::opcodeGetLevelExplicit, new OpGetLevel); interpreter.installSegment5 (Compiler::Stats::opcodeSetLevel, new OpSetLevel); interpreter.installSegment5 (Compiler::Stats::opcodeSetLevelExplicit, new OpSetLevel); interpreter.installSegment5 (Compiler::Stats::opcodeGetDeadCount, new OpGetDeadCount); interpreter.installSegment3 (Compiler::Stats::opcodeGetPCFacRep, new OpGetPCFacRep); interpreter.installSegment3 (Compiler::Stats::opcodeGetPCFacRepExplicit, new OpGetPCFacRep); interpreter.installSegment3 (Compiler::Stats::opcodeSetPCFacRep, new OpSetPCFacRep); interpreter.installSegment3 (Compiler::Stats::opcodeSetPCFacRepExplicit, new OpSetPCFacRep); interpreter.installSegment3 (Compiler::Stats::opcodeModPCFacRep, new OpModPCFacRep); interpreter.installSegment3 (Compiler::Stats::opcodeModPCFacRepExplicit, new OpModPCFacRep); interpreter.installSegment5 (Compiler::Stats::opcodeGetCommonDisease, new OpGetCommonDisease); interpreter.installSegment5 (Compiler::Stats::opcodeGetCommonDiseaseExplicit, new OpGetCommonDisease); interpreter.installSegment5 (Compiler::Stats::opcodeGetBlightDisease, new OpGetBlightDisease); interpreter.installSegment5 (Compiler::Stats::opcodeGetBlightDiseaseExplicit, new OpGetBlightDisease); interpreter.installSegment5 (Compiler::Stats::opcodeGetRace, new OpGetRace); interpreter.installSegment5 (Compiler::Stats::opcodeGetRaceExplicit, new OpGetRace); interpreter.installSegment5 (Compiler::Stats::opcodeGetWerewolfKills, new OpGetWerewolfKills); interpreter.installSegment3 (Compiler::Stats::opcodePcExpelled, new OpPcExpelled); interpreter.installSegment3 (Compiler::Stats::opcodePcExpelledExplicit, new OpPcExpelled); interpreter.installSegment3 (Compiler::Stats::opcodePcExpell, new OpPcExpell); interpreter.installSegment3 (Compiler::Stats::opcodePcExpellExplicit, new OpPcExpell); interpreter.installSegment3 (Compiler::Stats::opcodePcClearExpelled, new OpPcClearExpelled); interpreter.installSegment3 (Compiler::Stats::opcodePcClearExpelledExplicit, new OpPcClearExpelled); interpreter.installSegment5 (Compiler::Stats::opcodeRaiseRank, new OpRaiseRank); interpreter.installSegment5 (Compiler::Stats::opcodeRaiseRankExplicit, new OpRaiseRank); interpreter.installSegment5 (Compiler::Stats::opcodeLowerRank, new OpLowerRank); interpreter.installSegment5 (Compiler::Stats::opcodeLowerRankExplicit, new OpLowerRank); interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath); interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath); interpreter.installSegment5 (Compiler::Stats::opcodeOnMurder, new OpOnMurder); interpreter.installSegment5 (Compiler::Stats::opcodeOnMurderExplicit, new OpOnMurder); interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockout, new OpOnKnockout); interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout); interpreter.installSegment5 (Compiler::Stats::opcodeIsWerewolf, new OpIsWerewolf); interpreter.installSegment5 (Compiler::Stats::opcodeIsWerewolfExplicit, new OpIsWerewolf); interpreter.installSegment5 (Compiler::Stats::opcodeBecomeWerewolf, new OpSetWerewolf); interpreter.installSegment5 (Compiler::Stats::opcodeBecomeWerewolfExplicit, new OpSetWerewolf); interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolf, new OpSetWerewolf); interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolfExplicit, new OpSetWerewolf); interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobatics, new OpSetWerewolfAcrobatics); interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobaticsExplicit, new OpSetWerewolfAcrobatics); interpreter.installSegment5 (Compiler::Stats::opcodeGetStat, new OpGetStat); interpreter.installSegment5 (Compiler::Stats::opcodeGetStatExplicit, new OpGetStat); static const MagicEffect sMagicEffects[] = { { ESM::MagicEffect::ResistMagicka, ESM::MagicEffect::WeaknessToMagicka }, { ESM::MagicEffect::ResistFire, ESM::MagicEffect::WeaknessToFire }, { ESM::MagicEffect::ResistFrost, ESM::MagicEffect::WeaknessToFrost }, { ESM::MagicEffect::ResistShock, ESM::MagicEffect::WeaknessToShock }, { ESM::MagicEffect::ResistCommonDisease, ESM::MagicEffect::WeaknessToCommonDisease }, { ESM::MagicEffect::ResistBlightDisease, ESM::MagicEffect::WeaknessToBlightDisease }, { ESM::MagicEffect::ResistCorprusDisease, ESM::MagicEffect::WeaknessToCorprusDisease }, { ESM::MagicEffect::ResistPoison, ESM::MagicEffect::WeaknessToPoison }, { ESM::MagicEffect::ResistParalysis, -1 }, { ESM::MagicEffect::ResistNormalWeapons, ESM::MagicEffect::WeaknessToNormalWeapons }, { ESM::MagicEffect::WaterBreathing, -1 }, { ESM::MagicEffect::Chameleon, -1 }, { ESM::MagicEffect::WaterWalking, -1 }, { ESM::MagicEffect::SwiftSwim, -1 }, { ESM::MagicEffect::Jump, -1 }, { ESM::MagicEffect::Levitate, -1 }, { ESM::MagicEffect::Shield, -1 }, { ESM::MagicEffect::Sound, -1 }, { ESM::MagicEffect::Silence, -1 }, { ESM::MagicEffect::Blind, -1 }, { ESM::MagicEffect::Paralyze, -1 }, { ESM::MagicEffect::Invisibility, -1 }, { ESM::MagicEffect::FortifyAttack, -1 }, { ESM::MagicEffect::Sanctuary, -1 }, }; for (int i=0; i<24; ++i) { int positive = sMagicEffects[i].mPositiveEffect; int negative = sMagicEffects[i].mNegativeEffect; interpreter.installSegment5 (Compiler::Stats::opcodeGetMagicEffect+i, new OpGetMagicEffect (positive, negative)); interpreter.installSegment5 (Compiler::Stats::opcodeGetMagicEffectExplicit+i, new OpGetMagicEffect (positive, negative)); interpreter.installSegment5 (Compiler::Stats::opcodeSetMagicEffect+i, new OpSetMagicEffect (positive, negative)); interpreter.installSegment5 (Compiler::Stats::opcodeSetMagicEffectExplicit+i, new OpSetMagicEffect (positive, negative)); interpreter.installSegment5 (Compiler::Stats::opcodeModMagicEffect+i, new OpModMagicEffect (positive, negative)); interpreter.installSegment5 (Compiler::Stats::opcodeModMagicEffectExplicit+i, new OpModMagicEffect (positive, negative)); } } } } openmw-openmw-0.38.0/apps/openmw/mwscript/statsextensions.hpp000066400000000000000000000006001264522266000245220ustar00rootroot00000000000000#ifndef GAME_SCRIPT_STATSEXTENSIONS_H #define GAME_SCRIPT_STATSEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief stats-related script functionality (creatures and NPCs) namespace Stats { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/transformationextensions.cpp000066400000000000000000001036621264522266000264410ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/player.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/actorutil.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace MWScript { namespace Transformation { template class OpSetScale : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float scale = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWorld()->scaleObject(ptr,scale); } }; template class OpGetScale : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); runtime.push(ptr.getCellRef().getScale()); } }; template class OpModScale : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float scale = runtime[0].mFloat; runtime.pop(); // add the parameter to the object's scale. MWBase::Environment::get().getWorld()->scaleObject(ptr,ptr.getCellRef().getScale() + scale); } }; template class OpSetAngle : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float angle = osg::DegreesToRadians(runtime[0].mFloat); runtime.pop(); float ax = ptr.getRefData().getPosition().rot[0]; float ay = ptr.getRefData().getPosition().rot[1]; float az = ptr.getRefData().getPosition().rot[2]; if (axis == "x") MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); else if (axis == "y") MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); else if (axis == "z") MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); else throw std::runtime_error ("invalid rotation axis: " + axis); } }; template class OpGetStartingAngle : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); if (axis == "x") { runtime.push(osg::RadiansToDegrees(ptr.getCellRef().getPosition().rot[0])); } else if (axis == "y") { runtime.push(osg::RadiansToDegrees(ptr.getCellRef().getPosition().rot[1])); } else if (axis == "z") { runtime.push(osg::RadiansToDegrees(ptr.getCellRef().getPosition().rot[2])); } else throw std::runtime_error ("invalid rotation axis: " + axis); } }; template class OpGetAngle : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); if (axis=="x") { runtime.push(osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0])); } else if (axis=="y") { runtime.push(osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1])); } else if (axis=="z") { runtime.push(osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[2])); } else throw std::runtime_error ("invalid rotation axis: " + axis); } }; template class OpGetPos : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); if(axis == "x") { runtime.push(ptr.getRefData().getPosition().pos[0]); } else if(axis == "y") { runtime.push(ptr.getRefData().getPosition().pos[1]); } else if(axis == "z") { runtime.push(ptr.getRefData().getPosition().pos[2]); } else throw std::runtime_error ("invalid axis: " + axis); } }; template class OpSetPos : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); if (!ptr.isInCell()) return; if (ptr == MWMechanics::getPlayer()) { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); } std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float pos = runtime[0].mFloat; runtime.pop(); float ax = ptr.getRefData().getPosition().pos[0]; float ay = ptr.getRefData().getPosition().pos[1]; float az = ptr.getRefData().getPosition().pos[2]; MWWorld::Ptr updated = ptr; if(axis == "x") { updated = MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az); } else if(axis == "y") { updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az); } else if(axis == "z") { updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos); } else throw std::runtime_error ("invalid axis: " + axis); dynamic_cast(runtime.getContext()).updatePtr(ptr,updated); } }; template class OpGetStartingPos : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); if(axis == "x") { runtime.push(ptr.getCellRef().getPosition().pos[0]); } else if(axis == "y") { runtime.push(ptr.getCellRef().getPosition().pos[1]); } else if(axis == "z") { runtime.push(ptr.getCellRef().getPosition().pos[2]); } else throw std::runtime_error ("invalid axis: " + axis); } }; template class OpPositionCell : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); if (ptr.getContainerStore()) return; if (ptr == MWMechanics::getPlayer()) { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); } Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float zRot = runtime[0].mFloat; runtime.pop(); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::CellStore* store = 0; try { store = MWBase::Environment::get().getWorld()->getInterior(cellID); } catch(std::exception&) { // cell not found, move to exterior instead (vanilla PositionCell compatibility) const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); int cx,cy; MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); if(!cell) { std::string error = "PositionCell: unknown interior cell (" + cellID + "), moving to exterior instead"; runtime.getContext().report (error); std::cerr << error << std::endl; } } if(store) { MWWorld::Ptr base = ptr; ptr = MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z); dynamic_cast(runtime.getContext()).updatePtr(base,ptr); float ax = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0]); float ay = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1]); // Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200) // except for when you position the player, then degrees must be used. // See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference. if(ptr != MWMechanics::getPlayer()) zRot = zRot/60.0f; MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,osg::DegreesToRadians(zRot)); ptr.getClass().adjustPosition(ptr, false); } } }; template class OpPosition : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); if (!ptr.isInCell()) return; if (ptr == MWMechanics::getPlayer()) { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); } Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float zRot = runtime[0].mFloat; runtime.pop(); int cx,cy; MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); // another morrowind oddity: player will be moved to the exterior cell at this location, // non-player actors will move within the cell they are in. MWWorld::Ptr base = ptr; if (ptr == MWMechanics::getPlayer()) { MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(cx,cy); ptr = MWBase::Environment::get().getWorld()->moveObject(ptr,cell,x,y,z); } else { ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z); } dynamic_cast(runtime.getContext()).updatePtr(base,ptr); float ax = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0]); float ay = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1]); // Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200) // except for when you position the player, then degrees must be used. // See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference. if(ptr != MWMechanics::getPlayer()) zRot = zRot/60.0f; MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); ptr.getClass().adjustPosition(ptr, false); } }; template class OpPlaceItemCell : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float zRotDegrees = runtime[0].mFloat; runtime.pop(); MWWorld::CellStore* store = 0; try { store = MWBase::Environment::get().getWorld()->getInterior(cellID); } catch(std::exception&) { const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); int cx,cy; MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); if(!cell) { runtime.getContext().report ("unknown cell (" + cellID + ")"); std::cerr << "unknown cell (" << cellID << ")\n"; } } if(store) { ESM::Position pos; pos.pos[0] = x; pos.pos[1] = y; pos.pos[2] = z; pos.rot[0] = pos.rot[1] = 0; pos.rot[2] = osg::DegreesToRadians(zRotDegrees); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().setPosition(pos); MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); placed.getClass().adjustPosition(placed, true); } } }; template class OpPlaceItem : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float y = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float z = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float zRotDegrees = runtime[0].mFloat; runtime.pop(); MWWorld::Ptr player = MWMechanics::getPlayer(); if (!player.isInCell()) throw std::runtime_error("player not in a cell"); MWWorld::CellStore* store = NULL; if (player.getCell()->isExterior()) { int cx,cy; MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); } else store = player.getCell(); ESM::Position pos; pos.pos[0] = x; pos.pos[1] = y; pos.pos[2] = z; pos.rot[0] = pos.rot[1] = 0; pos.rot[2] = osg::DegreesToRadians(zRotDegrees); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().setPosition(pos); MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); placed.getClass().adjustPosition(placed, true); } }; template class OpPlaceAt : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr actor = pc ? MWMechanics::getPlayer() : R()(runtime); std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer count = runtime[0].mInteger; runtime.pop(); Interpreter::Type_Float distance = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Integer direction = runtime[0].mInteger; runtime.pop(); if (count<0) throw std::runtime_error ("count must be non-negative"); if (!actor.isInCell()) throw std::runtime_error ("actor is not in a cell"); for (int i=0; igetStore(), itemID, 1); ref.getPtr().getCellRef().setPosition(ipos); MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); } } }; template class OpRotate : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { const MWWorld::Ptr& ptr = R()(runtime); std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float rotation = osg::DegreesToRadians(runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); runtime.pop(); float ax = ptr.getRefData().getPosition().rot[0]; float ay = ptr.getRefData().getPosition().rot[1]; float az = ptr.getRefData().getPosition().rot[2]; if (axis == "x") MWBase::Environment::get().getWorld()->rotateObject(ptr,ax+rotation,ay,az); else if (axis == "y") MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay+rotation,az); else if (axis == "z") MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,az+rotation); else throw std::runtime_error ("invalid rotation axis: " + axis); } }; template class OpRotateWorld : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float rotation = osg::DegreesToRadians(runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); runtime.pop(); const float *objRot = ptr.getRefData().getPosition().rot; float ax = objRot[0]; float ay = objRot[1]; float az = objRot[2]; if (axis == "x") { MWBase::Environment::get().getWorld()->rotateObject(ptr,ax+rotation,ay,az); } else if (axis == "y") { MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay+rotation,az); } else if (axis == "z") { MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,az+rotation); } else throw std::runtime_error ("invalid rotation axis: " + axis); } }; template class OpSetAtStart : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); if (!ptr.isInCell()) return; float xr = ptr.getCellRef().getPosition().rot[0]; float yr = ptr.getCellRef().getPosition().rot[1]; float zr = ptr.getCellRef().getPosition().rot[2]; MWBase::Environment::get().getWorld()->rotateObject(ptr, xr, yr, zr); dynamic_cast(runtime.getContext()).updatePtr(ptr, MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], ptr.getCellRef().getPosition().pos[1], ptr.getCellRef().getPosition().pos[2])); } }; template class OpMove : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { const MWWorld::Ptr& ptr = R()(runtime); if (!ptr.isInCell()) return; std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); runtime.pop(); osg::Vec3f posChange; if (axis == "x") { posChange=osg::Vec3f(movement, 0, 0); } else if (axis == "y") { posChange=osg::Vec3f(0, movement, 0); } else if (axis == "z") { posChange=osg::Vec3f(0, 0, movement); } else throw std::runtime_error ("invalid movement axis: " + axis); // is it correct that disabled objects can't be Move-d? if (!ptr.getRefData().getBaseNode()) return; osg::Vec3f diff = ptr.getRefData().getBaseNode()->getAttitude() * posChange; osg::Vec3f worldPos(ptr.getRefData().getPosition().asVec3()); worldPos += diff; MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z()); } }; template class OpMoveWorld : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); if (!ptr.isInCell()) return; std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); runtime.pop(); const float *objPos = ptr.getRefData().getPosition().pos; MWWorld::Ptr updated; if (axis == "x") { updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]); } else if (axis == "y") { updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]); } else if (axis == "z") { updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement); } else throw std::runtime_error ("invalid movement axis: " + axis); } }; class OpResetActors : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { MWBase::Environment::get().getWorld()->resetActors(); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5(Compiler::Transformation::opcodeSetScale,new OpSetScale); interpreter.installSegment5(Compiler::Transformation::opcodeSetScaleExplicit,new OpSetScale); interpreter.installSegment5(Compiler::Transformation::opcodeSetAngle,new OpSetAngle); interpreter.installSegment5(Compiler::Transformation::opcodeSetAngleExplicit,new OpSetAngle); interpreter.installSegment5(Compiler::Transformation::opcodeGetScale,new OpGetScale); interpreter.installSegment5(Compiler::Transformation::opcodeGetScaleExplicit,new OpGetScale); interpreter.installSegment5(Compiler::Transformation::opcodeGetAngle,new OpGetAngle); interpreter.installSegment5(Compiler::Transformation::opcodeGetAngleExplicit,new OpGetAngle); interpreter.installSegment5(Compiler::Transformation::opcodeGetPos,new OpGetPos); interpreter.installSegment5(Compiler::Transformation::opcodeGetPosExplicit,new OpGetPos); interpreter.installSegment5(Compiler::Transformation::opcodeSetPos,new OpSetPos); interpreter.installSegment5(Compiler::Transformation::opcodeSetPosExplicit,new OpSetPos); interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingPos,new OpGetStartingPos); interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingPosExplicit,new OpGetStartingPos); interpreter.installSegment5(Compiler::Transformation::opcodePosition,new OpPosition); interpreter.installSegment5(Compiler::Transformation::opcodePositionExplicit,new OpPosition); interpreter.installSegment5(Compiler::Transformation::opcodePositionCell,new OpPositionCell); interpreter.installSegment5(Compiler::Transformation::opcodePositionCellExplicit,new OpPositionCell); interpreter.installSegment5(Compiler::Transformation::opcodePlaceItemCell,new OpPlaceItemCell); interpreter.installSegment5(Compiler::Transformation::opcodePlaceItem,new OpPlaceItem); interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtPc,new OpPlaceAt); interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMe,new OpPlaceAt); interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMeExplicit,new OpPlaceAt); interpreter.installSegment5(Compiler::Transformation::opcodeModScale,new OpModScale); interpreter.installSegment5(Compiler::Transformation::opcodeModScaleExplicit,new OpModScale); interpreter.installSegment5(Compiler::Transformation::opcodeRotate,new OpRotate); interpreter.installSegment5(Compiler::Transformation::opcodeRotateExplicit,new OpRotate); interpreter.installSegment5(Compiler::Transformation::opcodeRotateWorld,new OpRotateWorld); interpreter.installSegment5(Compiler::Transformation::opcodeRotateWorldExplicit,new OpRotateWorld); interpreter.installSegment5(Compiler::Transformation::opcodeSetAtStart,new OpSetAtStart); interpreter.installSegment5(Compiler::Transformation::opcodeSetAtStartExplicit,new OpSetAtStart); interpreter.installSegment5(Compiler::Transformation::opcodeMove,new OpMove); interpreter.installSegment5(Compiler::Transformation::opcodeMoveExplicit,new OpMove); interpreter.installSegment5(Compiler::Transformation::opcodeMoveWorld,new OpMoveWorld); interpreter.installSegment5(Compiler::Transformation::opcodeMoveWorldExplicit,new OpMoveWorld); interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngle, new OpGetStartingAngle); interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngleExplicit, new OpGetStartingAngle); interpreter.installSegment5(Compiler::Transformation::opcodeResetActors, new OpResetActors); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/transformationextensions.hpp000066400000000000000000000006331264522266000264400ustar00rootroot00000000000000#ifndef GAME_SCRIPT_TRANSFORMATIONEXTENSIONS_H #define GAME_SCRIPT_TRANSFORMATIONEXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief stats-related script functionality (creatures and NPCs) namespace Transformation { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwscript/userextensions.cpp000066400000000000000000000045651264522266000243530ustar00rootroot00000000000000#include "userextensions.hpp" #include #include #include #include #include #include #include "ref.hpp" namespace MWScript { /// Temporary script extensions. /// /// \attention Do not commit changes to this file to a git repository! namespace User { class OpUser1 : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.getContext().report ("user1: not in use"); } }; class OpUser2 : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { runtime.getContext().report ("user2: not in use"); } }; template class OpUser3 : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { // MWWorld::Ptr ptr = R()(runtime); runtime.getContext().report ("user3: not in use"); } }; template class OpUser4 : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { // MWWorld::Ptr ptr = R()(runtime); runtime.getContext().report ("user4: not in use"); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::User::opcodeUser1, new OpUser1); interpreter.installSegment5 (Compiler::User::opcodeUser2, new OpUser2); interpreter.installSegment5 (Compiler::User::opcodeUser3, new OpUser3); interpreter.installSegment5 (Compiler::User::opcodeUser3Explicit, new OpUser3); interpreter.installSegment5 (Compiler::User::opcodeUser4, new OpUser4); interpreter.installSegment5 (Compiler::User::opcodeUser4Explicit, new OpUser4); } } } openmw-openmw-0.38.0/apps/openmw/mwscript/userextensions.hpp000066400000000000000000000005631264522266000243520ustar00rootroot00000000000000#ifndef GAME_SCRIPT_USEREXTENSIONS_H #define GAME_SCRIPT_USEREXTENSIONS_H namespace Compiler { class Extensions; } namespace Interpreter { class Interpreter; } namespace MWScript { /// \brief Temporary script functionality limited to the console namespace User { void installOpcodes (Interpreter::Interpreter& interpreter); } } #endif openmw-openmw-0.38.0/apps/openmw/mwsound/000077500000000000000000000000001264522266000203635ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwsound/ffmpeg_decoder.cpp000066400000000000000000000316421264522266000240260ustar00rootroot00000000000000#include "ffmpeg_decoder.hpp" // auto_ptr #include #include #include #include namespace MWSound { void FFmpeg_Decoder::fail(const std::string &msg) { throw std::runtime_error("FFmpeg exception: "+msg); } int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) { try { std::istream& stream = *static_cast(user_data)->mDataStream; stream.clear(); stream.read((char*)buf, buf_size); return stream.gcount(); } catch (std::exception& ) { return 0; } } int FFmpeg_Decoder::writePacket(void *, uint8_t *, int) { throw std::runtime_error("can't write to read-only stream"); } int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence) { std::istream& stream = *static_cast(user_data)->mDataStream; whence &= ~AVSEEK_FORCE; stream.clear(); if(whence == AVSEEK_SIZE) { size_t prev = stream.tellg(); stream.seekg(0, std::ios_base::end); size_t size = stream.tellg(); stream.seekg(prev, std::ios_base::beg); return size; } if(whence == SEEK_SET) stream.seekg(offset, std::ios_base::beg); else if(whence == SEEK_CUR) stream.seekg(offset, std::ios_base::cur); else if(whence == SEEK_END) stream.seekg(offset, std::ios_base::end); else return -1; return stream.tellg(); } /* Used by getAV*Data to search for more compressed data, and buffer it in the * correct stream. It won't buffer data for streams that the app doesn't have a * handle for. */ bool FFmpeg_Decoder::getNextPacket() { if(!mStream) return false; int stream_idx = mStream - mFormatCtx->streams; while(av_read_frame(mFormatCtx, &mPacket) >= 0) { /* Check if the packet belongs to this stream */ if(stream_idx == mPacket.stream_index) { if(mPacket.pts != (int64_t)AV_NOPTS_VALUE) mNextPts = av_q2d((*mStream)->time_base)*mPacket.pts; return true; } /* Free the packet and look for another */ av_free_packet(&mPacket); } return false; } bool FFmpeg_Decoder::getAVAudioData() { int got_frame; if((*mStream)->codec->codec_type != AVMEDIA_TYPE_AUDIO) return false; do { if(mPacket.size == 0 && !getNextPacket()) return false; /* Decode some data, and check for errors */ int len = 0; if((len=avcodec_decode_audio4((*mStream)->codec, mFrame, &got_frame, &mPacket)) < 0) return false; /* Move the unread data to the front and clear the end bits */ int remaining = mPacket.size - len; if(remaining <= 0) av_free_packet(&mPacket); else { memmove(mPacket.data, &mPacket.data[len], remaining); av_shrink_packet(&mPacket, remaining); } if (!got_frame || mFrame->nb_samples == 0) continue; if(mSwr) { if(!mDataBuf || mDataBufLen < mFrame->nb_samples) { av_freep(&mDataBuf); if(av_samples_alloc(&mDataBuf, NULL, av_get_channel_layout_nb_channels(mOutputChannelLayout), mFrame->nb_samples, mOutputSampleFormat, 0) < 0) return false; else mDataBufLen = mFrame->nb_samples; } if(swr_convert(mSwr, (uint8_t**)&mDataBuf, mFrame->nb_samples, (const uint8_t**)mFrame->extended_data, mFrame->nb_samples) < 0) { return false; } mFrameData = &mDataBuf; } else mFrameData = &mFrame->data[0]; } while(got_frame == 0 || mFrame->nb_samples == 0); mNextPts += (double)mFrame->nb_samples / (double)(*mStream)->codec->sample_rate; return true; } size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length) { size_t dec = 0; while(dec < length) { /* If there's no decoded data, find some */ if(mFramePos >= mFrameSize) { if(!getAVAudioData()) break; mFramePos = 0; mFrameSize = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) * av_get_bytes_per_sample(mOutputSampleFormat); } /* Get the amount of bytes remaining to be written, and clamp to * the amount of decoded data we have */ size_t rem = std::min(length-dec, mFrameSize-mFramePos); /* Copy the data to the app's buffer and increment */ memcpy(data, mFrameData[0]+mFramePos, rem); data = (char*)data + rem; dec += rem; mFramePos += rem; } /* Return the number of bytes we were able to get */ return dec; } void FFmpeg_Decoder::open(const std::string &fname) { close(); mDataStream = mResourceMgr->get(fname); if((mFormatCtx=avformat_alloc_context()) == NULL) fail("Failed to allocate context"); mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek); if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0) { // "Note that a user-supplied AVFormatContext will be freed on failure". if (mFormatCtx) { if (mFormatCtx->pb != NULL) { if (mFormatCtx->pb->buffer != NULL) { av_free(mFormatCtx->pb->buffer); mFormatCtx->pb->buffer = NULL; } av_free(mFormatCtx->pb); mFormatCtx->pb = NULL; } avformat_free_context(mFormatCtx); } mFormatCtx = NULL; fail("Failed to allocate input stream"); } try { if(avformat_find_stream_info(mFormatCtx, NULL) < 0) fail("Failed to find stream info in "+fname); for(size_t j = 0;j < mFormatCtx->nb_streams;j++) { if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { mStream = &mFormatCtx->streams[j]; break; } } if(!mStream) fail("No audio streams in "+fname); (*mStream)->codec->request_sample_fmt = (*mStream)->codec->sample_fmt; AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id); if(!codec) { std::stringstream ss("No codec found for id "); ss << (*mStream)->codec->codec_id; fail(ss.str()); } if(avcodec_open2((*mStream)->codec, codec, NULL) < 0) fail("Failed to open audio codec " + std::string(codec->long_name)); mFrame = av_frame_alloc(); } catch(std::exception&) { if (mFormatCtx->pb->buffer != NULL) { av_free(mFormatCtx->pb->buffer); mFormatCtx->pb->buffer = NULL; } av_free(mFormatCtx->pb); mFormatCtx->pb = NULL; avformat_close_input(&mFormatCtx); throw; } } void FFmpeg_Decoder::close() { if(mStream) avcodec_close((*mStream)->codec); mStream = NULL; av_free_packet(&mPacket); av_freep(&mFrame); swr_free(&mSwr); av_freep(&mDataBuf); if(mFormatCtx) { if (mFormatCtx->pb != NULL) { // mFormatCtx->pb->buffer must be freed by hand, // if not, valgrind will show memleak, see: // // https://trac.ffmpeg.org/ticket/1357 // if (mFormatCtx->pb->buffer != NULL) { av_free(mFormatCtx->pb->buffer); mFormatCtx->pb->buffer = NULL; } av_free(mFormatCtx->pb); mFormatCtx->pb = NULL; } avformat_close_input(&mFormatCtx); } mDataStream.reset(); } std::string FFmpeg_Decoder::getName() { return mFormatCtx->filename; } void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { if(!mStream) fail("No audio stream info"); if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT || (*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP) mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P) mOutputSampleFormat = AV_SAMPLE_FMT_U8; else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P) mOutputSampleFormat = AV_SAMPLE_FMT_S16; else mOutputSampleFormat = AV_SAMPLE_FMT_S16; if(mOutputSampleFormat == AV_SAMPLE_FMT_U8) *type = SampleType_UInt8; else if(mOutputSampleFormat == AV_SAMPLE_FMT_S16) *type = SampleType_Int16; else if(mOutputSampleFormat == AV_SAMPLE_FMT_FLT) *type = SampleType_Float32; int64_t ch_layout = (*mStream)->codec->channel_layout; if(ch_layout == 0) ch_layout = av_get_default_channel_layout((*mStream)->codec->channels); mOutputChannelLayout = ch_layout; if (ch_layout == AV_CH_LAYOUT_5POINT1 || ch_layout == AV_CH_LAYOUT_7POINT1 || ch_layout == AV_CH_LAYOUT_QUAD) // FIXME: check for AL_EXT_MCFORMATS support mOutputChannelLayout = AV_CH_LAYOUT_STEREO; else if (ch_layout != AV_CH_LAYOUT_MONO && ch_layout != AV_CH_LAYOUT_STEREO) mOutputChannelLayout = AV_CH_LAYOUT_STEREO; if(mOutputChannelLayout == AV_CH_LAYOUT_MONO) *chans = ChannelConfig_Mono; else if(mOutputChannelLayout == AV_CH_LAYOUT_STEREO) *chans = ChannelConfig_Stereo; else if(mOutputChannelLayout == AV_CH_LAYOUT_QUAD) *chans = ChannelConfig_Quad; else if(mOutputChannelLayout == AV_CH_LAYOUT_5POINT1) *chans = ChannelConfig_5point1; else if(mOutputChannelLayout == AV_CH_LAYOUT_7POINT1) *chans = ChannelConfig_7point1; else { char str[1024]; av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels, (*mStream)->codec->channel_layout); fail(std::string("Unsupported channel layout: ")+str); } *samplerate = (*mStream)->codec->sample_rate; if(mOutputSampleFormat != (*mStream)->codec->sample_fmt || mOutputChannelLayout != ch_layout) { mSwr = swr_alloc_set_opts(mSwr, // SwrContext mOutputChannelLayout, // output ch layout mOutputSampleFormat, // output sample format (*mStream)->codec->sample_rate, // output sample rate ch_layout, // input ch layout (*mStream)->codec->sample_fmt, // input sample format (*mStream)->codec->sample_rate, // input sample rate 0, // logging level offset NULL); // log context if(!mSwr) fail(std::string("Couldn't allocate SwrContext")); if(swr_init(mSwr) < 0) fail(std::string("Couldn't initialize SwrContext")); } } size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) { if(!mStream) fail("No audio stream"); return readAVAudioData(buffer, bytes); } void FFmpeg_Decoder::readAll(std::vector &output) { if(!mStream) fail("No audio stream"); while(getAVAudioData()) { size_t got = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) * av_get_bytes_per_sample(mOutputSampleFormat); const char *inbuf = reinterpret_cast(mFrameData[0]); output.insert(output.end(), inbuf, inbuf+got); } } void FFmpeg_Decoder::rewind() { int stream_idx = mStream - mFormatCtx->streams; if(av_seek_frame(mFormatCtx, stream_idx, 0, 0) < 0) fail("Failed to seek in audio stream"); av_free_packet(&mPacket); mFrameSize = mFramePos = 0; mNextPts = 0.0; } size_t FFmpeg_Decoder::getSampleOffset() { int delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) / av_get_bytes_per_sample(mOutputSampleFormat); return (int)(mNextPts*(*mStream)->codec->sample_rate) - delay; } FFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs) : Sound_Decoder(vfs) , mFormatCtx(NULL) , mStream(NULL) , mFrame(NULL) , mFrameSize(0) , mFramePos(0) , mNextPts(0.0) , mSwr(0) , mOutputSampleFormat(AV_SAMPLE_FMT_NONE) , mOutputChannelLayout(0) , mDataBuf(NULL) , mFrameData(NULL) , mDataBufLen(0) { memset(&mPacket, 0, sizeof(mPacket)); /* We need to make sure ffmpeg is initialized. Optionally silence warning * output from the lib */ static bool done_init = false; if(!done_init) { av_register_all(); av_log_set_level(AV_LOG_ERROR); done_init = true; } } FFmpeg_Decoder::~FFmpeg_Decoder() { close(); } } openmw-openmw-0.38.0/apps/openmw/mwsound/ffmpeg_decoder.hpp000066400000000000000000000051311264522266000240250ustar00rootroot00000000000000#ifndef GAME_SOUND_FFMPEG_DECODER_H #define GAME_SOUND_FFMPEG_DECODER_H #include extern "C" { #include #include // From libavutil version 52.2.0 and onward the declaration of // AV_CH_LAYOUT_* is removed from libavcodec/avcodec.h and moved to // libavutil/channel_layout.h #if AV_VERSION_INT(52, 2, 0) <= AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO) #include #endif #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) #define av_frame_alloc avcodec_alloc_frame #endif // From version 54.56 binkaudio encoding format changed from S16 to FLTP. See: // https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d // http://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872 #include } #include #include #include #include "sound_decoder.hpp" namespace MWSound { class FFmpeg_Decoder : public Sound_Decoder { AVFormatContext *mFormatCtx; AVStream **mStream; AVPacket mPacket; AVFrame *mFrame; int mFrameSize; int mFramePos; double mNextPts; SwrContext *mSwr; enum AVSampleFormat mOutputSampleFormat; int64_t mOutputChannelLayout; uint8_t *mDataBuf; uint8_t **mFrameData; int mDataBufLen; bool getNextPacket(); Files::IStreamPtr mDataStream; static int readPacket(void *user_data, uint8_t *buf, int buf_size); static int writePacket(void *user_data, uint8_t *buf, int buf_size); static int64_t seek(void *user_data, int64_t offset, int whence); bool getAVAudioData(); size_t readAVAudioData(void *data, size_t length); virtual void open(const std::string &fname); virtual void close(); virtual std::string getName(); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual size_t read(char *buffer, size_t bytes); virtual void readAll(std::vector &output); virtual void rewind(); virtual size_t getSampleOffset(); void fail(const std::string &msg); FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const VFS::Manager* vfs); public: virtual ~FFmpeg_Decoder(); friend class SoundManager; }; #ifndef DEFAULT_DECODER #define DEFAULT_DECODER (::MWSound::FFmpeg_Decoder) #endif } #endif openmw-openmw-0.38.0/apps/openmw/mwsound/loudness.cpp000066400000000000000000000040601264522266000227230ustar00rootroot00000000000000#include "loudness.hpp" #include #include #include "soundmanagerimp.hpp" namespace MWSound { void Sound_Loudness::analyzeLoudness(const std::vector< char >& data, int sampleRate, ChannelConfig chans, SampleType type, float valuesPerSecond) { int samplesPerSegment = static_cast(sampleRate / valuesPerSecond); int numSamples = bytesToFrames(data.size(), chans, type); int advance = framesToBytes(1, chans, type); mSamplesPerSec = valuesPerSecond; mSamples.clear(); mSamples.reserve(numSamples/samplesPerSegment); int segment=0; int sample=0; while (segment < numSamples/samplesPerSegment) { float sum=0; int samplesAdded = 0; while (sample < numSamples && sample < (segment+1)*samplesPerSegment) { // get sample on a scale from -1 to 1 float value = 0; if (type == SampleType_UInt8) value = ((char)(data[sample*advance]^0x80))/128.f; else if (type == SampleType_Int16) { value = *reinterpret_cast(&data[sample*advance]); value /= float(std::numeric_limits::max()); } else if (type == SampleType_Float32) { value = *reinterpret_cast(&data[sample*advance]); value = std::max(-1.f, std::min(1.f, value)); // Float samples *should* be scaled to [-1,1] already. } sum += value*value; ++samplesAdded; ++sample; } float rms = 0; // root mean square if (samplesAdded > 0) rms = std::sqrt(sum / samplesAdded); mSamples.push_back(rms); ++segment; } mReady = true; } float Sound_Loudness::getLoudnessAtTime(float sec) const { if(mSamplesPerSec <= 0.0f || mSamples.empty() || sec < 0.0f) return 0.0f; size_t index = static_cast(sec * mSamplesPerSec); index = std::max(0, std::min(index, mSamples.size()-1)); return mSamples[index]; } } openmw-openmw-0.38.0/apps/openmw/mwsound/loudness.hpp000066400000000000000000000023061264522266000227310ustar00rootroot00000000000000#ifndef GAME_SOUND_LOUDNESS_H #define GAME_SOUND_LOUDNESS_H #include #include "sound_decoder.hpp" namespace MWSound { class Sound_Loudness { // Loudness sample info float mSamplesPerSec; std::vector mSamples; volatile bool mReady; public: Sound_Loudness() : mSamplesPerSec(0.0f), mReady(false) { } /** * Analyzes the energy (closely related to loudness) of a sound buffer. * The buffer will be divided into segments according to \a valuesPerSecond, * and for each segment a loudness value in the range of [0,1] will be computed. * @param data the sound buffer to analyze, containing raw samples * @param sampleRate the sample rate of the sound buffer * @param chans channel layout of the buffer * @param type sample type of the buffer * @param valuesPerSecond How many loudness values per second of audio to compute. */ void analyzeLoudness(const std::vector& data, int sampleRate, ChannelConfig chans, SampleType type, float valuesPerSecond); bool isReady() { return mReady; } float getLoudnessAtTime(float sec) const; }; } #endif /* GAME_SOUND_LOUDNESS_H */ openmw-openmw-0.38.0/apps/openmw/mwsound/movieaudiofactory.cpp000066400000000000000000000137751264522266000246350ustar00rootroot00000000000000#include "movieaudiofactory.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "sound_decoder.hpp" #include "sound.hpp" namespace MWSound { class MovieAudioDecoder; class MWSoundDecoderBridge : public Sound_Decoder { public: MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder) : Sound_Decoder(NULL) , mDecoder(decoder) { } private: MWSound::MovieAudioDecoder* mDecoder; virtual void open(const std::string &fname); virtual void close(); virtual void rewind(); virtual std::string getName(); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual size_t read(char *buffer, size_t bytes); virtual size_t getSampleOffset(); }; class MovieAudioDecoder : public Video::MovieAudioDecoder { public: MovieAudioDecoder(Video::VideoState *videoState) : Video::MovieAudioDecoder(videoState) { mDecoderBridge.reset(new MWSoundDecoderBridge(this)); } size_t getSampleOffset() { ssize_t clock_delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) / av_get_bytes_per_sample(mOutputSampleFormat); return (size_t)(mAudioClock*mAVStream->codec->sample_rate) - clock_delay; } std::string getStreamName() { return std::string(); } private: // MovieAudioDecoder overrides virtual double getAudioClock() { return (double)getSampleOffset()/(double)mAVStream->codec->sample_rate - MWBase::Environment::get().getSoundManager()->getTrackTimeDelay(mAudioTrack); } virtual void adjustAudioSettings(AVSampleFormat& sampleFormat, uint64_t& channelLayout, int& sampleRate) { if (sampleFormat == AV_SAMPLE_FMT_U8P || sampleFormat == AV_SAMPLE_FMT_U8) sampleFormat = AV_SAMPLE_FMT_U8; else if (sampleFormat == AV_SAMPLE_FMT_S16P || sampleFormat == AV_SAMPLE_FMT_S16) sampleFormat = AV_SAMPLE_FMT_S16; else if (sampleFormat == AV_SAMPLE_FMT_FLTP || sampleFormat == AV_SAMPLE_FMT_FLT) sampleFormat = AV_SAMPLE_FMT_S16; // FIXME: check for AL_EXT_FLOAT32 support else sampleFormat = AV_SAMPLE_FMT_S16; if (channelLayout == AV_CH_LAYOUT_5POINT1 || channelLayout == AV_CH_LAYOUT_7POINT1 || channelLayout == AV_CH_LAYOUT_QUAD) // FIXME: check for AL_EXT_MCFORMATS support channelLayout = AV_CH_LAYOUT_STEREO; else if (channelLayout != AV_CH_LAYOUT_MONO && channelLayout != AV_CH_LAYOUT_STEREO) channelLayout = AV_CH_LAYOUT_STEREO; } public: ~MovieAudioDecoder() { if(mAudioTrack.get()) MWBase::Environment::get().getSoundManager()->stopTrack(mAudioTrack); mAudioTrack.reset(); mDecoderBridge.reset(); } MWBase::SoundStreamPtr mAudioTrack; boost::shared_ptr mDecoderBridge; }; void MWSoundDecoderBridge::open(const std::string &fname) { throw std::runtime_error("unimplemented"); } void MWSoundDecoderBridge::close() {} void MWSoundDecoderBridge::rewind() {} std::string MWSoundDecoderBridge::getName() { return mDecoder->getStreamName(); } void MWSoundDecoderBridge::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { *samplerate = mDecoder->getOutputSampleRate(); uint64_t outputChannelLayout = mDecoder->getOutputChannelLayout(); if (outputChannelLayout == AV_CH_LAYOUT_MONO) *chans = ChannelConfig_Mono; else if (outputChannelLayout == AV_CH_LAYOUT_5POINT1) *chans = ChannelConfig_5point1; else if (outputChannelLayout == AV_CH_LAYOUT_7POINT1) *chans = ChannelConfig_7point1; else if (outputChannelLayout == AV_CH_LAYOUT_STEREO) *chans = ChannelConfig_Stereo; else if (outputChannelLayout == AV_CH_LAYOUT_QUAD) *chans = ChannelConfig_Quad; else { std::stringstream error; error << "Unsupported channel layout: " << outputChannelLayout; throw std::runtime_error(error.str()); } AVSampleFormat outputSampleFormat = mDecoder->getOutputSampleFormat(); if (outputSampleFormat == AV_SAMPLE_FMT_U8) *type = SampleType_UInt8; else if (outputSampleFormat == AV_SAMPLE_FMT_FLT) *type = SampleType_Float32; else if (outputSampleFormat == AV_SAMPLE_FMT_S16) *type = SampleType_Int16; else { char str[1024]; av_get_sample_fmt_string(str, sizeof(str), outputSampleFormat); throw std::runtime_error(std::string("Unsupported sample format: ") + str); } } size_t MWSoundDecoderBridge::read(char *buffer, size_t bytes) { return mDecoder->read(buffer, bytes); } size_t MWSoundDecoderBridge::getSampleOffset() { return mDecoder->getSampleOffset(); } boost::shared_ptr MovieAudioFactory::createDecoder(Video::VideoState* videoState) { boost::shared_ptr decoder(new MWSound::MovieAudioDecoder(videoState)); decoder->setupFormat(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundStreamPtr sound = sndMgr->playTrack(decoder->mDecoderBridge, MWBase::SoundManager::Play_TypeMovie); if (!sound.get()) { decoder.reset(); return decoder; } decoder->mAudioTrack = sound; return decoder; } } openmw-openmw-0.38.0/apps/openmw/mwsound/movieaudiofactory.hpp000066400000000000000000000005471264522266000246330ustar00rootroot00000000000000#ifndef OPENMW_MWSOUND_MOVIEAUDIOFACTORY_H #define OPENMW_MWSOUND_MOVIEAUDIOFACTORY_H #include namespace MWSound { class MovieAudioFactory : public Video::MovieAudioFactory { virtual boost::shared_ptr createDecoder(Video::VideoState* videoState); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwsound/openal_output.cpp000066400000000000000000001012541264522266000237700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "openal_output.hpp" #include "sound_decoder.hpp" #include "sound.hpp" #include "soundmanagerimp.hpp" #include "loudness.hpp" #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER 0x1013 #endif #ifndef ALC_SOFT_HRTF #define ALC_SOFT_HRTF 1 #define ALC_HRTF_SOFT 0x1992 #define ALC_DONT_CARE_SOFT 0x0002 #define ALC_HRTF_STATUS_SOFT 0x1993 #define ALC_HRTF_DISABLED_SOFT 0x0000 #define ALC_HRTF_ENABLED_SOFT 0x0001 #define ALC_HRTF_DENIED_SOFT 0x0002 #define ALC_HRTF_REQUIRED_SOFT 0x0003 #define ALC_HRTF_HEADPHONES_DETECTED_SOFT 0x0004 #define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT 0x0005 #define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994 #define ALC_HRTF_SPECIFIER_SOFT 0x1995 #define ALC_HRTF_ID_SOFT 0x1996 typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index); typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs); #ifdef AL_ALEXT_PROTOTYPES ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index); ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs); #endif #endif #define MAKE_PTRID(id) ((void*)(uintptr_t)id) #define GET_PTRID(ptr) ((ALuint)(uintptr_t)ptr) namespace { const int sLoudnessFPS = 20; // loudness values per second of audio // Helper to get an OpenAL extension function template void convertPointer(T& dest, R src) { memcpy(&dest, &src, sizeof(src)); } template void getFunc(T& func, ALCdevice *device, const char *name) { void* funcPtr = alcGetProcAddress(device, name); convertPointer(func, funcPtr); } } namespace MWSound { static void fail(const std::string &msg) { throw std::runtime_error("OpenAL exception: " + msg); } static void throwALCerror(ALCdevice *device) { ALCenum err = alcGetError(device); if(err != ALC_NO_ERROR) { const ALCchar *errstring = alcGetString(device, err); fail(errstring ? errstring : ""); } } static void throwALerror() { ALenum err = alGetError(); if(err != AL_NO_ERROR) { const ALchar *errstring = alGetString(err); fail(errstring ? errstring : ""); } } static ALenum getALFormat(ChannelConfig chans, SampleType type) { static const struct { ALenum format; ChannelConfig chans; SampleType type; } fmtlist[] = { { AL_FORMAT_MONO16, ChannelConfig_Mono, SampleType_Int16 }, { AL_FORMAT_MONO8, ChannelConfig_Mono, SampleType_UInt8 }, { AL_FORMAT_STEREO16, ChannelConfig_Stereo, SampleType_Int16 }, { AL_FORMAT_STEREO8, ChannelConfig_Stereo, SampleType_UInt8 }, }; static const size_t fmtlistsize = sizeof(fmtlist)/sizeof(fmtlist[0]); for(size_t i = 0;i < fmtlistsize;i++) { if(fmtlist[i].chans == chans && fmtlist[i].type == type) return fmtlist[i].format; } if(alIsExtensionPresent("AL_EXT_MCFORMATS")) { static const struct { char name[32]; ChannelConfig chans; SampleType type; } mcfmtlist[] = { { "AL_FORMAT_QUAD16", ChannelConfig_Quad, SampleType_Int16 }, { "AL_FORMAT_QUAD8", ChannelConfig_Quad, SampleType_UInt8 }, { "AL_FORMAT_51CHN16", ChannelConfig_5point1, SampleType_Int16 }, { "AL_FORMAT_51CHN8", ChannelConfig_5point1, SampleType_UInt8 }, { "AL_FORMAT_71CHN16", ChannelConfig_7point1, SampleType_Int16 }, { "AL_FORMAT_71CHN8", ChannelConfig_7point1, SampleType_UInt8 }, }; static const size_t mcfmtlistsize = sizeof(mcfmtlist)/sizeof(mcfmtlist[0]); for(size_t i = 0;i < mcfmtlistsize;i++) { if(mcfmtlist[i].chans == chans && mcfmtlist[i].type == type) { ALenum format = alGetEnumValue(mcfmtlist[i].name); if(format != 0 && format != -1) return format; } } } if(alIsExtensionPresent("AL_EXT_FLOAT32")) { static const struct { char name[32]; ChannelConfig chans; SampleType type; } fltfmtlist[] = { { "AL_FORMAT_MONO_FLOAT32", ChannelConfig_Mono, SampleType_Float32 }, { "AL_FORMAT_STEREO_FLOAT32", ChannelConfig_Stereo, SampleType_Float32 }, }; static const size_t fltfmtlistsize = sizeof(fltfmtlist)/sizeof(fltfmtlist[0]); for(size_t i = 0;i < fltfmtlistsize;i++) { if(fltfmtlist[i].chans == chans && fltfmtlist[i].type == type) { ALenum format = alGetEnumValue(fltfmtlist[i].name); if(format != 0 && format != -1) return format; } } if(alIsExtensionPresent("AL_EXT_MCFORMATS")) { static const struct { char name[32]; ChannelConfig chans; SampleType type; } fltmcfmtlist[] = { { "AL_FORMAT_QUAD32", ChannelConfig_Quad, SampleType_Float32 }, { "AL_FORMAT_51CHN32", ChannelConfig_5point1, SampleType_Float32 }, { "AL_FORMAT_71CHN32", ChannelConfig_7point1, SampleType_Float32 }, }; static const size_t fltmcfmtlistsize = sizeof(fltmcfmtlist)/sizeof(fltmcfmtlist[0]); for(size_t i = 0;i < fltmcfmtlistsize;i++) { if(fltmcfmtlist[i].chans == chans && fltmcfmtlist[i].type == type) { ALenum format = alGetEnumValue(fltmcfmtlist[i].name); if(format != 0 && format != -1) return format; } } } } fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")"); return AL_NONE; } // // A streaming OpenAL sound. // class OpenAL_SoundStream { static const ALuint sNumBuffers = 6; static const ALfloat sBufferLength; private: ALuint mSource; ALuint mBuffers[sNumBuffers]; ALint mCurrentBufIdx; ALenum mFormat; ALsizei mSampleRate; ALuint mBufferSize; ALuint mFrameSize; ALint mSilence; DecoderPtr mDecoder; volatile bool mIsFinished; void updateAll(bool local); OpenAL_SoundStream(const OpenAL_SoundStream &rhs); OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs); friend class OpenAL_Output; public: OpenAL_SoundStream(ALuint src, DecoderPtr decoder); ~OpenAL_SoundStream(); bool isPlaying(); double getStreamDelay() const; double getStreamOffset() const; bool process(); ALint refillQueue(); }; const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f; // // A background streaming thread (keeps active streams processed) // struct OpenAL_Output::StreamThread { typedef std::vector StreamVec; StreamVec mStreams; typedef std::vector > DecoderLoudnessVec; DecoderLoudnessVec mDecoderLoudness; volatile bool mQuitNow; boost::mutex mMutex; boost::condition_variable mCondVar; boost::thread mThread; StreamThread() : mQuitNow(false), mThread(boost::ref(*this)) { } ~StreamThread() { mQuitNow = true; mMutex.lock(); mMutex.unlock(); mCondVar.notify_all(); mThread.join(); } // boost::thread entry point void operator()() { boost::unique_lock lock(mMutex); while(!mQuitNow) { StreamVec::iterator iter = mStreams.begin(); while(iter != mStreams.end()) { if((*iter)->process() == false) iter = mStreams.erase(iter); else ++iter; } // Only do one loudness decode at a time, in case it takes particularly long we don't // want to block up anything. DecoderLoudnessVec::iterator dliter = mDecoderLoudness.begin(); if(dliter != mDecoderLoudness.end()) { DecoderPtr decoder = dliter->first; Sound_Loudness *loudness = dliter->second; mDecoderLoudness.erase(dliter); lock.unlock(); std::vector data; ChannelConfig chans = ChannelConfig_Mono; SampleType type = SampleType_Int16; int srate = 48000; try { decoder->getInfo(&srate, &chans, &type); decoder->readAll(data); } catch(std::exception &e) { std::cerr<< "Failed to decode audio: "<analyzeLoudness(data, srate, chans, type, static_cast(sLoudnessFPS)); lock.lock(); continue; } mCondVar.timed_wait(lock, boost::posix_time::milliseconds(50)); } } void add(OpenAL_SoundStream *stream) { boost::unique_lock lock(mMutex); if(std::find(mStreams.begin(), mStreams.end(), stream) == mStreams.end()) { mStreams.push_back(stream); lock.unlock(); mCondVar.notify_all(); } } void remove(OpenAL_SoundStream *stream) { boost::lock_guard lock(mMutex); StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream); if(iter != mStreams.end()) mStreams.erase(iter); } void removeAll() { boost::lock_guard lock(mMutex); mStreams.clear(); mDecoderLoudness.clear(); } void add(DecoderPtr decoder, Sound_Loudness *loudness) { boost::unique_lock lock(mMutex); mDecoderLoudness.push_back(std::make_pair(decoder, loudness)); lock.unlock(); mCondVar.notify_all(); } private: StreamThread(const StreamThread &rhs); StreamThread& operator=(const StreamThread &rhs); }; OpenAL_SoundStream::OpenAL_SoundStream(ALuint src, DecoderPtr decoder) : mSource(src), mCurrentBufIdx(0), mFrameSize(0), mSilence(0), mDecoder(decoder), mIsFinished(false) { alGenBuffers(sNumBuffers, mBuffers); throwALerror(); try { int srate; ChannelConfig chans; SampleType type; mDecoder->getInfo(&srate, &chans, &type); mFormat = getALFormat(chans, type); mSampleRate = srate; switch(type) { case SampleType_UInt8: mSilence = 0x80; break; case SampleType_Int16: mSilence = 0x00; break; case SampleType_Float32: mSilence = 0x00; break; } mFrameSize = framesToBytes(1, chans, type); mBufferSize = static_cast(sBufferLength*srate); mBufferSize *= mFrameSize; } catch(std::exception&) { alDeleteBuffers(sNumBuffers, mBuffers); alGetError(); throw; } mIsFinished = false; } OpenAL_SoundStream::~OpenAL_SoundStream() { alDeleteBuffers(sNumBuffers, mBuffers); alGetError(); mDecoder->close(); } bool OpenAL_SoundStream::isPlaying() { ALint state; alGetSourcei(mSource, AL_SOURCE_STATE, &state); throwALerror(); if(state == AL_PLAYING || state == AL_PAUSED) return true; return !mIsFinished; } double OpenAL_SoundStream::getStreamDelay() const { ALint state = AL_STOPPED; double d = 0.0; ALint offset; alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset); alGetSourcei(mSource, AL_SOURCE_STATE, &state); if(state == AL_PLAYING || state == AL_PAUSED) { ALint queued; alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); ALint inqueue = mBufferSize/mFrameSize*queued - offset; d = (double)inqueue / (double)mSampleRate; } throwALerror(); return d; } double OpenAL_SoundStream::getStreamOffset() const { ALint state = AL_STOPPED; ALint offset; double t; alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset); alGetSourcei(mSource, AL_SOURCE_STATE, &state); if(state == AL_PLAYING || state == AL_PAUSED) { ALint queued; alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); ALint inqueue = mBufferSize/mFrameSize*queued - offset; t = (double)(mDecoder->getSampleOffset() - inqueue) / (double)mSampleRate; } else { /* Underrun, or not started yet. The decoder offset is where we'll play * next. */ t = (double)mDecoder->getSampleOffset() / (double)mSampleRate; } throwALerror(); return t; } bool OpenAL_SoundStream::process() { try { if(refillQueue() > 0) { ALint state; alGetSourcei(mSource, AL_SOURCE_STATE, &state); if(state != AL_PLAYING && state != AL_PAUSED) { refillQueue(); alSourcePlay(mSource); } } } catch(std::exception&) { std::cout<< "Error updating stream \""<getName()<<"\"" < 0) { ALuint buf; alSourceUnqueueBuffers(mSource, 1, &buf); --processed; } ALint queued; alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); if(!mIsFinished && (ALuint)queued < sNumBuffers) { std::vector data(mBufferSize); for(;!mIsFinished && (ALuint)queued < sNumBuffers;++queued) { size_t got = mDecoder->read(&data[0], data.size()); if(got < data.size()) { mIsFinished = true; memset(&data[got], mSilence, data.size()-got); } if(got > 0) { ALuint bufid = mBuffers[mCurrentBufIdx]; alBufferData(bufid, mFormat, &data[0], data.size(), mSampleRate); alSourceQueueBuffers(mSource, 1, &bufid); mCurrentBufIdx = (mCurrentBufIdx+1) % sNumBuffers; } } } return queued; } // // An OpenAL output device // std::vector OpenAL_Output::enumerate() { std::vector devlist; const ALCchar *devnames; if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) devnames = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); else devnames = alcGetString(NULL, ALC_DEVICE_SPECIFIER); while(devnames && *devnames) { devlist.push_back(devnames); devnames += strlen(devnames)+1; } return devlist; } void OpenAL_Output::init(const std::string &devname) { deinit(); mDevice = alcOpenDevice(devname.c_str()); if(!mDevice) { if(devname.empty()) fail("Failed to open default device"); else fail("Failed to open \""+devname+"\""); } else { const ALCchar *name = NULL; if(alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT")) name = alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER); if(alcGetError(mDevice) != AL_NO_ERROR || !name) name = alcGetString(mDevice, ALC_DEVICE_SPECIFIER); std::cout << "Opened \""<(maxmono+maxstereo, 256); if (maxtotal == 0) // workaround for broken implementations maxtotal = 256; for(size_t i = 0;i < maxtotal;i++) { ALuint src = 0; alGenSources(1, &src); throwALerror(); mFreeSources.push_back(src); } } catch(std::exception &e) { std::cout <<"Error: "<removeAll(); for(size_t i = 0;i < mFreeSources.size();i++) alDeleteSources(1, &mFreeSources[i]); mFreeSources.clear(); alcMakeContextCurrent(0); if(mContext) alcDestroyContext(mContext); mContext = 0; if(mDevice) alcCloseDevice(mDevice); mDevice = 0; mInitialized = false; } std::vector OpenAL_Output::enumerateHrtf() { if(!mDevice) fail("Device not initialized"); std::vector ret; if(!alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF")) return ret; LPALCGETSTRINGISOFT alcGetStringiSOFT = 0; getFunc(alcGetStringiSOFT, mDevice, "alcGetStringiSOFT"); ALCint num_hrtf; alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf); ret.reserve(num_hrtf); for(ALCint i = 0;i < num_hrtf;++i) { const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i); ret.push_back(entry); } return ret; } void OpenAL_Output::enableHrtf(const std::string &hrtfname, bool auto_enable) { if(!alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF")) { std::cerr<< "HRTF extension not present" < attrs; attrs.push_back(ALC_HRTF_SOFT); attrs.push_back(auto_enable ? ALC_DONT_CARE_SOFT : ALC_TRUE); if(!hrtfname.empty()) { ALCint index = -1; ALCint num_hrtf; alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf); for(ALCint i = 0;i < num_hrtf;++i) { const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i); if(hrtfname == entry) { index = i; break; } } if(index < 0) std::cerr<< "Failed to find HRTF name \""< attrs; attrs.push_back(ALC_HRTF_SOFT); attrs.push_back(ALC_FALSE); attrs.push_back(0); alcResetDeviceSOFT(mDevice, &attrs[0]); ALCint hrtf_state; alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state); if(hrtf_state) std::cerr<< "Failed to disable HRTF" <mResourceMgr->exists(fname)) decoder->open(fname); else { std::string file = fname; std::string::size_type pos = file.rfind('.'); if(pos != std::string::npos) file = file.substr(0, pos)+".mp3"; decoder->open(file); } std::vector data; ChannelConfig chans; SampleType type; ALenum format; int srate; decoder->getInfo(&srate, &chans, &type); format = getALFormat(chans, type); decoder->readAll(data); decoder->close(); ALuint buf = 0; try { alGenBuffers(1, &buf); alBufferData(buf, format, &data[0], data.size(), srate); throwALerror(); } catch(...) { if(buf && alIsBuffer(buf)) alDeleteBuffers(1, &buf); throw; } return MAKE_PTRID(buf); } void OpenAL_Output::unloadSound(Sound_Handle data) { ALuint buffer = GET_PTRID(data); // Make sure no sources are playing this buffer before unloading it. SoundVec::const_iterator iter = mActiveSounds.begin(); for(;iter != mActiveSounds.end();++iter) { if(!(*iter)->mHandle) continue; ALuint source = GET_PTRID((*iter)->mHandle); ALint srcbuf; alGetSourcei(source, AL_BUFFER, &srcbuf); if((ALuint)srcbuf == buffer) { alSourceStop(source); alSourcei(source, AL_BUFFER, 0); } } alDeleteBuffers(1, &buffer); } size_t OpenAL_Output::getSoundDataSize(Sound_Handle data) const { ALuint buffer = GET_PTRID(data); ALint size = 0; alGetBufferi(buffer, AL_SIZE, &size); throwALerror(); return (ALuint)size; } void OpenAL_Output::initCommon2D(ALuint source, const osg::Vec3f &pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv) { alSourcef(source, AL_REFERENCE_DISTANCE, 1.0f); alSourcef(source, AL_MAX_DISTANCE, 1000.0f); alSourcef(source, AL_ROLLOFF_FACTOR, 0.0f); alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); if(useenv && mListenerEnv == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; } alSourcef(source, AL_GAIN, gain); alSourcef(source, AL_PITCH, pitch); alSourcefv(source, AL_POSITION, pos.ptr()); alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } void OpenAL_Output::initCommon3D(ALuint source, const osg::Vec3f &pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv) { alSourcef(source, AL_REFERENCE_DISTANCE, mindist); alSourcef(source, AL_MAX_DISTANCE, maxdist); alSourcef(source, AL_ROLLOFF_FACTOR, 1.0f); alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); if((pos - mListenerPos).length2() > maxdist*maxdist) gain = 0.0f; if(useenv && mListenerEnv == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; } alSourcef(source, AL_GAIN, gain); alSourcef(source, AL_PITCH, pitch); alSourcefv(source, AL_POSITION, pos.ptr()); alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } void OpenAL_Output::updateCommon(ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv, bool is3d) { if(is3d) { if((pos - mListenerPos).length2() > maxdist*maxdist) gain = 0.0f; } if(useenv && mListenerEnv == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; } alSourcef(source, AL_GAIN, gain); alSourcef(source, AL_PITCH, pitch); alSourcefv(source, AL_POSITION, pos.ptr()); alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } void OpenAL_Output::playSound(MWBase::SoundPtr sound, Sound_Handle data, float offset) { ALuint source; if(mFreeSources.empty()) fail("No free sources"); source = mFreeSources.front(); mFreeSources.pop_front(); try { initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), sound->getUseEnv()); alSourcef(source, AL_SEC_OFFSET, offset); throwALerror(); alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcePlay(source); throwALerror(); mActiveSounds.push_back(sound); } catch(std::exception&) { mFreeSources.push_back(source); throw; } sound->mHandle = MAKE_PTRID(source); } void OpenAL_Output::playSound3D(MWBase::SoundPtr sound, Sound_Handle data, float offset) { ALuint source; if(mFreeSources.empty()) fail("No free sources"); source = mFreeSources.front(); mFreeSources.pop_front(); try { initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), sound->getUseEnv()); alSourcef(source, AL_SEC_OFFSET, offset); throwALerror(); alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcePlay(source); throwALerror(); mActiveSounds.push_back(sound); } catch(std::exception&) { mFreeSources.push_back(source); throw; } sound->mHandle = MAKE_PTRID(source); } void OpenAL_Output::finishSound(MWBase::SoundPtr sound) { if(!sound->mHandle) return; ALuint source = GET_PTRID(sound->mHandle); sound->mHandle = 0; alSourceStop(source); alSourcei(source, AL_BUFFER, 0); mFreeSources.push_back(source); mActiveSounds.erase(std::find(mActiveSounds.begin(), mActiveSounds.end(), sound)); } bool OpenAL_Output::isSoundPlaying(MWBase::SoundPtr sound) { if(!sound->mHandle) return false; ALuint source = GET_PTRID(sound->mHandle); ALint state; alGetSourcei(source, AL_SOURCE_STATE, &state); throwALerror(); return state == AL_PLAYING || state == AL_PAUSED; } void OpenAL_Output::updateSound(MWBase::SoundPtr sound) { if(!sound->mHandle) return; ALuint source = GET_PTRID(sound->mHandle); updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), sound->getPitch(), sound->getUseEnv(), sound->getIs3D()); } void OpenAL_Output::streamSound(DecoderPtr decoder, MWBase::SoundStreamPtr sound) { OpenAL_SoundStream *stream = 0; ALuint source; if(mFreeSources.empty()) fail("No free sources"); source = mFreeSources.front(); mFreeSources.pop_front(); if(sound->getIsLooping()) std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; try { initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); throwALerror(); stream = new OpenAL_SoundStream(source, decoder); mStreamThread->add(stream); mActiveStreams.push_back(sound); } catch(std::exception&) { mStreamThread->remove(stream); delete stream; mFreeSources.push_back(source); throw; } sound->mHandle = stream; } void OpenAL_Output::streamSound3D(DecoderPtr decoder, MWBase::SoundStreamPtr sound) { OpenAL_SoundStream *stream = 0; ALuint source; if(mFreeSources.empty()) fail("No free sources"); source = mFreeSources.front(); mFreeSources.pop_front(); if(sound->getIsLooping()) std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; try { initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); throwALerror(); stream = new OpenAL_SoundStream(source, decoder); mStreamThread->add(stream); mActiveStreams.push_back(sound); } catch(std::exception&) { mStreamThread->remove(stream); delete stream; mFreeSources.push_back(source); throw; } sound->mHandle = stream; } void OpenAL_Output::finishStream(MWBase::SoundStreamPtr sound) { if(!sound->mHandle) return; OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); ALuint source = stream->mSource; sound->mHandle = 0; mStreamThread->remove(stream); alSourceStop(source); alSourcei(source, AL_BUFFER, 0); mFreeSources.push_back(source); mActiveStreams.erase(std::find(mActiveStreams.begin(), mActiveStreams.end(), sound)); delete stream; } double OpenAL_Output::getStreamDelay(MWBase::SoundStreamPtr sound) { if(!sound->mHandle) return 0.0; OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); return stream->getStreamDelay(); } double OpenAL_Output::getStreamOffset(MWBase::SoundStreamPtr sound) { if(!sound->mHandle) return 0.0; OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); boost::lock_guard lock(mStreamThread->mMutex); return stream->getStreamOffset(); } bool OpenAL_Output::isStreamPlaying(MWBase::SoundStreamPtr sound) { if(!sound->mHandle) return false; OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); boost::lock_guard lock(mStreamThread->mMutex); return stream->isPlaying(); } void OpenAL_Output::updateStream(MWBase::SoundStreamPtr sound) { if(!sound->mHandle) return; OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); ALuint source = stream->mSource; updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), sound->getPitch(), sound->getUseEnv(), sound->getIs3D()); } void OpenAL_Output::startUpdate() { alcSuspendContext(alcGetCurrentContext()); } void OpenAL_Output::finishUpdate() { alcProcessContext(alcGetCurrentContext()); } void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) { if(mContext) { ALfloat orient[6] = { atdir.x(), atdir.y(), atdir.z(), updir.x(), updir.y(), updir.z() }; alListenerfv(AL_POSITION, pos.ptr()); alListenerfv(AL_ORIENTATION, orient); throwALerror(); } mListenerPos = pos; mListenerEnv = env; } void OpenAL_Output::pauseSounds(int types) { std::vector sources; SoundVec::const_iterator sound = mActiveSounds.begin(); for(;sound != mActiveSounds.end();++sound) { if(*sound && (*sound)->mHandle && ((*sound)->getPlayType()&types)) sources.push_back(GET_PTRID((*sound)->mHandle)); } StreamVec::const_iterator stream = mActiveStreams.begin(); for(;stream != mActiveStreams.end();++stream) { if(*stream && (*stream)->mHandle && ((*stream)->getPlayType()&types)) { OpenAL_SoundStream *strm = reinterpret_cast((*stream)->mHandle); sources.push_back(strm->mSource); } } if(!sources.empty()) { alSourcePausev(sources.size(), &sources[0]); throwALerror(); } } void OpenAL_Output::resumeSounds(int types) { std::vector sources; SoundVec::const_iterator sound = mActiveSounds.begin(); for(;sound != mActiveSounds.end();++sound) { if(*sound && (*sound)->mHandle && ((*sound)->getPlayType()&types)) sources.push_back(GET_PTRID((*sound)->mHandle)); } StreamVec::const_iterator stream = mActiveStreams.begin(); for(;stream != mActiveStreams.end();++stream) { if(*stream && (*stream)->mHandle && ((*stream)->getPlayType()&types)) { OpenAL_SoundStream *strm = reinterpret_cast((*stream)->mHandle); sources.push_back(strm->mSource); } } if(!sources.empty()) { alSourcePlayv(sources.size(), &sources[0]); throwALerror(); } } void OpenAL_Output::loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness) { mStreamThread->add(decoder, loudness); } OpenAL_Output::OpenAL_Output(SoundManager &mgr) : Sound_Output(mgr), mDevice(0), mContext(0) , mListenerPos(0.0f, 0.0f, 0.0f), mListenerEnv(Env_Normal) , mStreamThread(new StreamThread) { } OpenAL_Output::~OpenAL_Output() { deinit(); } } openmw-openmw-0.38.0/apps/openmw/mwsound/openal_output.hpp000066400000000000000000000062631264522266000240010ustar00rootroot00000000000000#ifndef GAME_SOUND_OPENAL_OUTPUT_H #define GAME_SOUND_OPENAL_OUTPUT_H #include #include #include #include #include "alc.h" #include "al.h" #include "sound_output.hpp" namespace MWSound { class SoundManager; class Sound; class OpenAL_Output : public Sound_Output { ALCdevice *mDevice; ALCcontext *mContext; typedef std::deque IDDq; IDDq mFreeSources; typedef std::vector SoundVec; SoundVec mActiveSounds; typedef std::vector StreamVec; StreamVec mActiveStreams; osg::Vec3f mListenerPos; Environment mListenerEnv; struct StreamThread; std::auto_ptr mStreamThread; void initCommon2D(ALuint source, const osg::Vec3f &pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv); void initCommon3D(ALuint source, const osg::Vec3f &pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv); void updateCommon(ALuint source, const osg::Vec3f &pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv, bool is3d); OpenAL_Output& operator=(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs); public: virtual std::vector enumerate(); virtual void init(const std::string &devname=std::string()); virtual void deinit(); virtual std::vector enumerateHrtf(); virtual void enableHrtf(const std::string &hrtfname, bool auto_enable); virtual void disableHrtf(); virtual Sound_Handle loadSound(const std::string &fname); virtual void unloadSound(Sound_Handle data); virtual size_t getSoundDataSize(Sound_Handle data) const; virtual void playSound(MWBase::SoundPtr sound, Sound_Handle data, float offset); virtual void playSound3D(MWBase::SoundPtr sound, Sound_Handle data, float offset); virtual void finishSound(MWBase::SoundPtr sound); virtual bool isSoundPlaying(MWBase::SoundPtr sound); virtual void updateSound(MWBase::SoundPtr sound); virtual void streamSound(DecoderPtr decoder, MWBase::SoundStreamPtr sound); virtual void streamSound3D(DecoderPtr decoder, MWBase::SoundStreamPtr sound); virtual void finishStream(MWBase::SoundStreamPtr sound); virtual double getStreamDelay(MWBase::SoundStreamPtr sound); virtual double getStreamOffset(MWBase::SoundStreamPtr sound); virtual bool isStreamPlaying(MWBase::SoundStreamPtr sound); virtual void updateStream(MWBase::SoundStreamPtr sound); virtual void startUpdate(); virtual void finishUpdate(); virtual void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env); virtual void pauseSounds(int types); virtual void resumeSounds(int types); virtual void loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness); OpenAL_Output(SoundManager &mgr); virtual ~OpenAL_Output(); }; #ifndef DEFAULT_OUTPUT #define DEFAULT_OUTPUT(x) ::MWSound::OpenAL_Output((x)) #endif } #endif openmw-openmw-0.38.0/apps/openmw/mwsound/sound.hpp000066400000000000000000000057751264522266000222420ustar00rootroot00000000000000#ifndef GAME_SOUND_SOUND_H #define GAME_SOUND_SOUND_H #include "sound_output.hpp" namespace MWSound { class Sound { Sound& operator=(const Sound &rhs); Sound(const Sound &rhs); osg::Vec3f mPos; float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */ float mBaseVolume; float mPitch; float mMinDistance; float mMaxDistance; int mFlags; float mFadeOutTime; protected: Sound_Instance mHandle; friend class OpenAL_Output; public: void setPosition(const osg::Vec3f &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } void setBaseVolume(float volume) { mBaseVolume = volume; } void setFadeout(float duration) { mFadeOutTime = duration; } void updateFade(float duration) { if(mFadeOutTime > 0.0f) { float soundDuration = std::min(duration, mFadeOutTime); mVolume *= (mFadeOutTime-soundDuration) / mFadeOutTime; mFadeOutTime -= soundDuration; } } const osg::Vec3f &getPosition() const { return mPos; } float getRealVolume() const { return mVolume * mBaseVolume; } float getPitch() const { return mPitch; } float getMinDistance() const { return mMinDistance; } float getMaxDistance() const { return mMaxDistance; } MWBase::SoundManager::PlayType getPlayType() const { return (MWBase::SoundManager::PlayType)(mFlags&MWBase::SoundManager::Play_TypeMask); } bool getUseEnv() const { return !(mFlags&MWBase::SoundManager::Play_NoEnv); } bool getIsLooping() const { return mFlags&MWBase::SoundManager::Play_Loop; } bool getDistanceCull() const { return mFlags&MWBase::SoundManager::Play_RemoveAtDistance; } bool getIs3D() const { return mFlags&Play_3D; } Sound(const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) : mPos(pos), mVolume(vol), mBaseVolume(basevol), mPitch(pitch) , mMinDistance(mindist), mMaxDistance(maxdist), mFlags(flags) , mFadeOutTime(0.0f), mHandle(0) { } Sound(float vol, float basevol, float pitch, int flags) : mPos(0.0f, 0.0f, 0.0f), mVolume(vol), mBaseVolume(basevol), mPitch(pitch) , mMinDistance(1.0f), mMaxDistance(1000.0f), mFlags(flags) , mFadeOutTime(0.0f), mHandle(0) { } }; // Same as above, but it's a different type since the output handles them differently class Stream : public Sound { Stream& operator=(const Stream &rhs); Stream(const Stream &rhs); public: Stream(const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) : Sound(pos, vol, basevol, pitch, mindist, maxdist, flags) { } Stream(float vol, float basevol, float pitch, int flags) : Sound(vol, basevol, pitch, flags) { } }; } #endif openmw-openmw-0.38.0/apps/openmw/mwsound/sound_buffer.hpp000066400000000000000000000012371264522266000235600ustar00rootroot00000000000000#ifndef GAME_SOUND_SOUND_BUFFER_H #define GAME_SOUND_SOUND_BUFFER_H #include #include "soundmanagerimp.hpp" #include "sound_output.hpp" #include "loudness.hpp" #include "../mwworld/ptr.hpp" namespace MWSound { class Sound_Buffer { public: std::string mResourceName; float mVolume; float mMinDist, mMaxDist; Sound_Handle mHandle; size_t mUses; Sound_Buffer(std::string resname, float volume, float mindist, float maxdist) : mResourceName(resname), mVolume(volume), mMinDist(mindist), mMaxDist(maxdist), mHandle(0), mUses(0) { } }; } #endif /* GAME_SOUND_SOUND_BUFFER_H */ openmw-openmw-0.38.0/apps/openmw/mwsound/sound_decoder.hpp000066400000000000000000000027341264522266000237170ustar00rootroot00000000000000#ifndef GAME_SOUND_SOUND_DECODER_H #define GAME_SOUND_SOUND_DECODER_H #include #include namespace VFS { class Manager; } namespace MWSound { enum SampleType { SampleType_UInt8, SampleType_Int16, SampleType_Float32 }; const char *getSampleTypeName(SampleType type); enum ChannelConfig { ChannelConfig_Mono, ChannelConfig_Stereo, ChannelConfig_Quad, ChannelConfig_5point1, ChannelConfig_7point1 }; const char *getChannelConfigName(ChannelConfig config); size_t framesToBytes(size_t frames, ChannelConfig config, SampleType type); size_t bytesToFrames(size_t bytes, ChannelConfig config, SampleType type); struct Sound_Decoder { const VFS::Manager* mResourceMgr; virtual void open(const std::string &fname) = 0; virtual void close() = 0; virtual std::string getName() = 0; virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0; virtual size_t read(char *buffer, size_t bytes) = 0; virtual void readAll(std::vector &output); virtual void rewind() = 0; virtual size_t getSampleOffset() = 0; Sound_Decoder(const VFS::Manager* resourceMgr) : mResourceMgr(resourceMgr) { } virtual ~Sound_Decoder() { } private: Sound_Decoder(const Sound_Decoder &rhs); Sound_Decoder& operator=(const Sound_Decoder &rhs); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwsound/sound_output.hpp000066400000000000000000000057471264522266000236610ustar00rootroot00000000000000#ifndef GAME_SOUND_SOUND_OUTPUT_H #define GAME_SOUND_SOUND_OUTPUT_H #include #include #include #include "soundmanagerimp.hpp" namespace MWSound { class SoundManager; struct Sound_Decoder; class Sound; class Sound_Loudness; // An opaque handle for the implementation's sound buffers. typedef void *Sound_Handle; // An opaque handle for the implementation's sound instances. typedef void *Sound_Instance; class Sound_Output { SoundManager &mManager; virtual std::vector enumerate() = 0; virtual void init(const std::string &devname=std::string()) = 0; virtual void deinit() = 0; virtual std::vector enumerateHrtf() = 0; virtual void enableHrtf(const std::string &hrtfname, bool auto_enable) = 0; virtual void disableHrtf() = 0; virtual Sound_Handle loadSound(const std::string &fname) = 0; virtual void unloadSound(Sound_Handle data) = 0; virtual size_t getSoundDataSize(Sound_Handle data) const = 0; virtual void playSound(MWBase::SoundPtr sound, Sound_Handle data, float offset) = 0; virtual void playSound3D(MWBase::SoundPtr sound, Sound_Handle data, float offset) = 0; virtual void finishSound(MWBase::SoundPtr sound) = 0; virtual bool isSoundPlaying(MWBase::SoundPtr sound) = 0; virtual void updateSound(MWBase::SoundPtr sound) = 0; virtual void streamSound(DecoderPtr decoder, MWBase::SoundStreamPtr sound) = 0; virtual void streamSound3D(DecoderPtr decoder, MWBase::SoundStreamPtr sound) = 0; virtual void finishStream(MWBase::SoundStreamPtr sound) = 0; virtual double getStreamDelay(MWBase::SoundStreamPtr sound) = 0; virtual double getStreamOffset(MWBase::SoundStreamPtr sound) = 0; virtual bool isStreamPlaying(MWBase::SoundStreamPtr sound) = 0; virtual void updateStream(MWBase::SoundStreamPtr sound) = 0; virtual void startUpdate() = 0; virtual void finishUpdate() = 0; virtual void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) = 0; virtual void pauseSounds(int types) = 0; virtual void resumeSounds(int types) = 0; // HACK: The sound output implementation really shouldn't be handling // asynchronous loudness data loading, but it's currently where we have // a background processing thread. virtual void loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness) = 0; Sound_Output& operator=(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs); protected: bool mInitialized; Sound_Output(SoundManager &mgr) : mManager(mgr), mInitialized(false) { } public: virtual ~Sound_Output() { } bool isInitialized() const { return mInitialized; } friend class OpenAL_Output; friend class SoundManager; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwsound/soundmanagerimp.cpp000066400000000000000000001215241264522266000242650ustar00rootroot00000000000000#include "soundmanagerimp.hpp" #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/statemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwmechanics/actorutil.hpp" #include "sound_output.hpp" #include "sound_buffer.hpp" #include "sound_decoder.hpp" #include "sound.hpp" #include "openal_output.hpp" #define SOUND_OUT "OpenAL" #include "ffmpeg_decoder.hpp" #ifndef SOUND_IN #define SOUND_IN "FFmpeg" #endif namespace MWSound { SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound) : mVFS(vfs) , mOutput(new DEFAULT_OUTPUT(*this)) , mMasterVolume(1.0f) , mSFXVolume(1.0f) , mMusicVolume(1.0f) , mVoiceVolume(1.0f) , mFootstepsVolume(1.0f) , mSoundBuffers(new SoundBufferList::element_type()) , mBufferCacheSize(0) , mListenerUnderwater(false) , mListenerPos(0,0,0) , mListenerDir(1,0,0) , mListenerUp(0,0,1) , mPausedSoundTypes(0) { mMasterVolume = Settings::Manager::getFloat("master volume", "Sound"); mMasterVolume = std::min(std::max(mMasterVolume, 0.0f), 1.0f); mSFXVolume = Settings::Manager::getFloat("sfx volume", "Sound"); mSFXVolume = std::min(std::max(mSFXVolume, 0.0f), 1.0f); mMusicVolume = Settings::Manager::getFloat("music volume", "Sound"); mMusicVolume = std::min(std::max(mMusicVolume, 0.0f), 1.0f); mVoiceVolume = Settings::Manager::getFloat("voice volume", "Sound"); mVoiceVolume = std::min(std::max(mVoiceVolume, 0.0f), 1.0f); mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound"); mFootstepsVolume = std::min(std::max(mFootstepsVolume, 0.0f), 1.0f); mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1); mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1); mBufferCacheMax *= 1024*1024; mBufferCacheMin = std::min(mBufferCacheMin*1024*1024, mBufferCacheMax); if(!useSound) return; std::string hrtfname = Settings::Manager::getString("hrtf", "Sound"); int hrtfstate = Settings::Manager::getInt("hrtf enable", "Sound"); std::cout << "Sound output: " << SOUND_OUT << std::endl; std::cout << "Sound decoder: " << SOUND_IN << std::endl; try { std::vector names = mOutput->enumerate(); std::cout <<"Enumerated output devices:"<< std::endl; for(size_t i = 0;i < names.size();i++) std::cout <<" "<init(devname); } catch(std::exception &e) { if(devname.empty()) throw; std::cerr <<"Failed to open device \""<init(); Settings::Manager::setString("device", "Sound", ""); } names = mOutput->enumerateHrtf(); if(!names.empty()) { std::cout <<"Enumerated HRTF names:"<< std::endl; for(size_t i = 0;i < names.size();i++) std::cout <<" "<disableHrtf(); else if(!hrtfname.empty()) mOutput->enableHrtf(hrtfname, hrtfstate<0); } catch(std::exception &e) { std::cout <<"Sound init failed: "<begin(); for(;sfxiter != mSoundBuffers->end();++sfxiter) { if(sfxiter->mHandle) mOutput->unloadSound(sfxiter->mHandle); sfxiter->mHandle = 0; } mUnusedBuffers.clear(); mOutput.reset(); } // Return a new decoder instance, used as needed by the output implementations DecoderPtr SoundManager::getDecoder() { return DecoderPtr(new DEFAULT_DECODER (mVFS)); } Sound_Buffer *SoundManager::insertSound(const std::string &soundId, const ESM::Sound *sound) { MWBase::World* world = MWBase::Environment::get().getWorld(); static const float fAudioDefaultMinDistance = world->getStore().get().find("fAudioDefaultMinDistance")->getFloat(); static const float fAudioDefaultMaxDistance = world->getStore().get().find("fAudioDefaultMaxDistance")->getFloat(); static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); float volume, min, max; volume = static_cast(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0)); if(sound->mData.mMinRange == 0 && sound->mData.mMaxRange == 0) { min = fAudioDefaultMinDistance; max = fAudioDefaultMaxDistance; } else { min = sound->mData.mMinRange; max = sound->mData.mMaxRange; } min *= fAudioMinDistanceMult; max *= fAudioMaxDistanceMult; min = std::max(min, 1.0f); max = std::max(min, max); Sound_Buffer *sfx = &*mSoundBuffers->insert(mSoundBuffers->end(), Sound_Buffer("Sound/"+sound->mSound, volume, min, max) ); mVFS->normalizeFilename(sfx->mResourceName); mBufferNameMap.insert(std::make_pair(soundId, sfx)); return sfx; } // Lookup a soundId for its sound data (resource name, local volume, // minRange, and maxRange) Sound_Buffer *SoundManager::lookupSound(const std::string &soundId) const { NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId); if(snd != mBufferNameMap.end()) return snd->second; return 0; } // Lookup a soundId for its sound data (resource name, local volume, // minRange, and maxRange), and ensure it's ready for use. Sound_Buffer *SoundManager::loadSound(const std::string &soundId) { Sound_Buffer *sfx; NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId); if(snd != mBufferNameMap.end()) sfx = snd->second; else { MWBase::World *world = MWBase::Environment::get().getWorld(); const ESM::Sound *sound = world->getStore().get().find(soundId); sfx = insertSound(soundId, sound); } if(!sfx->mHandle) { sfx->mHandle = mOutput->loadSound(sfx->mResourceName); mBufferCacheSize += mOutput->getSoundDataSize(sfx->mHandle); if(mBufferCacheSize > mBufferCacheMax) { do { if(mUnusedBuffers.empty()) { std::cerr<< "No unused sound buffers to free, using "<getSoundDataSize(unused->mHandle); mOutput->unloadSound(unused->mHandle); unused->mHandle = 0; mUnusedBuffers.pop_back(); } while(mBufferCacheSize > mBufferCacheMin); } mUnusedBuffers.push_front(sfx); } return sfx; } DecoderPtr SoundManager::loadVoice(const std::string &voicefile, Sound_Loudness **lipdata) { DecoderPtr decoder = getDecoder(); // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. if(mVFS->exists(voicefile)) decoder->open(voicefile); else { std::string file = voicefile; std::string::size_type pos = file.rfind('.'); if(pos != std::string::npos) file = file.substr(0, pos)+".mp3"; decoder->open(file); } NameLoudnessRefMap::iterator lipiter = mVoiceLipNameMap.find(voicefile); if(lipiter != mVoiceLipNameMap.end()) { *lipdata = lipiter->second; return decoder; } mVoiceLipBuffers.insert(mVoiceLipBuffers.end(), Sound_Loudness()); lipiter = mVoiceLipNameMap.insert( std::make_pair(voicefile, &mVoiceLipBuffers.back()) ).first; mOutput->loadLoudnessAsync(decoder, lipiter->second); *lipdata = lipiter->second; return decoder; } MWBase::SoundStreamPtr SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal) { MWBase::World* world = MWBase::Environment::get().getWorld(); static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); static const float fAudioVoiceDefaultMinDistance = world->getStore().get().find("fAudioVoiceDefaultMinDistance")->getFloat(); static const float fAudioVoiceDefaultMaxDistance = world->getStore().get().find("fAudioVoiceDefaultMaxDistance")->getFloat(); static float minDistance = std::max(fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult, 1.0f); static float maxDistance = std::max(fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult, minDistance); MWBase::SoundStreamPtr sound; float basevol = volumeFromType(Play_TypeVoice); if(playlocal) { sound.reset(new Stream(1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice|Play_2D)); mOutput->streamSound(decoder, sound); } else { sound.reset(new Stream(pos, 1.0f, basevol, 1.0f, minDistance, maxDistance, Play_Normal|Play_TypeVoice|Play_3D)); mOutput->streamSound3D(decoder, sound); } return sound; } // Gets the combined volume settings for the given sound type float SoundManager::volumeFromType(PlayType type) const { float volume = mMasterVolume; switch(type) { case Play_TypeSfx: volume *= mSFXVolume; break; case Play_TypeVoice: volume *= mVoiceVolume; break; case Play_TypeFoot: volume *= mFootstepsVolume; break; case Play_TypeMusic: volume *= mMusicVolume; break; case Play_TypeMask: break; default: break; } return volume; } void SoundManager::stopMusic() { if(mMusic) mOutput->finishStream(mMusic); mMusic.reset(); } void SoundManager::streamMusicFull(const std::string& filename) { if(!mOutput->isInitialized()) return; std::cout <<"Playing "<open(filename); mMusic.reset(new Stream(1.0f, volumeFromType(Play_TypeMusic), 1.0f, Play_NoEnv|Play_TypeMusic|Play_2D)); mOutput->streamSound(decoder, mMusic); } catch(std::exception &e) { std::cout << "Music Error: " << e.what() << "\n"; mMusic.reset(); } } void SoundManager::streamMusic(const std::string& filename) { streamMusicFull("Music/"+filename); } void SoundManager::startRandomTitle() { std::vector filelist; if (mMusicFiles.find(mCurrentPlaylist) == mMusicFiles.end()) { const std::map& index = mVFS->getIndex(); std::string pattern = "Music/" + mCurrentPlaylist; mVFS->normalizeFilename(pattern); std::map::const_iterator found = index.lower_bound(pattern); while (found != index.end()) { if (found->first.size() >= pattern.size() && found->first.substr(0, pattern.size()) == pattern) filelist.push_back(found->first); else break; ++found; } mMusicFiles[mCurrentPlaylist] = filelist; } else filelist = mMusicFiles[mCurrentPlaylist]; if(!filelist.size()) return; int i = Misc::Rng::rollDice(filelist.size()); // Don't play the same music track twice in a row if (filelist[i] == mLastPlayedMusic) { i = (i+1) % filelist.size(); } streamMusicFull(filelist[i]); } bool SoundManager::isMusicPlaying() { return mMusic && mOutput->isStreamPlaying(mMusic); } void SoundManager::playPlaylist(const std::string &playlist) { mCurrentPlaylist = playlist; startRandomTitle(); } void SoundManager::say(const MWWorld::ConstPtr &ptr, const std::string &filename) { if(!mOutput->isInitialized()) return; try { std::string voicefile = "Sound/"+filename; Sound_Loudness *loudness; mVFS->normalizeFilename(voicefile); DecoderPtr decoder = loadVoice(voicefile, &loudness); if(!loudness->isReady()) mPendingSaySounds[ptr] = std::make_pair(decoder, loudness); else { MWBase::World *world = MWBase::Environment::get().getWorld(); const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); SaySoundMap::iterator oldIt = mActiveSaySounds.find(ptr); if (oldIt != mActiveSaySounds.end()) { mOutput->finishStream(oldIt->second.first); mActiveSaySounds.erase(oldIt); } MWBase::SoundStreamPtr sound = playVoice(decoder, pos, (ptr == MWMechanics::getPlayer())); mActiveSaySounds.insert(std::make_pair(ptr, std::make_pair(sound, loudness))); } } catch(std::exception &e) { std::cout <<"Sound Error: "<second.first; Sound_Loudness *loudness = snditer->second.second; float sec = mOutput->getStreamOffset(sound); return loudness->getLoudnessAtTime(sec); } return 0.0f; } void SoundManager::say(const std::string& filename) { if(!mOutput->isInitialized()) return; try { std::string voicefile = "Sound/"+filename; Sound_Loudness *loudness; mVFS->normalizeFilename(voicefile); DecoderPtr decoder = loadVoice(voicefile, &loudness); if(!loudness->isReady()) mPendingSaySounds[MWWorld::ConstPtr()] = std::make_pair(decoder, loudness); else { SaySoundMap::iterator oldIt = mActiveSaySounds.find(MWWorld::ConstPtr()); if (oldIt != mActiveSaySounds.end()) { mOutput->finishStream(oldIt->second.first); mActiveSaySounds.erase(oldIt); } mActiveSaySounds.insert(std::make_pair(MWWorld::ConstPtr(), std::make_pair(playVoice(decoder, osg::Vec3f(), true), loudness))); } } catch(std::exception &e) { std::cout <<"Sound Error: "<isStreamPlaying(snditer->second.first)) return false; return true; } return mPendingSaySounds.find(ptr) == mPendingSaySounds.end(); } void SoundManager::stopSay(const MWWorld::ConstPtr &ptr) { SaySoundMap::iterator snditer = mActiveSaySounds.find(ptr); if(snditer != mActiveSaySounds.end()) { mOutput->finishStream(snditer->second.first); mActiveSaySounds.erase(snditer); } mPendingSaySounds.erase(ptr); } MWBase::SoundStreamPtr SoundManager::playTrack(const DecoderPtr& decoder, PlayType type) { MWBase::SoundStreamPtr track; if(!mOutput->isInitialized()) return track; try { track.reset(new Stream(1.0f, volumeFromType(type), 1.0f, Play_NoEnv|type|Play_2D)); mOutput->streamSound(decoder, track); TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), track); mActiveTracks.insert(iter, track); } catch(std::exception &e) { std::cout <<"Sound Error: "<finishStream(stream); TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), stream); if(iter != mActiveTracks.end() && *iter == stream) mActiveTracks.erase(iter); } double SoundManager::getTrackTimeDelay(MWBase::SoundStreamPtr stream) { return mOutput->getStreamDelay(stream); } MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, PlayType type, PlayMode mode, float offset) { MWBase::SoundPtr sound; if(!mOutput->isInitialized()) return sound; try { Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); float basevol = volumeFromType(type); sound.reset(new Sound(volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D)); mOutput->playSound(sound, sfx->mHandle, offset); if(sfx->mUses++ == 0) { SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); if(iter != mUnusedBuffers.end()) mUnusedBuffers.erase(iter); } mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx)); } catch(std::exception&) { //std::cout <<"Sound Error: "<isInitialized()) return sound; try { // Look up the sound in the ESM data Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); float basevol = volumeFromType(type); const ESM::Position &pos = ptr.getRefData().getPosition(); const osg::Vec3f objpos(pos.asVec3()); if((mode&Play_RemoveAtDistance) && (mListenerPos-objpos).length2() > 2000*2000) return MWBase::SoundPtr(); if(!(mode&Play_NoPlayerLocal) && ptr == MWMechanics::getPlayer()) { sound.reset(new Sound(volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D)); mOutput->playSound(sound, sfx->mHandle, offset); } else { sound.reset(new Sound(objpos, volume * sfx->mVolume, basevol, pitch, sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D)); mOutput->playSound3D(sound, sfx->mHandle, offset); } if(sfx->mUses++ == 0) { SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); if(iter != mUnusedBuffers.end()) mUnusedBuffers.erase(iter); } mActiveSounds[ptr].push_back(std::make_pair(sound, sfx)); } catch(std::exception&) { //std::cout <<"Sound Error: "<isInitialized()) return sound; try { // Look up the sound in the ESM data Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); float basevol = volumeFromType(type); sound.reset(new Sound(initialPos, volume * sfx->mVolume, basevol, pitch, sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D)); mOutput->playSound3D(sound, sfx->mHandle, offset); if(sfx->mUses++ == 0) { SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); if(iter != mUnusedBuffers.end()) mUnusedBuffers.erase(iter); } mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx)); } catch(std::exception &) { //std::cout <<"Sound Error: "<finishSound(sound); } void SoundManager::stopSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId) { SoundMap::iterator snditer = mActiveSounds.find(ptr); if(snditer != mActiveSounds.end()) { Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) { if(sndidx->second == sfx) mOutput->finishSound(sndidx->first); } } } void SoundManager::stopSound3D(const MWWorld::ConstPtr &ptr) { SoundMap::iterator snditer = mActiveSounds.find(ptr); if(snditer != mActiveSounds.end()) { SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) mOutput->finishSound(sndidx->first); } } void SoundManager::stopSound(const MWWorld::CellStore *cell) { SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { if(snditer->first != MWWorld::ConstPtr() && snditer->first != MWMechanics::getPlayer() && snditer->first.getCell() == cell) { SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) mOutput->finishSound(sndidx->first); } ++snditer; } SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); while(sayiter != mActiveSaySounds.end()) { if(sayiter->first != MWWorld::ConstPtr() && sayiter->first != MWMechanics::getPlayer() && sayiter->first.getCell() == cell) { mOutput->finishStream(sayiter->second.first); } ++sayiter; } } void SoundManager::stopSound(const std::string& soundId) { SoundMap::iterator snditer = mActiveSounds.find(MWWorld::ConstPtr()); if(snditer != mActiveSounds.end()) { Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) { if(sndidx->second == sfx) mOutput->finishSound(sndidx->first); } } } void SoundManager::fadeOutSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId, float duration) { SoundMap::iterator snditer = mActiveSounds.find(ptr); if(snditer != mActiveSounds.end()) { Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) { if(sndidx->second == sfx) sndidx->first->setFadeout(duration); } } } bool SoundManager::getSoundPlaying(const MWWorld::ConstPtr &ptr, const std::string& soundId) const { SoundMap::const_iterator snditer = mActiveSounds.find(ptr); if(snditer != mActiveSounds.end()) { Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId)); SoundBufferRefPairList::const_iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) { if(sndidx->second == sfx && mOutput->isSoundPlaying(sndidx->first)) return true; } } return false; } void SoundManager::pauseSounds(int types) { if(mOutput->isInitialized()) { types &= Play_TypeMask; mOutput->pauseSounds(types); mPausedSoundTypes |= types; } } void SoundManager::resumeSounds(int types) { if(mOutput->isInitialized()) { types &= types&Play_TypeMask&mPausedSoundTypes; mOutput->resumeSounds(types); mPausedSoundTypes &= ~types; } } void SoundManager::updateRegionSound(float duration) { static float sTimeToNextEnvSound = 0.0f; static int total = 0; static std::string regionName = ""; static float sTimePassed = 0.0; MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::ConstPtr player = world->getPlayerPtr(); const ESM::Cell *cell = player.getCell()->getCell(); sTimePassed += duration; if(!cell->isExterior() || sTimePassed < sTimeToNextEnvSound) return; float a = Misc::Rng::rollClosedProbability(); // NOTE: We should use the "Minimum Time Between Environmental Sounds" and // "Maximum Time Between Environmental Sounds" fallback settings here. sTimeToNextEnvSound = 5.0f*a + 15.0f*(1.0f-a); sTimePassed = 0; if(regionName != cell->mRegion) { regionName = cell->mRegion; total = 0; } const ESM::Region *regn = world->getStore().get().search(regionName); if(regn == NULL) return; std::vector::const_iterator soundIter; if(total == 0) { soundIter = regn->mSoundList.begin(); while(soundIter != regn->mSoundList.end()) { total += (int)soundIter->mChance; ++soundIter; } if(total == 0) return; } int r = Misc::Rng::rollDice(total); int pos = 0; soundIter = regn->mSoundList.begin(); while(soundIter != regn->mSoundList.end()) { if(r - pos < soundIter->mChance) { playSound(soundIter->mSound.toString(), 1.0f, 1.0f); break; } pos += soundIter->mChance; ++soundIter; } } void SoundManager::updateSounds(float duration) { static float timePassed = 0.0; timePassed += duration; if(timePassed < (1.0f/30.0f)) return; duration = timePassed; timePassed = 0.0f; // Make sure music is still playing if(!isMusicPlaying()) startRandomTitle(); Environment env = Env_Normal; if (mListenerUnderwater) env = Env_Underwater; else if(mUnderwaterSound) { mOutput->finishSound(mUnderwaterSound); mUnderwaterSound.reset(); } mOutput->startUpdate(); mOutput->updateListener( mListenerPos, mListenerDir, mListenerUp, env ); // Check if any sounds are finished playing, and trash them SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); while(sndidx != snditer->second.end()) { MWWorld::ConstPtr ptr = snditer->first; MWBase::SoundPtr sound = sndidx->first; if(!ptr.isEmpty() && sound->getIs3D()) { const ESM::Position &pos = ptr.getRefData().getPosition(); const osg::Vec3f objpos(pos.asVec3()); sound->setPosition(objpos); if(sound->getDistanceCull()) { if((mListenerPos - objpos).length2() > 2000*2000) mOutput->finishSound(sound); } } if(!mOutput->isSoundPlaying(sound)) { mOutput->finishSound(sound); Sound_Buffer *sfx = sndidx->second; if(sfx->mUses-- == 1) mUnusedBuffers.push_front(sfx); sndidx = snditer->second.erase(sndidx); } else { sound->updateFade(duration); mOutput->updateSound(sound); ++sndidx; } } if(snditer->second.empty()) mActiveSounds.erase(snditer++); else ++snditer; } SayDecoderMap::iterator penditer = mPendingSaySounds.begin(); while(penditer != mPendingSaySounds.end()) { Sound_Loudness *loudness = penditer->second.second; if(loudness->isReady()) { try { DecoderPtr decoder = penditer->second.first; decoder->rewind(); MWBase::SoundStreamPtr sound; MWWorld::ConstPtr ptr = penditer->first; SaySoundMap::iterator old = mActiveSaySounds.find(ptr); if (old != mActiveSaySounds.end()) { mOutput->finishStream(old->second.first); mActiveSaySounds.erase(old); } if(ptr == MWWorld::ConstPtr()) sound = playVoice(decoder, osg::Vec3f(), true); else { MWBase::World *world = MWBase::Environment::get().getWorld(); const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); sound = playVoice(decoder, pos, (ptr == MWMechanics::getPlayer())); } mActiveSaySounds.insert(std::make_pair(ptr, std::make_pair(sound, loudness))); } catch(std::exception &e) { std::cerr<< "Sound Error: "<first; MWBase::SoundStreamPtr sound = sayiter->second.first; if(!ptr.isEmpty() && sound->getIs3D()) { MWBase::World *world = MWBase::Environment::get().getWorld(); const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); sound->setPosition(pos); if(sound->getDistanceCull()) { if((mListenerPos - pos).length2() > 2000*2000) mOutput->finishStream(sound); } } if(!mOutput->isStreamPlaying(sound)) { mOutput->finishStream(sound); mActiveSaySounds.erase(sayiter++); } else { sound->updateFade(duration); mOutput->updateStream(sound); ++sayiter; } } TrackList::iterator trkiter = mActiveTracks.begin(); for(;trkiter != mActiveTracks.end();++trkiter) { MWBase::SoundStreamPtr sound = *trkiter; if(!mOutput->isStreamPlaying(sound)) { mOutput->finishStream(sound); trkiter = mActiveTracks.erase(trkiter); } else { sound->updateFade(duration); mOutput->updateStream(sound); ++trkiter; } } if(mListenerUnderwater) { // Play underwater sound (after updating sounds) if(!(mUnderwaterSound && mOutput->isSoundPlaying(mUnderwaterSound))) mUnderwaterSound = playSound("Underwater", 1.0f, 1.0f, Play_TypeSfx, Play_LoopNoEnv); } mOutput->finishUpdate(); } void SoundManager::update(float duration) { if(!mOutput->isInitialized()) return; if (MWBase::Environment::get().getStateManager()->getState()!= MWBase::StateManager::State_NoGame) { updateSounds(duration); updateRegionSound(duration); } } void SoundManager::processChangedSettings(const Settings::CategorySettingVector& settings) { mMasterVolume = Settings::Manager::getFloat("master volume", "Sound"); mMusicVolume = Settings::Manager::getFloat("music volume", "Sound"); mSFXVolume = Settings::Manager::getFloat("sfx volume", "Sound"); mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound"); mVoiceVolume = Settings::Manager::getFloat("voice volume", "Sound"); if(!mOutput->isInitialized()) return; mOutput->startUpdate(); SoundMap::iterator snditer = mActiveSounds.begin(); for(;snditer != mActiveSounds.end();++snditer) { SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) { MWBase::SoundPtr sound = sndidx->first; sound->setBaseVolume(volumeFromType(sound->getPlayType())); mOutput->updateSound(sound); } } SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); for(;sayiter != mActiveSaySounds.end();++sayiter) { MWBase::SoundStreamPtr sound = sayiter->second.first; sound->setBaseVolume(volumeFromType(sound->getPlayType())); mOutput->updateStream(sound); } TrackList::iterator trkiter = mActiveTracks.begin(); for(;trkiter != mActiveTracks.end();++trkiter) { MWBase::SoundStreamPtr sound = *trkiter; sound->setBaseVolume(volumeFromType(sound->getPlayType())); mOutput->updateStream(sound); } if(mMusic) { mMusic->setBaseVolume(volumeFromType(mMusic->getPlayType())); mOutput->updateStream(mMusic); } mOutput->finishUpdate(); } void SoundManager::setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater) { mListenerPos = pos; mListenerDir = dir; mListenerUp = up; mListenerUnderwater = underwater; } void SoundManager::updatePtr(const MWWorld::ConstPtr &old, const MWWorld::ConstPtr &updated) { SoundMap::iterator snditer = mActiveSounds.find(old); if(snditer != mActiveSounds.end()) { SoundBufferRefPairList sndlist = snditer->second; mActiveSounds.erase(snditer); mActiveSounds[updated] = sndlist; } SaySoundMap::iterator sayiter = mActiveSaySounds.find(old); if(sayiter != mActiveSaySounds.end()) { SoundLoudnessPair sndlist = sayiter->second; mActiveSaySounds.erase(sayiter); mActiveSaySounds[updated] = sndlist; } SayDecoderMap::iterator penditer = mPendingSaySounds.find(old); if(penditer != mPendingSaySounds.end()) { DecoderLoudnessPair dl = penditer->second; mPendingSaySounds.erase(penditer); mPendingSaySounds[updated] = dl; } } // Default readAll implementation, for decoders that can't do anything // better void Sound_Decoder::readAll(std::vector &output) { size_t total = output.size(); size_t got; output.resize(total+32768); while((got=read(&output[total], output.size()-total)) > 0) { total += got; output.resize(total*2); } output.resize(total); } const char *getSampleTypeName(SampleType type) { switch(type) { case SampleType_UInt8: return "U8"; case SampleType_Int16: return "S16"; case SampleType_Float32: return "Float32"; } return "(unknown sample type)"; } const char *getChannelConfigName(ChannelConfig config) { switch(config) { case ChannelConfig_Mono: return "Mono"; case ChannelConfig_Stereo: return "Stereo"; case ChannelConfig_Quad: return "Quad"; case ChannelConfig_5point1: return "5.1 Surround"; case ChannelConfig_7point1: return "7.1 Surround"; } return "(unknown channel config)"; } size_t framesToBytes(size_t frames, ChannelConfig config, SampleType type) { switch(config) { case ChannelConfig_Mono: frames *= 1; break; case ChannelConfig_Stereo: frames *= 2; break; case ChannelConfig_Quad: frames *= 4; break; case ChannelConfig_5point1: frames *= 6; break; case ChannelConfig_7point1: frames *= 8; break; } switch(type) { case SampleType_UInt8: frames *= 1; break; case SampleType_Int16: frames *= 2; break; case SampleType_Float32: frames *= 4; break; } return frames; } size_t bytesToFrames(size_t bytes, ChannelConfig config, SampleType type) { return bytes / framesToBytes(1, config, type); } void SoundManager::clear() { SoundMap::iterator snditer = mActiveSounds.begin(); for(;snditer != mActiveSounds.end();++snditer) { SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) { mOutput->finishSound(sndidx->first); Sound_Buffer *sfx = sndidx->second; if(sfx->mUses-- == 1) mUnusedBuffers.push_front(sfx); } } mActiveSounds.clear(); SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); for(;sayiter != mActiveSaySounds.end();++sayiter) mOutput->finishStream(sayiter->second.first); mActiveSaySounds.clear(); TrackList::iterator trkiter = mActiveTracks.begin(); for(;trkiter != mActiveTracks.end();++trkiter) mOutput->finishStream(*trkiter); mActiveTracks.clear(); mPendingSaySounds.clear(); mUnderwaterSound.reset(); stopMusic(); } } openmw-openmw-0.38.0/apps/openmw/mwsound/soundmanagerimp.hpp000066400000000000000000000222461264522266000242730ustar00rootroot00000000000000#ifndef GAME_SOUND_SOUNDMANAGER_H #define GAME_SOUND_SOUNDMANAGER_H #include #include #include #include #include #include #include #include "loudness.hpp" #include "../mwbase/soundmanager.hpp" namespace VFS { class Manager; } namespace ESM { struct Sound; } namespace MWSound { class Sound_Output; struct Sound_Decoder; class Sound; class Sound_Buffer; enum Environment { Env_Normal, Env_Underwater }; // Extra play flags, not intended for caller use enum PlayModeEx { Play_2D = 0, Play_3D = 1<<31 }; class SoundManager : public MWBase::SoundManager { const VFS::Manager* mVFS; std::auto_ptr mOutput; // Caches available music tracks by std::map > mMusicFiles; std::string mLastPlayedMusic; // The music file that was last played float mMasterVolume; float mSFXVolume; float mMusicVolume; float mVoiceVolume; float mFootstepsVolume; typedef std::auto_ptr > SoundBufferList; // List of sound buffers, grown as needed. New enties are added to the // back, allowing existing Sound_Buffer references/pointers to remain // valid. SoundBufferList mSoundBuffers; size_t mBufferCacheMin; size_t mBufferCacheMax; size_t mBufferCacheSize; typedef std::map NameBufferMap; NameBufferMap mBufferNameMap; typedef std::deque LoudnessList; LoudnessList mVoiceLipBuffers; typedef std::map NameLoudnessRefMap; NameLoudnessRefMap mVoiceLipNameMap; // NOTE: unused buffers are stored in front-newest order. typedef std::deque SoundList; SoundList mUnusedBuffers; typedef std::pair SoundBufferRefPair; typedef std::vector SoundBufferRefPairList; typedef std::map SoundMap; SoundMap mActiveSounds; typedef std::pair SoundLoudnessPair; typedef std::map SaySoundMap; SaySoundMap mActiveSaySounds; typedef std::pair DecoderLoudnessPair; typedef std::map SayDecoderMap; SayDecoderMap mPendingSaySounds; typedef std::vector TrackList; TrackList mActiveTracks; MWBase::SoundStreamPtr mMusic; std::string mCurrentPlaylist; bool mListenerUnderwater; osg::Vec3f mListenerPos; osg::Vec3f mListenerDir; osg::Vec3f mListenerUp; int mPausedSoundTypes; MWBase::SoundPtr mUnderwaterSound; Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); Sound_Buffer *lookupSound(const std::string &soundId) const; Sound_Buffer *loadSound(const std::string &soundId); // Ensures the loudness/"lip" data gets loaded, and returns a decoder // to start streaming DecoderPtr loadVoice(const std::string &voicefile, Sound_Loudness **lipdata); MWBase::SoundStreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal); void streamMusicFull(const std::string& filename); void updateSounds(float duration); void updateRegionSound(float duration); float volumeFromType(PlayType type) const; SoundManager(const SoundManager &rhs); SoundManager& operator=(const SoundManager &rhs); protected: DecoderPtr getDecoder(); friend class OpenAL_Output; public: SoundManager(const VFS::Manager* vfs, bool useSound); virtual ~SoundManager(); virtual void processChangedSettings(const Settings::CategorySettingVector& settings); virtual void stopMusic(); ///< Stops music if it's playing virtual void streamMusic(const std::string& filename); ///< Play a soundifle /// \param filename name of a sound file in "Music/" in the data directory. virtual void startRandomTitle(); ///< Starts a random track from the current playlist virtual bool isMusicPlaying(); ///< Returns true if music is playing virtual void playPlaylist(const std::string &playlist); ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist virtual void say(const MWWorld::ConstPtr &reference, const std::string& filename); ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. virtual void say(const std::string& filename); ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. virtual bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const; ///< Is actor not speaking? virtual void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()); ///< Stop an actor speaking virtual float getSaySoundLoudness(const MWWorld::ConstPtr& reference) const; ///< Check the currently playing say sound for this actor /// and get an average loudness value (scale [0,1]) at the current time position. /// If the actor is not saying anything, returns 0. virtual MWBase::SoundStreamPtr playTrack(const DecoderPtr& decoder, PlayType type); ///< Play a 2D audio track, using a custom decoder virtual void stopTrack(MWBase::SoundStreamPtr stream); ///< Stop the given audio track from playing virtual double getTrackTimeDelay(MWBase::SoundStreamPtr stream); ///< Retives the time delay, in seconds, of the audio track (must be a sound /// returned by \ref playTrack). Only intended to be called by the track /// decoder's read method. virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0); ///< Play a sound, independently of 3D-position ///< @param offset Number of seconds into the sound to start playback. virtual MWBase::SoundPtr playSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0); ///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified. ///< @param offset Number of seconds into the sound to start playback. virtual MWBase::SoundPtr playSound3D(const osg::Vec3f& initialPos, const std::string& soundId, float volume, float pitch, PlayType type, PlayMode mode, float offset=0); ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated using Sound::setPosition. ///< @param offset Number of seconds into the sound to start playback. virtual void stopSound(MWBase::SoundPtr sound); ///< Stop the given sound from playing /// @note no-op if \a sound is null virtual void stopSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId); ///< Stop the given object from playing the given sound, virtual void stopSound3D(const MWWorld::ConstPtr &reference); ///< Stop the given object from playing all sounds. virtual void stopSound(const MWWorld::CellStore *cell); ///< Stop all sounds for the given cell. virtual void stopSound(const std::string& soundId); ///< Stop a non-3d looping sound virtual void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration); ///< Fade out given sound (that is already playing) of given object ///< @param reference Reference to object, whose sound is faded out ///< @param soundId ID of the sound to fade out. ///< @param duration Time until volume reaches 0. virtual bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? virtual void pauseSounds(int types=Play_TypeMask); ///< Pauses all currently playing sounds, including music. virtual void resumeSounds(int types=Play_TypeMask); ///< Resumes all previously paused sounds. virtual void update(float duration); virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater); virtual void updatePtr (const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated); virtual void clear(); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwstate/000077500000000000000000000000001264522266000203535ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwstate/character.cpp000066400000000000000000000115731264522266000230220ustar00rootroot00000000000000#include "character.hpp" #include #include #include #include #include #include #include #include bool MWState::operator< (const Slot& left, const Slot& right) { return left.mTimeStamp ignore reader.getRecHeader(); slot.mProfile.load (reader); if (Misc::StringUtils::lowerCase (slot.mProfile.mContentFiles.at (0))!= Misc::StringUtils::lowerCase (game)) return; // this file is for a different game -> ignore mSlots.push_back (slot); } void MWState::Character::addSlot (const ESM::SavedGame& profile) { Slot slot; std::ostringstream stream; // The profile description is user-supplied, so we need to escape the path for (std::string::const_iterator it = profile.mDescription.begin(); it != profile.mDescription.end(); ++it) { if (std::isalnum(*it)) // Ignores multibyte characters and non alphanumeric characters stream << *it; else stream << "_"; } const std::string ext = ".omwsave"; slot.mPath = mPath / (stream.str() + ext); // Append an index if necessary to ensure a unique file int i=0; while (boost::filesystem::exists(slot.mPath)) { std::ostringstream test; test << stream.str(); test << " - " << ++i; slot.mPath = mPath / (test.str() + ext); } slot.mProfile = profile; slot.mTimeStamp = std::time (0); mSlots.push_back (slot); } MWState::Character::Character (const boost::filesystem::path& saves, const std::string& game) : mPath (saves) { if (!boost::filesystem::is_directory (mPath)) { boost::filesystem::create_directories (mPath); } else { for (boost::filesystem::directory_iterator iter (mPath); iter!=boost::filesystem::directory_iterator(); ++iter) { boost::filesystem::path slotPath = *iter; try { addSlot (slotPath, game); } catch (...) {} // ignoring bad saved game files for now } std::sort (mSlots.begin(), mSlots.end()); } } void MWState::Character::cleanup() { if (mSlots.size() == 0) { // All slots are gone, no need to keep the empty directory if (boost::filesystem::is_directory (mPath)) { // Extra safety check to make sure the directory is empty (e.g. slots failed to parse header) boost::filesystem::directory_iterator it(mPath); if (it == boost::filesystem::directory_iterator()) boost::filesystem::remove_all(mPath); } } } const MWState::Slot *MWState::Character::createSlot (const ESM::SavedGame& profile) { addSlot (profile); return &mSlots.back(); } void MWState::Character::deleteSlot (const Slot *slot) { int index = slot - &mSlots[0]; if (index<0 || index>=static_cast (mSlots.size())) { // sanity check; not entirely reliable throw std::logic_error ("slot not found"); } boost::filesystem::remove(slot->mPath); mSlots.erase (mSlots.begin()+index); } const MWState::Slot *MWState::Character::updateSlot (const Slot *slot, const ESM::SavedGame& profile) { int index = slot - &mSlots[0]; if (index<0 || index>=static_cast (mSlots.size())) { // sanity check; not entirely reliable throw std::logic_error ("slot not found"); } Slot newSlot = *slot; newSlot.mProfile = profile; newSlot.mTimeStamp = std::time (0); mSlots.erase (mSlots.begin()+index); mSlots.push_back (newSlot); return &mSlots.back(); } MWState::Character::SlotIterator MWState::Character::begin() const { return mSlots.rbegin(); } MWState::Character::SlotIterator MWState::Character::end() const { return mSlots.rend(); } ESM::SavedGame MWState::Character::getSignature() const { if (mSlots.empty()) throw std::logic_error ("character signature not available"); std::vector::const_iterator iter (mSlots.begin()); Slot slot = *iter; for (++iter; iter!=mSlots.end(); ++iter) if (iter->mProfile.mPlayerLevel>slot.mProfile.mPlayerLevel) slot = *iter; else if (iter->mProfile.mPlayerLevel==slot.mProfile.mPlayerLevel && iter->mTimeStamp>slot.mTimeStamp) slot = *iter; return slot.mProfile; } const boost::filesystem::path& MWState::Character::getPath() const { return mPath; } openmw-openmw-0.38.0/apps/openmw/mwstate/character.hpp000066400000000000000000000037501264522266000230250ustar00rootroot00000000000000#ifndef GAME_STATE_CHARACTER_H #define GAME_STATE_CHARACTER_H #include #include namespace MWState { struct Slot { boost::filesystem::path mPath; ESM::SavedGame mProfile; std::time_t mTimeStamp; }; bool operator< (const Slot& left, const Slot& right); class Character { public: typedef std::vector::const_reverse_iterator SlotIterator; private: boost::filesystem::path mPath; std::vector mSlots; void addSlot (const boost::filesystem::path& path, const std::string& game); void addSlot (const ESM::SavedGame& profile); public: Character (const boost::filesystem::path& saves, const std::string& game); void cleanup(); ///< Delete the directory we used, if it is empty const Slot *createSlot (const ESM::SavedGame& profile); ///< Create new slot. /// /// \attention The ownership of the slot is not transferred. /// \note Slot must belong to this character. /// /// \attention The \a slot pointer will be invalidated by this call. void deleteSlot (const Slot *slot); const Slot *updateSlot (const Slot *slot, const ESM::SavedGame& profile); /// \note Slot must belong to this character. /// /// \attention The \a slot pointer will be invalidated by this call. SlotIterator begin() const; ///< Any call to createSlot and updateSlot can invalidate the returned iterator. SlotIterator end() const; const boost::filesystem::path& getPath() const; ESM::SavedGame getSignature() const; ///< Return signature information for this character. /// /// \attention This function must not be called if there are no slots. }; } #endif openmw-openmw-0.38.0/apps/openmw/mwstate/charactermanager.cpp000066400000000000000000000063471264522266000243600ustar00rootroot00000000000000#include "charactermanager.hpp" #include #include #include // std::isalnum #include MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves, const std::string& game) : mPath (saves), mCurrent (0), mGame (game) { if (!boost::filesystem::is_directory (mPath)) { boost::filesystem::create_directories (mPath); } else { for (boost::filesystem::directory_iterator iter (mPath); iter!=boost::filesystem::directory_iterator(); ++iter) { boost::filesystem::path characterDir = *iter; if (boost::filesystem::is_directory (characterDir)) { Character character (characterDir, mGame); if (character.begin()!=character.end()) mCharacters.push_back (character); } } } } MWState::Character *MWState::CharacterManager::getCurrentCharacter (bool create, const std::string& name) { if (!mCurrent && create) createCharacter(name); return mCurrent; } void MWState::CharacterManager::deleteSlot(const MWState::Character *character, const MWState::Slot *slot) { std::list::iterator it = findCharacter(character); it->deleteSlot(slot); if (character->begin() == character->end()) { // All slots deleted, cleanup and remove this character it->cleanup(); if (character == mCurrent) mCurrent = NULL; mCharacters.erase(it); } } void MWState::CharacterManager::createCharacter(const std::string& name) { std::ostringstream stream; // The character name is user-supplied, so we need to escape the path for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) { if (std::isalnum(*it)) // Ignores multibyte characters and non alphanumeric characters stream << *it; else stream << "_"; } boost::filesystem::path path = mPath / stream.str(); // Append an index if necessary to ensure a unique directory int i=0; while (boost::filesystem::exists(path)) { std::ostringstream test; test << stream.str(); test << " - " << ++i; path = mPath / test.str(); } mCharacters.push_back (Character (path, mGame)); mCurrent = &mCharacters.back(); } std::list::iterator MWState::CharacterManager::findCharacter(const MWState::Character* character) { std::list::iterator it = mCharacters.begin(); for (; it != mCharacters.end(); ++it) { if (&*it == character) break; } if (it == mCharacters.end()) throw std::logic_error ("invalid character"); return it; } void MWState::CharacterManager::setCurrentCharacter (const Character *character) { std::list::iterator it = findCharacter(character); mCurrent = &*it; } void MWState::CharacterManager::clearCurrentCharacter() { mCurrent = 0; } std::list::const_iterator MWState::CharacterManager::begin() const { return mCharacters.begin(); } std::list::const_iterator MWState::CharacterManager::end() const { return mCharacters.end(); } openmw-openmw-0.38.0/apps/openmw/mwstate/charactermanager.hpp000066400000000000000000000032121264522266000243510ustar00rootroot00000000000000#ifndef GAME_STATE_CHARACTERMANAGER_H #define GAME_STATE_CHARACTERMANAGER_H #include #include "character.hpp" namespace MWState { class CharacterManager { boost::filesystem::path mPath; // Uses std::list, so that mCurrent stays valid when characters are deleted std::list mCharacters; Character *mCurrent; std::string mGame; private: CharacterManager (const CharacterManager&); ///< Not implemented CharacterManager& operator= (const CharacterManager&); ///< Not implemented std::list::iterator findCharacter(const MWState::Character* character); public: CharacterManager (const boost::filesystem::path& saves, const std::string& game); Character *getCurrentCharacter (bool create, const std::string& name); ///< \param create Create a new character, if there is no current character. /// \param name The character name to use in case a new character is created. void deleteSlot(const MWState::Character *character, const MWState::Slot *slot); void createCharacter(const std::string& name); ///< Create new character within saved game management /// \param name Name for the character (does not need to be unique) void setCurrentCharacter (const Character *character); void clearCurrentCharacter(); std::list::const_iterator begin() const; std::list::const_iterator end() const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwstate/statemanagerimp.cpp000066400000000000000000000533701264522266000242500ustar00rootroot00000000000000#include "statemanagerimp.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwscript/globalscripts.hpp" void MWState::StateManager::cleanup (bool force) { if (mState!=State_NoGame || force) { MWBase::Environment::get().getSoundManager()->clear(); MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); MWBase::Environment::get().getWorld()->clear(); MWBase::Environment::get().getWindowManager()->clear(); MWBase::Environment::get().getInputManager()->clear(); MWBase::Environment::get().getMechanicsManager()->clear(); mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); mTimePlayed = 0; MWMechanics::CreatureStats::cleanup(); } } std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) const { const std::vector& current = MWBase::Environment::get().getWorld()->getContentFiles(); const std::vector& prev = reader.getGameFiles(); std::map map; for (int iPrev = 0; iPrev (prev.size()); ++iPrev) { std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) if (id==Misc::StringUtils::lowerCase (current[iCurrent])) { map.insert (std::make_pair (iPrev, iCurrent)); break; } } return map; } MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) : mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) { } void MWState::StateManager::requestQuit() { mQuitRequest = true; } bool MWState::StateManager::hasQuitRequest() const { return mQuitRequest; } void MWState::StateManager::askLoadRecent() { if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) return; if( !mAskLoadRecent ) { if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves { MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } else { MWState::Slot lastSave = *getCurrentCharacter()->begin(); std::vector buttons; buttons.push_back("#{sYes}"); buttons.push_back("#{sNo}"); std::string tag("%s"); std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); size_t pos = message.find(tag); message.replace(pos, tag.length(), lastSave.mProfile.mDescription); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons); mAskLoadRecent = true; } } } MWState::StateManager::State MWState::StateManager::getState() const { return mState; } void MWState::StateManager::newGame (bool bypass) { cleanup(); if (!bypass) MWBase::Environment::get().getWindowManager()->setNewGame (true); try { MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); MWBase::Environment::get().getWorld()->startNewGame (bypass); mState = State_Running; } catch (std::exception& e) { std::stringstream error; error << "Failed to start new game: " << e.what(); std::cerr << error.str() << std::endl; cleanup (true); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); std::vector buttons; buttons.push_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons); } } void MWState::StateManager::endGame() { mState = State_Ended; } void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) { try { ESM::SavedGame profile; MWBase::World& world = *MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world.getPlayerPtr(); profile.mContentFiles = world.getContentFiles(); profile.mPlayerName = player.get()->mBase->mName; profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); std::string classId = player.get()->mBase->mClass; if (world.getStore().get().isDynamic(classId)) profile.mPlayerClassName = world.getStore().get().find(classId)->mName; else profile.mPlayerClassId = classId; profile.mPlayerCell = world.getCellName(); profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); profile.mInGameTime.mDay = world.getDay(); profile.mInGameTime.mMonth = world.getMonth(); profile.mInGameTime.mYear = world.getYear(); profile.mTimePlayed = mTimePlayed; profile.mDescription = description; writeScreenshot(profile.mScreenshot); if (!slot) slot = getCurrentCharacter()->createSlot (profile); else slot = getCurrentCharacter()->updateSlot (slot, profile); // Write to a memory stream first. If there is an exception during the save process, we don't want to trash the // existing save file we are overwriting. std::stringstream stream; ESM::ESMWriter writer; const std::vector& current = MWBase::Environment::get().getWorld()->getContentFiles(); for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); ++iter) writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 writer.setFormat (ESM::SavedGame::sCurrentFormat); // all unused writer.setVersion(0); writer.setType(0); writer.setAuthor(""); writer.setDescription(""); int recordCount = 1 // saved game header +MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords() +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() +MWBase::Environment::get().getWindowManager()->countSavedGameRecords() +MWBase::Environment::get().getMechanicsManager()->countSavedGameRecords(); writer.setRecordCount (recordCount); writer.save (stream); Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); // Using only Cells for progress information, since they typically have the largest records by far listener.setProgressRange(MWBase::Environment::get().getWorld()->countSavedGameCells()); listener.setLabel("#{sNotifyMessage4}", true); Loading::ScopedLoad load(&listener); writer.startRecord (ESM::REC_SAVE); slot->mProfile.save (writer); writer.endRecord (ESM::REC_SAVE); MWBase::Environment::get().getJournal()->write (writer, listener); MWBase::Environment::get().getDialogueManager()->write (writer, listener); MWBase::Environment::get().getWorld()->write (writer, listener); MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener); MWBase::Environment::get().getWindowManager()->write(writer, listener); MWBase::Environment::get().getMechanicsManager()->write(writer, listener); // Ensure we have written the number of records that was estimated if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record std::cerr << "Warning: number of written savegame records does not match. Estimated: " << recordCount+1 << ", written: " << writer.getRecordCount() << std::endl; writer.close(); if (stream.fail()) throw std::runtime_error("Write operation failed (memory stream)"); // All good, write to file boost::filesystem::ofstream filestream (slot->mPath, std::ios::binary); filestream << stream.rdbuf(); if (filestream.fail()) throw std::runtime_error("Write operation failed (file stream)"); Settings::Manager::setString ("character", "Saves", slot->mPath.parent_path().filename().string()); } catch (const std::exception& e) { std::stringstream error; error << "Failed to save game: " << e.what(); std::cerr << error.str() << std::endl; std::vector buttons; buttons.push_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons); // If no file was written, clean up the slot if (slot && !boost::filesystem::exists(slot->mPath)) getCurrentCharacter()->deleteSlot(slot); } } void MWState::StateManager::quickSave (std::string name) { if (!(mState==State_Running && MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1 // char gen && MWBase::Environment::get().getWindowManager()->isSavingAllowed())) { //You can not save your game right now MWBase::Environment::get().getWindowManager()->messageBox("#{sSaveGameDenied}"); return; } const Slot* slot = NULL; Character* mCurrentCharacter = getCurrentCharacter(true); //Get current character //Find quicksave slot for (Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) { if (it->mProfile.mDescription == name) slot = &*it; } saveGame(name, slot); } void MWState::StateManager::loadGame(const std::string& filepath) { for (CharacterIterator it = mCharacterManager.begin(); it != mCharacterManager.end(); ++it) { const MWState::Character& character = *it; for (MWState::Character::SlotIterator slotIt = character.begin(); slotIt != character.end(); ++slotIt) { const MWState::Slot& slot = *slotIt; if (slot.mPath == boost::filesystem::path(filepath)) { loadGame(&character, slot.mPath.string()); return; } } } // have to peek into the save file to get the player name ESM::ESMReader reader; reader.open (filepath); if (reader.getRecName()!=ESM::REC_SAVE) return; // invalid save file -> ignore reader.getRecHeader(); ESM::SavedGame profile; profile.load (reader); reader.close(); MWState::Character* character = mCharacterManager.getCurrentCharacter(true, profile.mPlayerName); loadGame(character, filepath); mTimePlayed = profile.mTimePlayed; } void MWState::StateManager::loadGame (const Character *character, const std::string& filepath) { try { cleanup(); ESM::ESMReader reader; reader.open (filepath); if (reader.getFormat() > ESM::SavedGame::sCurrentFormat) throw std::runtime_error("This save file was created using a newer version of OpenMW and is thus not supported. Please upgrade to the newest OpenMW version to load this file."); std::map contentFileMap = buildContentFileIndexMap (reader); Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); listener.setProgressRange(100); listener.setLabel("#{sLoadingMessage14}"); Loading::ScopedLoad load(&listener); bool firstPersonCam = false; size_t total = reader.getFileSize(); int currentPercent = 0; while (reader.hasMoreRecs()) { ESM::NAME n = reader.getRecName(); reader.getRecHeader(); switch (n.val) { case ESM::REC_SAVE: { ESM::SavedGame profile; profile.load(reader); if (!verifyProfile(profile)) { cleanup (true); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); return; } mTimePlayed = profile.mTimePlayed; } break; case ESM::REC_JOUR: case ESM::REC_JOUR_LEGACY: case ESM::REC_QUES: MWBase::Environment::get().getJournal()->readRecord (reader, n.val); break; case ESM::REC_DIAS: MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); break; case ESM::REC_ALCH: case ESM::REC_ARMO: case ESM::REC_BOOK: case ESM::REC_CLAS: case ESM::REC_CLOT: case ESM::REC_ENCH: case ESM::REC_NPC_: case ESM::REC_SPEL: case ESM::REC_WEAP: case ESM::REC_GLOB: case ESM::REC_PLAY: case ESM::REC_CSTA: case ESM::REC_WTHR: case ESM::REC_DYNA: case ESM::REC_ACTC: case ESM::REC_PROJ: case ESM::REC_MPRJ: case ESM::REC_ENAB: case ESM::REC_LEVC: case ESM::REC_LEVI: MWBase::Environment::get().getWorld()->readRecord(reader, n.val, contentFileMap); break; case ESM::REC_CAM_: reader.getHNT(firstPersonCam, "FIRS"); break; case ESM::REC_GSCR: MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); break; case ESM::REC_GMAP: case ESM::REC_KEYS: case ESM::REC_ASPL: case ESM::REC_MARK: MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); break; case ESM::REC_DCOU: case ESM::REC_STLN: MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.val); break; default: // ignore invalid records std::cerr << "Ignoring unknown record: " << n.toString() << std::endl; reader.skipRecord(); } int progressPercent = static_cast(float(reader.getFileOffset())/total*100); if (progressPercent > currentPercent) { listener.increaseProgress(progressPercent-currentPercent); currentPercent = progressPercent; } } mCharacterManager.setCurrentCharacter(character); mState = State_Running; Settings::Manager::setString ("character", "Saves", character->getPath().filename().string()); MWBase::Environment::get().getWindowManager()->setNewGame(false); MWBase::Environment::get().getWorld()->setupPlayer(); MWBase::Environment::get().getWorld()->renderPlayer(); MWBase::Environment::get().getWindowManager()->updatePlayer(); MWBase::Environment::get().getMechanicsManager()->playerLoaded(); if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson()) MWBase::Environment::get().getWorld()->togglePOV(); MWWorld::ConstPtr ptr = MWMechanics::getPlayer(); const ESM::CellId& cellId = ptr.getCell()->getCell()->getCellId(); // Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false); // Vanilla MW will restart startup scripts when a save game is loaded. This is unintuitive, // but some mods may be using it as a reload detector. MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); // Do not trigger erroneous cellChanged events MWBase::Environment::get().getWorld()->markCellAsUnchanged(); } catch (const std::exception& e) { std::stringstream error; error << "Failed to load saved game: " << e.what(); std::cerr << error.str() << std::endl; cleanup (true); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); std::vector buttons; buttons.push_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons); } } void MWState::StateManager::quickLoad() { if (Character* mCurrentCharacter = getCurrentCharacter (false)) { if (mCurrentCharacter->begin() == mCurrentCharacter->end()) return; loadGame (mCurrentCharacter, mCurrentCharacter->begin()->mPath.string()); //Get newest save } } void MWState::StateManager::deleteGame(const MWState::Character *character, const MWState::Slot *slot) { mCharacterManager.deleteSlot(character, slot); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) { MWWorld::ConstPtr player = MWMechanics::getPlayer(); std::string name = player.get()->mBase->mName; return mCharacterManager.getCurrentCharacter (create, name); } MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() { return mCharacterManager.begin(); } MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() { return mCharacterManager.end(); } void MWState::StateManager::update (float duration) { mTimePlayed += duration; // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. if (mAskLoadRecent) { int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); MWState::Character *curCharacter = getCurrentCharacter(false); if(iButton==0 && curCharacter) { mAskLoadRecent = false; //Load last saved game for current character MWState::Slot lastSave = *curCharacter->begin(); loadGame(curCharacter, lastSave.mPath.string()); } else if(iButton==1) { mAskLoadRecent = false; MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } } } bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const { const std::vector& selectedContentFiles = MWBase::Environment::get().getWorld()->getContentFiles(); bool notFound = false; for (std::vector::const_iterator it = profile.mContentFiles.begin(); it != profile.mContentFiles.end(); ++it) { if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it) == selectedContentFiles.end()) { std::cerr << "Savegame dependency " << *it << " is missing." << std::endl; notFound = true; } } if (notFound) { std::vector buttons; buttons.push_back("#{sYes}"); buttons.push_back("#{sNo}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox("#{sMissingMastersMsg}", buttons, true); int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); if (selectedButton == 1 || selectedButton == -1) return false; } return true; } void MWState::StateManager::writeScreenshot(std::vector &imageData) const { int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing osg::ref_ptr screenshot (new osg::Image); MWBase::Environment::get().getWorld()->screenshot(screenshot.get(), screenshotW, screenshotH); osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg"); if (!readerwriter) { std::cerr << "Unable to write screenshot, can't find a jpg ReaderWriter" << std::endl; return; } std::ostringstream ostream; osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*screenshot, ostream); if (!result.success()) { std::cerr << "Unable to write screenshot: " << result.message() << " code " << result.status() << std::endl; return; } std::string data = ostream.str(); imageData = std::vector(data.begin(), data.end()); } openmw-openmw-0.38.0/apps/openmw/mwstate/statemanagerimp.hpp000066400000000000000000000061061264522266000242500ustar00rootroot00000000000000#ifndef GAME_STATE_STATEMANAGER_H #define GAME_STATE_STATEMANAGER_H #include #include "../mwbase/statemanager.hpp" #include #include "charactermanager.hpp" namespace MWState { class StateManager : public MWBase::StateManager { bool mQuitRequest; bool mAskLoadRecent; State mState; CharacterManager mCharacterManager; double mTimePlayed; private: void cleanup (bool force = false); bool verifyProfile (const ESM::SavedGame& profile) const; void writeScreenshot (std::vector& imageData) const; std::map buildContentFileIndexMap (const ESM::ESMReader& reader) const; public: StateManager (const boost::filesystem::path& saves, const std::string& game); virtual void requestQuit(); virtual bool hasQuitRequest() const; virtual void askLoadRecent(); virtual State getState() const; virtual void newGame (bool bypass = false); ///< Start a new game. /// /// \param bypass Skip new game mechanics. virtual void endGame(); virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot); ///< Delete a saved game slot from this character. If all save slots are deleted, the character will be deleted too. virtual void saveGame (const std::string& description, const Slot *slot = 0); ///< Write a saved game to \a slot or create a new slot if \a slot == 0. /// /// \note Slot must belong to the current character. ///Saves a file, using supplied filename, overwritting if needed /** This is mostly used for quicksaving and autosaving, for they use the same name over and over again \param name Name of save, defaults to "Quicksave"**/ virtual void quickSave(std::string name = "Quicksave"); ///Loads the last saved file /** Used for quickload **/ virtual void quickLoad(); virtual void loadGame (const std::string& filepath); ///< Load a saved game directly from the given file path. This will search the CharacterManager /// for a Character containing this save file, and set this Character current if one was found. /// Otherwise, a new Character will be created. virtual void loadGame (const Character *character, const std::string &filepath); ///< Load a saved game file belonging to the given character. virtual Character *getCurrentCharacter (bool create = true); ///< \param create Create a new character, if there is no current character. virtual CharacterIterator characterBegin(); ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned /// iterator. virtual CharacterIterator characterEnd(); virtual void update (float duration); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/000077500000000000000000000000001264522266000203625ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw/mwworld/action.cpp000066400000000000000000000032351264522266000223460ustar00rootroot00000000000000#include "action.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwmechanics/actorutil.hpp" const MWWorld::Ptr& MWWorld::Action::getTarget() const { return mTarget; } MWWorld::Action::Action (bool keepSound, const Ptr& target) : mKeepSound (keepSound), mSoundOffset(0), mTarget (target) {} MWWorld::Action::~Action() {} void MWWorld::Action::execute (const Ptr& actor) { if(!mSoundId.empty()) { if(mKeepSound && actor == MWMechanics::getPlayer()) MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Normal, mSoundOffset ); else { bool local = mTarget.isEmpty() || !mTarget.isInCell(); // no usable target if(mKeepSound) MWBase::Environment::get().getSoundManager()->playSound3D( (local ? actor : mTarget).getRefData().getPosition().asVec3(), mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Normal, mSoundOffset ); else MWBase::Environment::get().getSoundManager()->playSound3D(local ? actor : mTarget, mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Normal, mSoundOffset ); } } executeImp (actor); } void MWWorld::Action::setSound (const std::string& id) { mSoundId = id; } void MWWorld::Action::setSoundOffset(float offset) { mSoundOffset=offset; } openmw-openmw-0.38.0/apps/openmw/mwworld/action.hpp000066400000000000000000000016701264522266000223540ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTION_H #define GAME_MWWORLD_ACTION_H #include #include "ptr.hpp" namespace MWWorld { /// \brief Abstract base for actions class Action { std::string mSoundId; bool mKeepSound; float mSoundOffset; Ptr mTarget; // not implemented Action (const Action& action); Action& operator= (const Action& action); virtual void executeImp (const Ptr& actor) = 0; protected: const Ptr& getTarget() const; public: Action (bool keepSound = false, const Ptr& target = Ptr()); ///< \param keepSound Keep playing the sound even if the object the sound is played on is removed. virtual ~Action(); void execute (const Ptr& actor); void setSound (const std::string& id); void setSoundOffset(float offset); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actionalchemy.cpp000066400000000000000000000012211264522266000237020ustar00rootroot00000000000000#include "actionalchemy.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWWorld { void ActionAlchemy::executeImp (const Ptr& actor) { if (actor != MWMechanics::getPlayer()) return; if(MWMechanics::isPlayerInCombat()) { //Ensure we're not in combat MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage3}"); return; } MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Alchemy); } } openmw-openmw-0.38.0/apps/openmw/mwworld/actionalchemy.hpp000066400000000000000000000003551264522266000237160ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONALCHEMY_H #define GAME_MWWORLD_ACTIONALCHEMY_H #include "action.hpp" namespace MWWorld { class ActionAlchemy : public Action { virtual void executeImp (const Ptr& actor); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actionapply.cpp000066400000000000000000000023621264522266000234140ustar00rootroot00000000000000#include "actionapply.hpp" #include "class.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/containerstore.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWWorld { ActionApply::ActionApply (const Ptr& object, const std::string& id) : Action (false, object), mId (id) {} void ActionApply::executeImp (const Ptr& actor) { MWBase::Environment::get().getWorld()->breakInvisibility(actor); actor.getClass().apply (actor, mId, actor); actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor); } ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& object, const std::string& id, int skillIndex, int usageType) : Action (false, object), mId (id), mSkillIndex (skillIndex), mUsageType (usageType) {} void ActionApplyWithSkill::executeImp (const Ptr& actor) { MWBase::Environment::get().getWorld()->breakInvisibility(actor); if (actor.getClass().apply (actor, mId, actor) && mUsageType!=-1 && actor == MWMechanics::getPlayer()) actor.getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType); actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor); } } openmw-openmw-0.38.0/apps/openmw/mwworld/actionapply.hpp000066400000000000000000000013331264522266000234160ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONAPPLY_H #define GAME_MWWORLD_ACTIONAPPLY_H #include #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class ActionApply : public Action { std::string mId; virtual void executeImp (const Ptr& actor); public: ActionApply (const Ptr& object, const std::string& id); }; class ActionApplyWithSkill : public Action { std::string mId; int mSkillIndex; int mUsageType; virtual void executeImp (const Ptr& actor); public: ActionApplyWithSkill (const Ptr& object, const std::string& id, int skillIndex, int usageType); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actiondoor.cpp000066400000000000000000000005461264522266000232340ustar00rootroot00000000000000#include "actiondoor.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" namespace MWWorld { ActionDoor::ActionDoor (const MWWorld::Ptr& object) : Action (false, object) { } void ActionDoor::executeImp (const MWWorld::Ptr& actor) { MWBase::Environment::get().getWorld()->activateDoor(getTarget()); } } openmw-openmw-0.38.0/apps/openmw/mwworld/actiondoor.hpp000066400000000000000000000004751264522266000232420ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONDOOR_H #define GAME_MWWORLD_ACTIONDOOR_H #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class ActionDoor : public Action { virtual void executeImp (const MWWorld::Ptr& actor); public: ActionDoor (const Ptr& object); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actioneat.cpp000066400000000000000000000014551264522266000230420ustar00rootroot00000000000000#include "actioneat.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/containerstore.hpp" #include "../mwmechanics/actorutil.hpp" #include "class.hpp" namespace MWWorld { void ActionEat::executeImp (const Ptr& actor) { // remove used item (assume the item is present in inventory) getTarget().getContainerStore()->remove(getTarget(), 1, actor); // apply to actor std::string id = getTarget().getCellRef().getRefId(); if (actor.getClass().apply (actor, id, actor) && actor == MWMechanics::getPlayer()) actor.getClass().skillUsageSucceeded (actor, ESM::Skill::Alchemy, 1); } ActionEat::ActionEat (const MWWorld::Ptr& object) : Action (false, object) {} } openmw-openmw-0.38.0/apps/openmw/mwworld/actioneat.hpp000066400000000000000000000004721264522266000230450ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONEAT_H #define GAME_MWWORLD_ACTIONEAT_H #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class ActionEat : public Action { virtual void executeImp (const Ptr& actor); public: ActionEat (const MWWorld::Ptr& object); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actionequip.cpp000066400000000000000000000054531264522266000234160ustar00rootroot00000000000000#include "actionequip.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/actorutil.hpp" #include #include "inventorystore.hpp" #include "player.hpp" #include "class.hpp" namespace MWWorld { ActionEquip::ActionEquip (const MWWorld::Ptr& object) : Action (false, object) { } void ActionEquip::executeImp (const Ptr& actor) { MWWorld::Ptr object = getTarget(); MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); std::pair result = object.getClass().canBeEquipped (object, actor); // display error message if the player tried to equip something if (!result.second.empty() && actor == MWMechanics::getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox(result.second); switch(result.first) { case 0: return; default: break; } // slots that this item can be equipped in std::pair, bool> slots_ = getTarget().getClass().getEquipmentSlots(getTarget()); if (slots_.first.empty()) return; // retrieve ContainerStoreIterator to the item MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { if (*it == object) { break; } } if (it == invStore.end()) { std::stringstream error; error << "ActionEquip can't find item " << object.getCellRef().getRefId(); throw std::runtime_error(error.str()); } // equip the item in the first free slot std::vector::const_iterator slot=slots_.first.begin(); for (;slot!=slots_.first.end(); ++slot) { // if the item is equipped already, nothing to do if (invStore.getSlot(*slot) == it) return; if (invStore.getSlot(*slot) == invStore.end()) { // slot is not occupied invStore.equip(*slot, it, actor); break; } } // all slots are occupied -> cycle // move all slots one towards begin(), then equip the item in the slot that is now free if (slot == slots_.first.end()) { for (slot=slots_.first.begin();slot!=slots_.first.end(); ++slot) { invStore.unequipSlot(*slot, actor); if (slot+1 != slots_.first.end()) invStore.equip(*slot, invStore.getSlot(*(slot+1)), actor); else invStore.equip(*slot, it, actor); } } } } openmw-openmw-0.38.0/apps/openmw/mwworld/actionequip.hpp000066400000000000000000000005351264522266000234170ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONEQUIP_H #define GAME_MWWORLD_ACTIONEQUIP_H #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class ActionEquip : public Action { virtual void executeImp (const Ptr& actor); public: /// @param item to equip ActionEquip (const Ptr& object); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actionopen.cpp000066400000000000000000000012631264522266000232270ustar00rootroot00000000000000#include "actionopen.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/disease.hpp" #include "class.hpp" #include "containerstore.hpp" namespace MWWorld { ActionOpen::ActionOpen (const MWWorld::Ptr& container, bool loot) : Action (false, container) , mLoot(loot) { } void ActionOpen::executeImp (const MWWorld::Ptr& actor) { if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return; MWMechanics::diseaseContact(actor, getTarget()); MWBase::Environment::get().getWindowManager()->openContainer(getTarget(), mLoot); } } openmw-openmw-0.38.0/apps/openmw/mwworld/actionopen.hpp000066400000000000000000000010421264522266000232270ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONOPEN_H #define GAME_MWWORLD_ACTIONOPEN_H #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class ActionOpen : public Action { virtual void executeImp (const MWWorld::Ptr& actor); public: ActionOpen (const Ptr& container, bool loot=false); ///< \param container The Container the Player has activated. /// \param loot If true, display the "dispose of corpse" button private: bool mLoot; }; } #endif // ACTIONOPEN_H openmw-openmw-0.38.0/apps/openmw/mwworld/actionread.cpp000066400000000000000000000041751264522266000232060ustar00rootroot00000000000000#include "actionread.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "player.hpp" #include "class.hpp" #include "esmstore.hpp" namespace MWWorld { ActionRead::ActionRead (const MWWorld::Ptr& object) : Action (false, object) { } void ActionRead::executeImp (const MWWorld::Ptr& actor) { if (actor != MWMechanics::getPlayer()) return; //Ensure we're not in combat if(MWMechanics::isPlayerInCombat() // Reading in combat is still allowed if the scroll/book is not in the player inventory yet // (since otherwise, there would be no way to pick it up) && getTarget().getContainerStore() == &actor.getClass().getContainerStore(actor) ) { MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage4}"); return; } bool showTakeButton = (getTarget().getContainerStore() != &actor.getClass().getContainerStore(actor)); LiveCellRef *ref = getTarget().get(); if (ref->mBase->mData.mIsScroll) MWBase::Environment::get().getWindowManager()->showScroll(getTarget(), showTakeButton); else MWBase::Environment::get().getWindowManager()->showBook(getTarget(), showTakeButton); MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); // Skill gain from books if (ref->mBase->mData.mSkillID >= 0 && ref->mBase->mData.mSkillID < ESM::Skill::Length && !npcStats.hasBeenUsed (ref->mBase->mId)) { MWWorld::LiveCellRef *playerRef = actor.get(); const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().get().find ( playerRef->mBase->mClass ); npcStats.increaseSkill (ref->mBase->mData.mSkillID, *class_, true); npcStats.flagAsUsed (ref->mBase->mId); } } } openmw-openmw-0.38.0/apps/openmw/mwworld/actionread.hpp000066400000000000000000000005731264522266000232110ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONREAD_H #define GAME_MWWORLD_ACTIONREAD_H #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class ActionRead : public Action { virtual void executeImp (const MWWorld::Ptr& actor); public: /// @param book or scroll to read ActionRead (const Ptr& object); }; } #endif // ACTIONOPEN_H openmw-openmw-0.38.0/apps/openmw/mwworld/actionrepair.cpp000066400000000000000000000013121264522266000235430ustar00rootroot00000000000000#include "actionrepair.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWWorld { ActionRepair::ActionRepair(const Ptr &item) : Action(false, item) { } void ActionRepair::executeImp (const Ptr& actor) { if (actor != MWMechanics::getPlayer()) return; if(MWMechanics::isPlayerInCombat()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage2}"); return; } MWBase::Environment::get().getWindowManager()->startRepairItem(getTarget()); } } openmw-openmw-0.38.0/apps/openmw/mwworld/actionrepair.hpp000066400000000000000000000004471264522266000235600ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONREPAIR_H #define GAME_MWWORLD_ACTIONREPAIR_H #include "action.hpp" namespace MWWorld { class ActionRepair : public Action { virtual void executeImp (const Ptr& actor); public: ActionRepair(const MWWorld::Ptr& item); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actionsoulgem.cpp000066400000000000000000000013631264522266000237420ustar00rootroot00000000000000#include "actionsoulgem.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWWorld { ActionSoulgem::ActionSoulgem(const Ptr &object) : Action(false, object) { } void ActionSoulgem::executeImp(const Ptr &actor) { if (actor != MWMechanics::getPlayer()) return; if(MWMechanics::isPlayerInCombat()) { //Ensure we're not in combat MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage5}"); return; } MWBase::Environment::get().getWindowManager()->showSoulgemDialog(getTarget()); } } openmw-openmw-0.38.0/apps/openmw/mwworld/actionsoulgem.hpp000066400000000000000000000005571264522266000237530ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONSOULGEM_H #define GAME_MWWORLD_ACTIONSOULGEM_H #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class ActionSoulgem : public Action { virtual void executeImp (const MWWorld::Ptr& actor); public: /// @param soulgem to use ActionSoulgem (const Ptr& object); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actiontake.cpp000066400000000000000000000013561264522266000232150ustar00rootroot00000000000000#include "actiontake.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "class.hpp" #include "containerstore.hpp" namespace MWWorld { ActionTake::ActionTake (const MWWorld::Ptr& object) : Action (true, object) {} void ActionTake::executeImp (const Ptr& actor) { MWBase::Environment::get().getMechanicsManager()->itemTaken( actor, getTarget(), MWWorld::Ptr(), getTarget().getRefData().getCount()); actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); MWBase::Environment::get().getWorld()->deleteObject (getTarget()); } } openmw-openmw-0.38.0/apps/openmw/mwworld/actiontake.hpp000066400000000000000000000004761264522266000232240ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONTAKE_H #define GAME_MWWORLD_ACTIONTAKE_H #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class ActionTake : public Action { virtual void executeImp (const Ptr& actor); public: ActionTake (const MWWorld::Ptr& object); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actiontalk.cpp000066400000000000000000000005371264522266000232240ustar00rootroot00000000000000#include "actiontalk.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" namespace MWWorld { ActionTalk::ActionTalk (const Ptr& actor) : Action (false, actor) {} void ActionTalk::executeImp (const Ptr& actor) { MWBase::Environment::get().getDialogueManager()->startDialogue (getTarget()); } } openmw-openmw-0.38.0/apps/openmw/mwworld/actiontalk.hpp000066400000000000000000000005651264522266000232320ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONTALK_H #define GAME_MWWORLD_ACTIONTALK_H #include "ptr.hpp" #include "action.hpp" namespace MWWorld { class ActionTalk : public Action { virtual void executeImp (const Ptr& actor); public: ActionTalk (const Ptr& actor); ///< \param actor The actor the player is talking to }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actionteleport.cpp000066400000000000000000000053161264522266000241270ustar00rootroot00000000000000#include "actionteleport.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "player.hpp" namespace { void getFollowers (const MWWorld::Ptr& actor, std::set& out) { std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor); for(std::list::iterator it = followers.begin();it != followers.end();++it) { if (out.insert(*it).second) { getFollowers(*it, out); } } } } namespace MWWorld { ActionTeleport::ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers) : Action (true), mCellName (cellName), mPosition (position), mTeleportFollowers(teleportFollowers) { } void ActionTeleport::executeImp (const Ptr& actor) { if (mTeleportFollowers) { //find any NPC that is following the actor and teleport him too std::set followers; getFollowers(actor, followers); for(std::set::iterator it = followers.begin();it != followers.end();++it) { MWWorld::Ptr follower = *it; std::string script = follower.getClass().getScript(follower); if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1) continue; if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() <= 800*800) teleport(*it); } } teleport(actor); } void ActionTeleport::teleport(const Ptr &actor) { MWBase::World* world = MWBase::Environment::get().getWorld(); if(actor == world->getPlayerPtr()) { world->getPlayer().setTeleported(true); if (mCellName.empty()) world->changeToExteriorCell (mPosition); else world->changeToInteriorCell (mCellName, mPosition); } else { if (mCellName.empty()) { int cellX; int cellY; world->positionToIndex(mPosition.pos[0],mPosition.pos[1],cellX,cellY); world->moveObject(actor,world->getExterior(cellX,cellY), mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); } else world->moveObject(actor,world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); } } } openmw-openmw-0.38.0/apps/openmw/mwworld/actionteleport.hpp000066400000000000000000000016151264522266000241320ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONTELEPORT_H #define GAME_MWWORLD_ACTIONTELEPORT_H #include #include #include "action.hpp" namespace MWWorld { class ActionTeleport : public Action { std::string mCellName; ESM::Position mPosition; bool mTeleportFollowers; /// Teleports this actor and also teleports anyone following that actor. virtual void executeImp (const Ptr& actor); /// Teleports only the given actor (internal use). void teleport(const Ptr &actor); public: ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers); ///< If cellName is empty, an exterior cell is assumed. /// @param teleportFollowers Whether to teleport any following actors of the target actor as well. }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/actiontrap.cpp000066400000000000000000000027371264522266000232430ustar00rootroot00000000000000#include "actiontrap.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" namespace MWWorld { void ActionTrap::executeImp(const Ptr &actor) { osg::Vec3f actorPosition(actor.getRefData().getPosition().asVec3()); osg::Vec3f trapPosition(mTrapSource.getRefData().getPosition().asVec3()); float activationDistance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); // GUI calcs if object in activation distance include object and player geometry const float fudgeFactor = 1.25f; // Hack: if actor is beyond activation range, then assume actor is using telekinesis // to open door/container. // Note, can't just detonate the trap at the trapped object's location and use the blast // radius, because for most trap spells this is 1 foot, much less than the activation distance. if ((trapPosition - actorPosition).length() < (activationDistance * fudgeFactor)) { // assume actor touched trap MWMechanics::CastSpell cast(mTrapSource, actor); cast.mHitPosition = actorPosition; cast.cast(mSpellId); } else { // assume telekinesis used MWMechanics::CastSpell cast(mTrapSource, mTrapSource); cast.mHitPosition = trapPosition; cast.cast(mSpellId); } mTrapSource.getCellRef().setTrap(""); } } openmw-openmw-0.38.0/apps/openmw/mwworld/actiontrap.hpp000066400000000000000000000012251264522266000232370ustar00rootroot00000000000000#ifndef GAME_MWWORLD_ACTIONTRAP_H #define GAME_MWWORLD_ACTIONTRAP_H #include #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class ActionTrap : public Action { std::string mSpellId; MWWorld::Ptr mTrapSource; virtual void executeImp (const Ptr& actor); public: /// @param spellId /// @param actor Actor that activated the trap /// @param trapSource ActionTrap (const Ptr& actor, const std::string& spellId, const Ptr& trapSource) : Action(false, actor), mSpellId(spellId), mTrapSource(trapSource) {} }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/cellref.cpp000066400000000000000000000110221264522266000224760ustar00rootroot00000000000000#include "cellref.hpp" #include namespace MWWorld { const ESM::RefNum& CellRef::getRefNum() const { return mCellRef.mRefNum; } bool CellRef::hasContentFile() const { return mCellRef.mRefNum.hasContentFile(); } void CellRef::unsetRefNum() { mCellRef.mRefNum.unset(); } std::string CellRef::getRefId() const { return mCellRef.mRefID; } bool CellRef::getTeleport() const { return mCellRef.mTeleport; } ESM::Position CellRef::getDoorDest() const { return mCellRef.mDoorDest; } std::string CellRef::getDestCell() const { return mCellRef.mDestCell; } float CellRef::getScale() const { return mCellRef.mScale; } void CellRef::setScale(float scale) { if (scale != mCellRef.mScale) { mChanged = true; mCellRef.mScale = scale; } } ESM::Position CellRef::getPosition() const { return mCellRef.mPos; } void CellRef::setPosition(const ESM::Position &position) { mChanged = true; mCellRef.mPos = position; } float CellRef::getEnchantmentCharge() const { return mCellRef.mEnchantmentCharge; } void CellRef::setEnchantmentCharge(float charge) { if (charge != mCellRef.mEnchantmentCharge) { mChanged = true; mCellRef.mEnchantmentCharge = charge; } } int CellRef::getCharge() const { return mCellRef.mChargeInt; } void CellRef::setCharge(int charge) { if (charge != mCellRef.mChargeInt) { mChanged = true; mCellRef.mChargeInt = charge; } } float CellRef::getChargeFloat() const { return mCellRef.mChargeFloat; } void CellRef::setChargeFloat(float charge) { if (charge != mCellRef.mChargeFloat) { mChanged = true; mCellRef.mChargeFloat = charge; } } std::string CellRef::getOwner() const { return mCellRef.mOwner; } std::string CellRef::getGlobalVariable() const { return mCellRef.mGlobalVariable; } void CellRef::resetGlobalVariable() { if (!mCellRef.mGlobalVariable.empty()) { mChanged = true; mCellRef.mGlobalVariable.erase(); } } void CellRef::setFactionRank(int factionRank) { if (factionRank != mCellRef.mFactionRank) { mChanged = true; mCellRef.mFactionRank = factionRank; } } int CellRef::getFactionRank() const { return mCellRef.mFactionRank; } void CellRef::setOwner(const std::string &owner) { if (owner != mCellRef.mOwner) { mChanged = true; mCellRef.mOwner = owner; } } std::string CellRef::getSoul() const { return mCellRef.mSoul; } void CellRef::setSoul(const std::string &soul) { if (soul != mCellRef.mSoul) { mChanged = true; mCellRef.mSoul = soul; } } std::string CellRef::getFaction() const { return mCellRef.mFaction; } void CellRef::setFaction(const std::string &faction) { if (faction != mCellRef.mFaction) { mChanged = true; mCellRef.mFaction = faction; } } int CellRef::getLockLevel() const { return mCellRef.mLockLevel; } void CellRef::setLockLevel(int lockLevel) { if (lockLevel != mCellRef.mLockLevel) { mChanged = true; mCellRef.mLockLevel = lockLevel; } } std::string CellRef::getKey() const { return mCellRef.mKey; } std::string CellRef::getTrap() const { return mCellRef.mTrap; } void CellRef::setTrap(const std::string& trap) { if (trap != mCellRef.mTrap) { mChanged = true; mCellRef.mTrap = trap; } } int CellRef::getGoldValue() const { return mCellRef.mGoldValue; } void CellRef::setGoldValue(int value) { if (value != mCellRef.mGoldValue) { mChanged = true; mCellRef.mGoldValue = value; } } void CellRef::writeState(ESM::ObjectState &state) const { state.mRef = mCellRef; } bool CellRef::hasChanged() const { return mChanged; } } openmw-openmw-0.38.0/apps/openmw/mwworld/cellref.hpp000066400000000000000000000076531264522266000225220ustar00rootroot00000000000000#ifndef OPENMW_MWWORLD_CELLREF_H #define OPENMW_MWWORLD_CELLREF_H #include namespace ESM { struct ObjectState; } namespace MWWorld { /// \brief Encapsulated variant of ESM::CellRef with change tracking class CellRef { public: CellRef (const ESM::CellRef& ref) : mCellRef(ref) { mChanged = false; } // Note: Currently unused for items in containers const ESM::RefNum& getRefNum() const; // Set RefNum to its default state. void unsetRefNum(); /// Does the RefNum have a content file? bool hasContentFile() const; // Id of object being referenced std::string getRefId() const; // For doors - true if this door teleports to somewhere else, false // if it should open through animation. bool getTeleport() const; // Teleport location for the door, if this is a teleporting door. ESM::Position getDoorDest() const; // Destination cell for doors (optional) std::string getDestCell() const; // Scale applied to mesh float getScale() const; void setScale(float scale); // The *original* position and rotation as it was given in the Construction Set. // Current position and rotation of the object is stored in RefData. ESM::Position getPosition() const; void setPosition (const ESM::Position& position); // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full). float getEnchantmentCharge() const; void setEnchantmentCharge(float charge); // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. // If this returns int(-1) it means full health. int getCharge() const; float getChargeFloat() const; // Implemented as union with int charge void setCharge(int charge); void setChargeFloat(float charge); // The NPC that owns this object (and will get angry if you steal it) std::string getOwner() const; void setOwner(const std::string& owner); // Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed // even if it has an Owner field. // Used by bed rent scripts to allow the player to use the bed for the duration of the rent. std::string getGlobalVariable() const; void resetGlobalVariable(); // ID of creature trapped in this soul gem std::string getSoul() const; void setSoul(const std::string& soul); // The faction that owns this object (and will get angry if // you take it and are not a faction member) std::string getFaction() const; void setFaction (const std::string& faction); // PC faction rank required to use the item. Sometimes is -1, which means "any rank". void setFactionRank(int factionRank); int getFactionRank() const; // Lock level for doors and containers // Positive for a locked door. 0 for a door that was never locked. // For an unlocked door, it is set to -(previous locklevel) int getLockLevel() const; void setLockLevel(int lockLevel); // Key and trap ID names, if any std::string getKey() const; std::string getTrap() const; void setTrap(const std::string& trap); // This is 5 for Gold_005 references, 100 for Gold_100 and so on. int getGoldValue() const; void setGoldValue(int value); // Write the content of this CellRef into the given ObjectState void writeState (ESM::ObjectState& state) const; // Has this CellRef changed since it was originally loaded? bool hasChanged() const; private: bool mChanged; ESM::CellRef mCellRef; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/cellreflist.hpp000066400000000000000000000020351264522266000234030ustar00rootroot00000000000000#ifndef GAME_MWWORLD_CELLREFLIST_H #define GAME_MWWORLD_CELLREFLIST_H #include #include "livecellref.hpp" namespace MWWorld { /// \brief Collection of references of one type template struct CellRefList { typedef LiveCellRef LiveRef; typedef std::list List; List mList; /// Search for the given reference in the given reclist from /// ESMStore. Insert the reference into the list if a match is /// found. If not, throw an exception. /// Moved to cpp file, as we require a custom compare operator for it, /// and the build will fail with an ugly three-way cyclic header dependence /// so we need to pass the instantiation of the method to the linker, when /// all methods are known. void load (ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore); LiveRef &insert (const LiveRef &item) { mList.push_back(item); return mList.back(); } }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/cells.cpp000066400000000000000000000240061264522266000221720ustar00rootroot00000000000000#include "cells.hpp" #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "class.hpp" #include "esmstore.hpp" #include "containerstore.hpp" #include "cellstore.hpp" MWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { if (cell->mData.mFlags & ESM::Cell::Interior) { std::string lowerName(Misc::StringUtils::lowerCase(cell->mName)); std::map::iterator result = mInteriors.find (lowerName); if (result==mInteriors.end()) { result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first; } return &result->second; } else { std::map, CellStore>::iterator result = mExteriors.find (std::make_pair (cell->getGridX(), cell->getGridY())); if (result==mExteriors.end()) { result = mExteriors.insert (std::make_pair ( std::make_pair (cell->getGridX(), cell->getGridY()), CellStore (cell, mStore, mReader))).first; } return &result->second; } } void MWWorld::Cells::clear() { mInteriors.clear(); mExteriors.clear(); std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair("", (MWWorld::CellStore*)0)); mIdCacheIndex = 0; } MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, CellStore& cellStore) { Ptr ptr = getPtr (name, cellStore); if (!ptr.isEmpty() && ptr.isInCell()) { mIdCache[mIdCacheIndex].first = name; mIdCache[mIdCacheIndex].second = &cellStore; if (++mIdCacheIndex>=mIdCache.size()) mIdCacheIndex = 0; } return ptr; } void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const { if (cell.getState()!=CellStore::State_Loaded) cell.load (); ESM::CellState cellState; cell.saveState (cellState); writer.startRecord (ESM::REC_CSTA); cellState.mId.save (writer); cellState.save (writer); cell.writeFog(writer); cell.writeReferences (writer); writer.endRecord (ESM::REC_CSTA); } MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector& reader) : mStore (store), mReader (reader), mIdCache (40, std::pair ("", (CellStore*)0)), /// \todo make cache size configurable mIdCacheIndex (0) {} MWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y) { std::map, CellStore>::iterator result = mExteriors.find (std::make_pair (x, y)); if (result==mExteriors.end()) { const ESM::Cell *cell = mStore.get().search(x, y); if (!cell) { // Cell isn't predefined. Make one on the fly. ESM::Cell record; record.mData.mFlags = ESM::Cell::HasWater; record.mData.mX = x; record.mData.mY = y; record.mWater = 0; record.mMapColor = 0; cell = MWBase::Environment::get().getWorld()->createRecord (record); } result = mExteriors.insert (std::make_pair ( std::make_pair (x, y), CellStore (cell, mStore, mReader))).first; } if (result->second.getState()!=CellStore::State_Loaded) { result->second.load (); } return &result->second; } MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) { std::string lowerName = Misc::StringUtils::lowerCase(name); std::map::iterator result = mInteriors.find (lowerName); if (result==mInteriors.end()) { const ESM::Cell *cell = mStore.get().find(lowerName); result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first; } if (result->second.getState()!=CellStore::State_Loaded) { result->second.load (); } return &result->second; } MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id) { if (id.mPaged) return getExterior (id.mIndex.mX, id.mIndex.mY); return getInterior (id.mWorldspace); } MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell, bool searchInContainers) { if (cell.getState()==CellStore::State_Unloaded) cell.preload (); if (cell.getState()==CellStore::State_Preloaded) { if (cell.hasId (name)) { cell.load (); } else return Ptr(); } Ptr ptr = cell.search (name); if (!ptr.isEmpty() && MWWorld::CellStore::isAccessible(ptr.getRefData(), ptr.getCellRef())) return ptr; if (searchInContainers) return cell.searchInContainer (name); return Ptr(); } MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) { // First check the cache for (std::vector >::iterator iter (mIdCache.begin()); iter!=mIdCache.end(); ++iter) if (iter->first==name && iter->second) { Ptr ptr = getPtr (name, *iter->second); if (!ptr.isEmpty()) return ptr; } // Then check cells that are already listed // Search in reverse, this is a workaround for an ambiguous chargen_plank reference in the vanilla game. // there is one at -22,16 and one at -2,-9, the latter should be used. for (std::map, CellStore>::reverse_iterator iter = mExteriors.rbegin(); iter!=mExteriors.rend(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); if (!ptr.isEmpty()) return ptr; } for (std::map::iterator iter = mInteriors.begin(); iter!=mInteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); if (!ptr.isEmpty()) return ptr; } // Now try the other cells const MWWorld::Store &cells = mStore.get(); MWWorld::Store::iterator iter; for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter) { CellStore *cellStore = getCellStore (&(*iter)); Ptr ptr = getPtrAndCache (name, *cellStore); if (!ptr.isEmpty()) return ptr; } for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter) { CellStore *cellStore = getCellStore (&(*iter)); Ptr ptr = getPtrAndCache (name, *cellStore); if (!ptr.isEmpty()) return ptr; } // giving up return Ptr(); } void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector &out) { const MWWorld::Store &cells = mStore.get(); for (MWWorld::Store::iterator iter = cells.extBegin(); iter != cells.extEnd(); ++iter) { CellStore *cellStore = getCellStore (&(*iter)); Ptr ptr = getPtrAndCache (name, *cellStore); if (!ptr.isEmpty()) out.push_back(ptr); } } void MWWorld::Cells::getInteriorPtrs(const std::string &name, std::vector &out) { const MWWorld::Store &cells = mStore.get(); for (MWWorld::Store::iterator iter = cells.intBegin(); iter != cells.intEnd(); ++iter) { CellStore *cellStore = getCellStore (&(*iter)); Ptr ptr = getPtrAndCache (name, *cellStore); if (!ptr.isEmpty()) out.push_back(ptr); } } int MWWorld::Cells::countSavedGameRecords() const { int count = 0; for (std::map::const_iterator iter (mInteriors.begin()); iter!=mInteriors.end(); ++iter) if (iter->second.hasState()) ++count; for (std::map, CellStore>::const_iterator iter (mExteriors.begin()); iter!=mExteriors.end(); ++iter) if (iter->second.hasState()) ++count; return count; } void MWWorld::Cells::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { for (std::map, CellStore>::iterator iter (mExteriors.begin()); iter!=mExteriors.end(); ++iter) if (iter->second.hasState()) { writeCell (writer, iter->second); progress.increaseProgress(); } for (std::map::iterator iter (mInteriors.begin()); iter!=mInteriors.end(); ++iter) if (iter->second.hasState()) { writeCell (writer, iter->second); progress.increaseProgress(); } } struct GetCellStoreCallback : public MWWorld::CellStore::GetCellStoreCallback { public: GetCellStoreCallback(MWWorld::Cells& cells) : mCells(cells) { } MWWorld::Cells& mCells; virtual MWWorld::CellStore* getCellStore(const ESM::CellId& cellId) { try { return mCells.getCell(cellId); } catch (...) { return NULL; } } }; bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap) { if (type==ESM::REC_CSTA) { ESM::CellState state; state.mId.load (reader); CellStore *cellStore = 0; try { cellStore = getCell (state.mId); } catch (...) { // silently drop cells that don't exist anymore std::cerr << "Dropping state for cell " << state.mId.mWorldspace << " (cell no longer exists)" << std::endl; reader.skipRecord(); return true; } state.load (reader); cellStore->loadState (state); if (state.mHasFogOfWar) cellStore->readFog(reader); if (cellStore->getState()!=CellStore::State_Loaded) cellStore->load (); GetCellStoreCallback callback(*this); cellStore->readReferences (reader, contentFileMap, &callback); return true; } return false; } openmw-openmw-0.38.0/apps/openmw/mwworld/cells.hpp000066400000000000000000000047361264522266000222070ustar00rootroot00000000000000#ifndef GAME_MWWORLD_CELLS_H #define GAME_MWWORLD_CELLS_H #include #include #include #include "ptr.hpp" namespace ESM { class ESMReader; class ESMWriter; struct CellId; struct Cell; } namespace Loading { class Listener; } namespace MWWorld { class ESMStore; /// \brief Cell container class Cells { const MWWorld::ESMStore& mStore; std::vector& mReader; mutable std::map mInteriors; mutable std::map, CellStore> mExteriors; std::vector > mIdCache; std::size_t mIdCacheIndex; Cells (const Cells&); Cells& operator= (const Cells&); CellStore *getCellStore (const ESM::Cell *cell); Ptr getPtrAndCache (const std::string& name, CellStore& cellStore); void writeCell (ESM::ESMWriter& writer, CellStore& cell) const; public: void clear(); Cells (const MWWorld::ESMStore& store, std::vector& reader); CellStore *getExterior (int x, int y); CellStore *getInterior (const std::string& name); CellStore *getCell (const ESM::CellId& id); Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false); ///< \param searchInContainers Only affect loaded cells. /// @note name must be lower case /// @note name must be lower case Ptr getPtr (const std::string& name); /// Get all Ptrs referencing \a name in exterior cells /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. /// @note name must be lower case void getExteriorPtrs (const std::string& name, std::vector& out); /// Get all Ptrs referencing \a name in interior cells /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. /// @note name must be lower case void getInteriorPtrs (const std::string& name, std::vector& out); int countSavedGameRecords() const; void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; bool readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/cellstore.cpp000066400000000000000000000751461264522266000230770ustar00rootroot00000000000000#include "cellstore.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwmechanics/creaturestats.hpp" #include "ptr.hpp" #include "esmstore.hpp" #include "class.hpp" #include "containerstore.hpp" namespace { template MWWorld::Ptr searchInContainerList (MWWorld::CellRefList& containerList, const std::string& id) { for (typename MWWorld::CellRefList::List::iterator iter (containerList.mList.begin()); iter!=containerList.mList.end(); ++iter) { MWWorld::Ptr container (&*iter, 0); MWWorld::Ptr ptr = container.getClass().getContainerStore (container).search (id); if (!ptr.isEmpty()) return ptr; } return MWWorld::Ptr(); } template MWWorld::Ptr searchViaActorId (MWWorld::CellRefList& actorList, int actorId, MWWorld::CellStore *cell, const std::map& toIgnore) { for (typename MWWorld::CellRefList::List::iterator iter (actorList.mList.begin()); iter!=actorList.mList.end(); ++iter) { MWWorld::Ptr actor (&*iter, cell); if (toIgnore.find(&*iter) != toIgnore.end()) continue; if (actor.getClass().getCreatureStats (actor).matchesActorId (actorId) && actor.getRefData().getCount() > 0) return actor; } return MWWorld::Ptr(); } template void writeReferenceCollection (ESM::ESMWriter& writer, const MWWorld::CellRefList& collection) { if (!collection.mList.empty()) { // references for (typename MWWorld::CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { if (!iter->mData.hasChanged() && !iter->mRef.hasChanged() && iter->mRef.hasContentFile()) { // Reference that came from a content file and has not been changed -> ignore continue; } if (iter->mData.getCount()==0 && !iter->mRef.hasContentFile()) { // Deleted reference that did not come from a content file -> ignore continue; } RecordType state; iter->save (state); // recordId currently unused writer.writeHNT ("OBJE", collection.mList.front().mBase->sRecordId); state.save (writer); } } } template void readReferenceCollection (ESM::ESMReader& reader, MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); RecordType state; state.mRef = cref; state.load(reader); // If the reference came from a content file, make sure this content file is loaded if (state.mRef.mRefNum.hasContentFile()) { std::map::const_iterator iter = contentFileMap.find (state.mRef.mRefNum.mContentFile); if (iter==contentFileMap.end()) return; // content file has been removed -> skip state.mRef.mRefNum.mContentFile = iter->second; } if (!MWWorld::LiveCellRef::checkState (state)) return; // not valid anymore with current content files -> skip const T *record = esmStore.get().search (state.mRef.mRefID); if (!record) return; if (state.mRef.mRefNum.hasContentFile()) { for (typename MWWorld::CellRefList::List::iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) if (iter->mRef.getRefNum()==state.mRef.mRefNum) { // overwrite existing reference iter->load (state); return; } } // new reference MWWorld::LiveCellRef ref (record); ref.load (state); collection.mList.push_back (ref); } struct SearchByRefNumVisitor { MWWorld::LiveCellRefBase* mFound; ESM::RefNum mRefNumToFind; SearchByRefNumVisitor(const ESM::RefNum& toFind) : mFound(NULL) , mRefNumToFind(toFind) { } bool operator()(const MWWorld::Ptr& ptr) { if (ptr.getCellRef().getRefNum() == mRefNumToFind) { mFound = ptr.getBase(); return false; } return true; } }; } namespace MWWorld { template void CellRefList::load(ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore) { const MWWorld::Store &store = esmStore.get(); if (const X *ptr = store.search (ref.mRefID)) { typename std::list::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefNum); LiveRef liveCellRef (ref, ptr); if (deleted) liveCellRef.mData.setDeletedByContentFile(true); if (iter != mList.end()) *iter = liveCellRef; else mList.push_back (liveCellRef); } else { std::cerr << "Error: could not resolve cell reference " << ref.mRefID << " (dropping reference)" << std::endl; } } template bool operator==(const LiveCellRef& ref, int pRefnum) { return (ref.mRef.mRefnum == pRefnum); } void CellStore::moveFrom(const Ptr &object, CellStore *from) { if (mState != State_Loaded) load(); mHasState = true; MovedRefTracker::iterator found = mMovedToAnotherCell.find(object.getBase()); if (found != mMovedToAnotherCell.end()) { // A cell we had previously moved an object to is returning it to us. assert (found->second == from); mMovedToAnotherCell.erase(found); } else { mMovedHere.insert(std::make_pair(object.getBase(), from)); } updateMergedRefs(); } MWWorld::Ptr CellStore::moveTo(const Ptr &object, CellStore *cellToMoveTo) { if (cellToMoveTo == this) throw std::runtime_error("moveTo: object is already in this cell"); // We assume that *this is in State_Loaded since we could hardly have reference to a live object otherwise. if (mState != State_Loaded) throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); // Ensure that the object actually exists in the cell SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); forEach(searchVisitor); if (!searchVisitor.mFound) throw std::runtime_error("moveTo: object is not in this cell"); // Objects with no refnum can't be handled correctly in the merging process that happens // on a save/load, so do a simple copy & delete for these objects. if (!object.getCellRef().getRefNum().hasContentFile()) { MWWorld::Ptr copied = object.getClass().copyToCell(object, *cellToMoveTo, object.getRefData().getCount()); object.getRefData().setCount(0); object.getRefData().setBaseNode(NULL); return copied; } MovedRefTracker::iterator found = mMovedHere.find(object.getBase()); if (found != mMovedHere.end()) { // Special case - object didn't originate in this cell // Move it back to its original cell first CellStore* originalCell = found->second; assert (originalCell != this); originalCell->moveFrom(object, this); mMovedHere.erase(found); // Now that object is back to its rightful owner, we can move it if (cellToMoveTo != originalCell) { originalCell->moveTo(object, cellToMoveTo); } updateMergedRefs(); return MWWorld::Ptr(object.getBase(), cellToMoveTo); } cellToMoveTo->moveFrom(object, this); mMovedToAnotherCell.insert(std::make_pair(object.getBase(), cellToMoveTo)); updateMergedRefs(); return MWWorld::Ptr(object.getBase(), cellToMoveTo); } struct MergeVisitor { MergeVisitor(std::vector& mergeTo, const std::map& movedHere, const std::map& movedToAnotherCell) : mMergeTo(mergeTo) , mMovedHere(movedHere) , mMovedToAnotherCell(movedToAnotherCell) { } bool operator() (const MWWorld::Ptr& ptr) { if (mMovedToAnotherCell.find(ptr.getBase()) != mMovedToAnotherCell.end()) return true; mMergeTo.push_back(ptr.getBase()); return true; } void merge() { for (std::map::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) mMergeTo.push_back(it->first); } private: std::vector& mMergeTo; const std::map& mMovedHere; const std::map& mMovedToAnotherCell; }; void CellStore::updateMergedRefs() { mMergedRefs.clear(); MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell); forEachInternal(visitor); visitor.merge(); } CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector& readerList) : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) { mWaterLevel = cell->mWater; } const ESM::Cell *CellStore::getCell() const { return mCell; } CellStore::State CellStore::getState() const { return mState; } bool CellStore::hasState() const { return mHasState; } bool CellStore::hasId (const std::string& id) const { if (mState==State_Unloaded) return false; if (mState==State_Preloaded) return std::binary_search (mIds.begin(), mIds.end(), id); return searchConst (id).isEmpty(); } template struct SearchVisitor { PtrType mFound; std::string mIdToFind; bool operator()(const PtrType& ptr) { if (ptr.getCellRef().getRefId() == mIdToFind) { mFound = ptr; return false; } return true; } }; Ptr CellStore::search (const std::string& id) { SearchVisitor searchVisitor; searchVisitor.mIdToFind = id; forEach(searchVisitor); return searchVisitor.mFound; } ConstPtr CellStore::searchConst (const std::string& id) const { SearchVisitor searchVisitor; searchVisitor.mIdToFind = id; forEachConst(searchVisitor); return searchVisitor.mFound; } Ptr CellStore::searchViaActorId (int id) { if (Ptr ptr = ::searchViaActorId (mNpcs, id, this, mMovedToAnotherCell)) return ptr; if (Ptr ptr = ::searchViaActorId (mCreatures, id, this, mMovedToAnotherCell)) return ptr; for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) { MWWorld::Ptr actor (it->first, this); if (!actor.getClass().isActor()) continue; if (actor.getClass().getCreatureStats (actor).matchesActorId (id) && actor.getRefData().getCount() > 0) return actor; } return Ptr(); } float CellStore::getWaterLevel() const { if (isExterior()) return -1; return mWaterLevel; } void CellStore::setWaterLevel (float level) { mWaterLevel = level; mHasState = true; } int CellStore::count() const { return mMergedRefs.size(); } void CellStore::load () { if (mState!=State_Loaded) { if (mState==State_Preloaded) mIds.clear(); loadRefs (); mState = State_Loaded; // TODO: the pathgrid graph only needs to be loaded for active cells, so move this somewhere else. // In a simple test, loading the graph for all cells in MW + expansions took 200 ms mPathgridGraph.load(this); } } void CellStore::preload () { if (mState==State_Unloaded) { listRefs (); mState = State_Preloaded; } } void CellStore::listRefs() { std::vector& esm = mReader; assert (mCell); if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. // Load references from all plugins that do something with this cell. for (size_t i = 0; i < mCell->mContextList.size(); i++) { // Reopen the ESM reader and seek to the right position. int index = mCell->mContextList.at(i).index; mCell->restore (esm[index], i); ESM::CellRef ref; // Get each reference in turn bool deleted = false; while (mCell->getNextRef (esm[index], ref, deleted)) { if (deleted) continue; // Don't list reference if it was moved to a different cell. ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); if (iter != mCell->mMovedRefs.end()) { continue; } mIds.push_back (Misc::StringUtils::lowerCase (ref.mRefID)); } } // List moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { const ESM::CellRef &ref = *it; mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID)); } std::sort (mIds.begin(), mIds.end()); } void CellStore::loadRefs() { std::vector& esm = mReader; assert (mCell); if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. // Load references from all plugins that do something with this cell. for (size_t i = 0; i < mCell->mContextList.size(); i++) { // Reopen the ESM reader and seek to the right position. int index = mCell->mContextList.at(i).index; mCell->restore (esm[index], i); ESM::CellRef ref; ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile; // Get each reference in turn bool deleted = false; while(mCell->getNextRef(esm[index], ref, deleted)) { // Don't load reference if it was moved to a different cell. ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); if (iter != mCell->mMovedRefs.end()) { continue; } loadRef (ref, deleted); } } // Load moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { ESM::CellRef &ref = const_cast(*it); loadRef (ref, false); } updateMergedRefs(); } bool CellStore::isExterior() const { return mCell->isExterior(); } Ptr CellStore::searchInContainer (const std::string& id) { bool oldState = mHasState; mHasState = true; if (Ptr ptr = searchInContainerList (mContainers, id)) return ptr; if (Ptr ptr = searchInContainerList (mCreatures, id)) return ptr; if (Ptr ptr = searchInContainerList (mNpcs, id)) return ptr; mHasState = oldState; return Ptr(); } void CellStore::loadRef (ESM::CellRef& ref, bool deleted) { Misc::StringUtils::lowerCaseInPlace (ref.mRefID); const MWWorld::ESMStore& store = mStore; switch (store.find (ref.mRefID)) { case ESM::REC_ACTI: mActivators.load(ref, deleted, store); break; case ESM::REC_ALCH: mPotions.load(ref, deleted,store); break; case ESM::REC_APPA: mAppas.load(ref, deleted, store); break; case ESM::REC_ARMO: mArmors.load(ref, deleted, store); break; case ESM::REC_BOOK: mBooks.load(ref, deleted, store); break; case ESM::REC_CLOT: mClothes.load(ref, deleted, store); break; case ESM::REC_CONT: mContainers.load(ref, deleted, store); break; case ESM::REC_CREA: mCreatures.load(ref, deleted, store); break; case ESM::REC_DOOR: mDoors.load(ref, deleted, store); break; case ESM::REC_INGR: mIngreds.load(ref, deleted, store); break; case ESM::REC_LEVC: mCreatureLists.load(ref, deleted, store); break; case ESM::REC_LEVI: mItemLists.load(ref, deleted, store); break; case ESM::REC_LIGH: mLights.load(ref, deleted, store); break; case ESM::REC_LOCK: mLockpicks.load(ref, deleted, store); break; case ESM::REC_MISC: mMiscItems.load(ref, deleted, store); break; case ESM::REC_NPC_: mNpcs.load(ref, deleted, store); break; case ESM::REC_PROB: mProbes.load(ref, deleted, store); break; case ESM::REC_REPA: mRepairs.load(ref, deleted, store); break; case ESM::REC_STAT: mStatics.load(ref, deleted, store); break; case ESM::REC_WEAP: mWeapons.load(ref, deleted, store); break; case ESM::REC_BODY: mBodyParts.load(ref, deleted, store); break; case 0: std::cerr << "Cell reference " + ref.mRefID + " not found!\n"; break; default: std::cerr << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; } } void CellStore::loadState (const ESM::CellState& state) { mHasState = true; if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) mWaterLevel = state.mWaterLevel; mWaterLevel = state.mWaterLevel; mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn); } void CellStore::saveState (ESM::CellState& state) const { state.mId = mCell->getCellId(); if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) state.mWaterLevel = mWaterLevel; state.mWaterLevel = mWaterLevel; state.mHasFogOfWar = (mFogState.get() ? 1 : 0); state.mLastRespawn = mLastRespawn.toEsm(); } void CellStore::writeFog(ESM::ESMWriter &writer) const { if (mFogState.get()) { mFogState->save(writer, mCell->mData.mFlags & ESM::Cell::Interior); } } void CellStore::readFog(ESM::ESMReader &reader) { mFogState.reset(new ESM::FogState()); mFogState->load(reader); } void CellStore::writeReferences (ESM::ESMWriter& writer) const { writeReferenceCollection (writer, mActivators); writeReferenceCollection (writer, mPotions); writeReferenceCollection (writer, mAppas); writeReferenceCollection (writer, mArmors); writeReferenceCollection (writer, mBooks); writeReferenceCollection (writer, mClothes); writeReferenceCollection (writer, mContainers); writeReferenceCollection (writer, mCreatures); writeReferenceCollection (writer, mDoors); writeReferenceCollection (writer, mIngreds); writeReferenceCollection (writer, mCreatureLists); writeReferenceCollection (writer, mItemLists); writeReferenceCollection (writer, mLights); writeReferenceCollection (writer, mLockpicks); writeReferenceCollection (writer, mMiscItems); writeReferenceCollection (writer, mNpcs); writeReferenceCollection (writer, mProbes); writeReferenceCollection (writer, mRepairs); writeReferenceCollection (writer, mStatics); writeReferenceCollection (writer, mWeapons); writeReferenceCollection (writer, mBodyParts); for (MovedRefTracker::const_iterator it = mMovedToAnotherCell.begin(); it != mMovedToAnotherCell.end(); ++it) { LiveCellRefBase* base = it->first; ESM::RefNum refNum = base->mRef.getRefNum(); ESM::CellId movedTo = it->second->getCell()->getCellId(); refNum.save(writer, true, "MVRF"); movedTo.save(writer); } } void CellStore::readReferences (ESM::ESMReader& reader, const std::map& contentFileMap, GetCellStoreCallback* callback) { mHasState = true; while (reader.isNextSub ("OBJE")) { unsigned int unused; reader.getHT (unused); // load the RefID first so we know what type of object it is ESM::CellRef cref; cref.loadId(reader, true); int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID); if (type == 0) { std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl; reader.skipHSubUntil("OBJE"); continue; } switch (type) { case ESM::REC_ACTI: readReferenceCollection (reader, mActivators, cref, contentFileMap); break; case ESM::REC_ALCH: readReferenceCollection (reader, mPotions, cref, contentFileMap); break; case ESM::REC_APPA: readReferenceCollection (reader, mAppas, cref, contentFileMap); break; case ESM::REC_ARMO: readReferenceCollection (reader, mArmors, cref, contentFileMap); break; case ESM::REC_BOOK: readReferenceCollection (reader, mBooks, cref, contentFileMap); break; case ESM::REC_CLOT: readReferenceCollection (reader, mClothes, cref, contentFileMap); break; case ESM::REC_CONT: readReferenceCollection (reader, mContainers, cref, contentFileMap); break; case ESM::REC_CREA: readReferenceCollection (reader, mCreatures, cref, contentFileMap); break; case ESM::REC_DOOR: readReferenceCollection (reader, mDoors, cref, contentFileMap); break; case ESM::REC_INGR: readReferenceCollection (reader, mIngreds, cref, contentFileMap); break; case ESM::REC_LEVC: readReferenceCollection (reader, mCreatureLists, cref, contentFileMap); break; case ESM::REC_LEVI: readReferenceCollection (reader, mItemLists, cref, contentFileMap); break; case ESM::REC_LIGH: readReferenceCollection (reader, mLights, cref, contentFileMap); break; case ESM::REC_LOCK: readReferenceCollection (reader, mLockpicks, cref, contentFileMap); break; case ESM::REC_MISC: readReferenceCollection (reader, mMiscItems, cref, contentFileMap); break; case ESM::REC_NPC_: readReferenceCollection (reader, mNpcs, cref, contentFileMap); break; case ESM::REC_PROB: readReferenceCollection (reader, mProbes, cref, contentFileMap); break; case ESM::REC_REPA: readReferenceCollection (reader, mRepairs, cref, contentFileMap); break; case ESM::REC_STAT: readReferenceCollection (reader, mStatics, cref, contentFileMap); break; case ESM::REC_WEAP: readReferenceCollection (reader, mWeapons, cref, contentFileMap); break; case ESM::REC_BODY: readReferenceCollection (reader, mBodyParts, cref, contentFileMap); break; default: throw std::runtime_error ("unknown type in cell reference section"); } } while (reader.isNextSub("MVRF")) { reader.cacheSubName(); ESM::RefNum refnum; ESM::CellId movedTo; refnum.load(reader, true, "MVRF"); movedTo.load(reader); // Search for the reference. It might no longer exist if its content file was removed. SearchByRefNumVisitor visitor(refnum); forEachInternal(visitor); if (!visitor.mFound) { std::cerr << "Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)" << std::endl; continue; } MWWorld::LiveCellRefBase* movedRef = visitor.mFound; CellStore* otherCell = callback->getCellStore(movedTo); if (otherCell == NULL) { std::cerr << "Dropping moved ref tag for " << movedRef->mRef.getRefId() << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location." << std::endl; // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Restore original coordinates: movedRef->mData.setPosition(movedRef->mRef.getPosition()); continue; } if (otherCell == this) { // Should never happen unless someone's tampering with files. std::cerr << "Found invalid moved ref, ignoring" << std::endl; continue; } moveTo(MWWorld::Ptr(movedRef, this), otherCell); } updateMergedRefs(); } bool operator== (const CellStore& left, const CellStore& right) { return left.getCell()->getCellId()==right.getCell()->getCellId(); } bool operator!= (const CellStore& left, const CellStore& right) { return !(left==right); } bool CellStore::isPointConnected(const int start, const int end) const { return mPathgridGraph.isPointConnected(start, end); } std::list CellStore::aStarSearch(const int start, const int end) const { return mPathgridGraph.aStarSearch(start, end); } void CellStore::setFog(ESM::FogState *fog) { mFogState.reset(fog); } ESM::FogState* CellStore::getFog() const { return mFogState.get(); } void CellStore::respawn() { if (mState == State_Loaded) { static int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get().find("iMonthsToRespawn")->getInt(); if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn) { mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp(); for (CellRefList::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it) { Ptr ptr (&*it, this); ptr.getClass().respawn(ptr); } for (CellRefList::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) { Ptr ptr (&*it, this); ptr.getClass().respawn(ptr); } for (CellRefList::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) { Ptr ptr (&*it, this); ptr.getClass().respawn(ptr); } for (CellRefList::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it) { Ptr ptr (&*it, this); ptr.getClass().respawn(ptr); } } } } } openmw-openmw-0.38.0/apps/openmw/mwworld/cellstore.hpp000066400000000000000000000443641264522266000231020ustar00rootroot00000000000000#ifndef GAME_MWWORLD_CELLSTORE_H #define GAME_MWWORLD_CELLSTORE_H #include #include #include #include #include #include #include "livecellref.hpp" #include "cellreflist.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld #include "timestamp.hpp" #include "ptr.hpp" namespace ESM { struct CellState; struct FogState; struct CellId; } namespace MWWorld { class ESMStore; /// \brief Mutable state of a cell class CellStore { public: enum State { State_Unloaded, State_Preloaded, State_Loaded }; private: const MWWorld::ESMStore& mStore; std::vector& mReader; // Even though fog actually belongs to the player and not cells, // it makes sense to store it here since we need it once for each cell. // Note this is NULL until the cell is explored to save some memory boost::shared_ptr mFogState; const ESM::Cell *mCell; State mState; bool mHasState; std::vector mIds; float mWaterLevel; MWWorld::TimeStamp mLastRespawn; // List of refs owned by this cell CellRefList mActivators; CellRefList mPotions; CellRefList mAppas; CellRefList mArmors; CellRefList mBooks; CellRefList mClothes; CellRefList mContainers; CellRefList mCreatures; CellRefList mDoors; CellRefList mIngreds; CellRefList mCreatureLists; CellRefList mItemLists; CellRefList mLights; CellRefList mLockpicks; CellRefList mMiscItems; CellRefList mNpcs; CellRefList mProbes; CellRefList mRepairs; CellRefList mStatics; CellRefList mWeapons; CellRefList mBodyParts; typedef std::map MovedRefTracker; // References owned by a different cell that have been moved here. // MovedRefTracker mMovedHere; // References owned by this cell that have been moved to another cell. // MovedRefTracker mMovedToAnotherCell; // Merged list of ref's currently in this cell - i.e. with added refs from mMovedHere, removed refs from mMovedToAnotherCell std::vector mMergedRefs; /// Moves object from the given cell to this cell. void moveFrom(const MWWorld::Ptr& object, MWWorld::CellStore* from); /// Repopulate mMergedRefs. void updateMergedRefs(); // helper function for forEachInternal template bool forEachImp (Visitor& visitor, List& list) { for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); ++iter) { if (!isAccessible(iter->mData, iter->mRef)) continue; if (!visitor (MWWorld::Ptr(&*iter, this))) return false; } return true; } // listing only objects owned by this cell. Internal use only, you probably want to use forEach() so that moved objects are accounted for. template bool forEachInternal (Visitor& visitor) { return forEachImp (visitor, mActivators) && forEachImp (visitor, mPotions) && forEachImp (visitor, mAppas) && forEachImp (visitor, mArmors) && forEachImp (visitor, mBooks) && forEachImp (visitor, mClothes) && forEachImp (visitor, mContainers) && forEachImp (visitor, mDoors) && forEachImp (visitor, mIngreds) && forEachImp (visitor, mItemLists) && forEachImp (visitor, mLights) && forEachImp (visitor, mLockpicks) && forEachImp (visitor, mMiscItems) && forEachImp (visitor, mProbes) && forEachImp (visitor, mRepairs) && forEachImp (visitor, mStatics) && forEachImp (visitor, mWeapons) && forEachImp (visitor, mBodyParts) && forEachImp (visitor, mCreatures) && forEachImp (visitor, mNpcs) && forEachImp (visitor, mCreatureLists); } /// @note If you get a linker error here, this means the given type can not be stored in a cell. The supported types are /// defined at the bottom of this file. template CellRefList& get(); public: /// Should this reference be accessible to the outside world (i.e. to scripts / game logic)? /// Determined based on the deletion flags. By default, objects deleted by content files are never accessible; /// objects deleted by setCount(0) are still accessible *if* they came from a content file (needed for vanilla /// scripting compatibility, and the fact that objects may be "un-deleted" in the original game). static bool isAccessible(const MWWorld::RefData& refdata, const MWWorld::CellRef& cref) { return !refdata.isDeletedByContentFile() && (cref.hasContentFile() || refdata.getCount() > 0); } /// Moves object from this cell to the given cell. /// @note automatically updates given cell by calling cellToMoveTo->moveFrom(...) /// @note throws exception if cellToMoveTo == this /// @return updated MWWorld::Ptr with the new CellStore pointer set. MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); /// Make a copy of the given object and insert it into this cell. /// @note If you get a linker error here, this means the given type can not be inserted into a cell. /// The supported types are defined at the bottom of this file. template LiveCellRefBase* insert(const LiveCellRef* ref) { mHasState = true; CellRefList& list = get(); LiveCellRefBase* ret = &list.insert(*ref); updateMergedRefs(); return ret; } /// @param readerList The readers to use for loading of the cell on-demand. CellStore (const ESM::Cell *cell_, const MWWorld::ESMStore& store, std::vector& readerList); const ESM::Cell *getCell() const; State getState() const; bool hasState() const; ///< Does this cell have state that needs to be stored in a saved game file? bool hasId (const std::string& id) const; ///< May return true for deleted IDs when in preload state. Will return false, if cell is /// unloaded. /// @note Will not account for moved references which may exist in Loaded state. Use search() instead if the cell is loaded. Ptr search (const std::string& id); ///< Will return an empty Ptr if cell is not loaded. Does not check references in /// containers. /// @note Triggers CellStore hasState flag. ConstPtr searchConst (const std::string& id) const; ///< Will return an empty Ptr if cell is not loaded. Does not check references in /// containers. /// @note Does not trigger CellStore hasState flag. Ptr searchViaActorId (int id); ///< Will return an empty Ptr if cell is not loaded. float getWaterLevel() const; void setWaterLevel (float level); void setFog (ESM::FogState* fog); ///< \note Takes ownership of the pointer ESM::FogState* getFog () const; int count() const; ///< Return total number of references, including deleted ones. void load (); ///< Load references from content file. void preload (); ///< Build ID list from content file. /// Call visitor (MWWorld::Ptr) for each reference. visitor must return a bool. Returning /// false will abort the iteration. /// \note Prefer using forEachConst when possible. /// \attention This function also lists deleted (count 0) objects! /// \return Iteration completed? template bool forEach (Visitor& visitor) { if (mState != State_Loaded) return false; mHasState = true; for (unsigned int i=0; imData, mMergedRefs[i]->mRef)) continue; if (!visitor(MWWorld::Ptr(mMergedRefs[i], this))) return false; } return true; } /// Call visitor (MWWorld::ConstPtr) for each reference. visitor must return a bool. Returning /// false will abort the iteration. /// \attention This function also lists deleted (count 0) objects! /// \return Iteration completed? template bool forEachConst (Visitor& visitor) const { if (mState != State_Loaded) return false; for (unsigned int i=0; imData, mMergedRefs[i]->mRef)) continue; if (!visitor(MWWorld::ConstPtr(mMergedRefs[i], this))) return false; } return true; } /// Call visitor (ref) for each reference of given type. visitor must return a bool. Returning /// false will abort the iteration. /// \attention This function also lists deleted (count 0) objects! /// \return Iteration completed? template bool forEachType(Visitor& visitor) { if (mState != State_Loaded) return false; mHasState = true; CellRefList& list = get(); for (typename CellRefList::List::iterator it (list.mList.begin()); it!=list.mList.end(); ++it) { LiveCellRefBase* base = &*it; if (mMovedToAnotherCell.find(base) != mMovedToAnotherCell.end()) continue; if (!isAccessible(base->mData, base->mRef)) continue; if (!visitor(MWWorld::Ptr(base, this))) return false; } for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) { LiveCellRefBase* base = it->first; if (dynamic_cast*>(base)) if (!visitor(MWWorld::Ptr(base, this))) return false; } return true; } // NOTE: does not account for moved references // Should be phased out when we have const version of forEach inline const CellRefList& getReadOnlyDoors() const { return mDoors; } inline const CellRefList& getReadOnlyStatics() const { return mStatics; } bool isExterior() const; Ptr searchInContainer (const std::string& id); void loadState (const ESM::CellState& state); void saveState (ESM::CellState& state) const; void writeFog (ESM::ESMWriter& writer) const; void readFog (ESM::ESMReader& reader); void writeReferences (ESM::ESMWriter& writer) const; struct GetCellStoreCallback { public: ///@note must return NULL if the cell is not found virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0; }; /// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved references) void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap, GetCellStoreCallback* callback); void respawn (); ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. bool isPointConnected(const int start, const int end) const; std::list aStarSearch(const int start, const int end) const; private: /// Run through references and store IDs void listRefs(); void loadRefs(); void loadRef (ESM::CellRef& ref, bool deleted); ///< Make case-adjustments to \a ref and insert it into the respective container. /// /// Invalid \a ref objects are silently dropped. MWMechanics::PathgridGraph mPathgridGraph; }; template<> inline CellRefList& CellStore::get() { mHasState = true; return mActivators; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mPotions; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mAppas; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mArmors; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mBooks; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mClothes; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mContainers; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mCreatures; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mDoors; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mIngreds; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mCreatureLists; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mItemLists; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mLights; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mLockpicks; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mMiscItems; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mNpcs; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mProbes; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mRepairs; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mStatics; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mWeapons; } template<> inline CellRefList& CellStore::get() { mHasState = true; return mBodyParts; } bool operator== (const CellStore& left, const CellStore& right); bool operator!= (const CellStore& left, const CellStore& right); } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/cellvisitors.hpp000066400000000000000000000010041264522266000236100ustar00rootroot00000000000000#ifndef GAME_MWWORLD_CELLVISITORS_H #define GAME_MWWORLD_CELLVISITORS_H #include #include #include "ptr.hpp" namespace MWWorld { struct ListAndResetObjectsVisitor { std::vector mObjects; bool operator() (MWWorld::Ptr ptr) { if (ptr.getRefData().getBaseNode()) { ptr.getRefData().setBaseNode(NULL); mObjects.push_back (ptr); } return true; } }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/class.cpp000066400000000000000000000302631264522266000221770ustar00rootroot00000000000000#include "class.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" #include "ptr.hpp" #include "refdata.hpp" #include "nullaction.hpp" #include "failedaction.hpp" #include "actiontake.hpp" #include "containerstore.hpp" #include "../mwgui/tooltips.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/magiceffects.hpp" namespace MWWorld { std::map > Class::sClasses; Class::Class() {} Class::~Class() {} void Class::insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const { } void Class::insertObject(const Ptr& ptr, const std::string& mesh, MWPhysics::PhysicsSystem& physics) const { } bool Class::apply (const MWWorld::Ptr& ptr, const std::string& id, const MWWorld::Ptr& actor) const { return false; } void Class::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor) const { throw std::runtime_error ("class does not represent an actor"); } bool Class::canSell (const MWWorld::ConstPtr& item, int npcServices) const { return false; } int Class::getServices(const ConstPtr &actor) const { throw std::runtime_error ("class does not have services"); } MWMechanics::CreatureStats& Class::getCreatureStats (const Ptr& ptr) const { throw std::runtime_error ("class does not have creature stats"); } MWMechanics::NpcStats& Class::getNpcStats (const Ptr& ptr) const { throw std::runtime_error ("class does not have NPC stats"); } bool Class::hasItemHealth (const ConstPtr& ptr) const { return false; } int Class::getItemHealth(const ConstPtr &ptr) const { if (ptr.getCellRef().getCharge() == -1) return getItemMaxHealth(ptr); else return ptr.getCellRef().getCharge(); } int Class::getItemMaxHealth (const ConstPtr& ptr) const { throw std::runtime_error ("class does not have item health"); } void Class::hit(const Ptr& ptr, float attackStrength, int type) const { throw std::runtime_error("class cannot hit"); } void Class::block(const Ptr &ptr) const { throw std::runtime_error("class cannot block"); } void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, bool successful) const { throw std::runtime_error("class cannot be hit"); } boost::shared_ptr Class::activate (const Ptr& ptr, const Ptr& actor) const { return boost::shared_ptr (new NullAction); } boost::shared_ptr Class::use (const Ptr& ptr) const { return boost::shared_ptr (new NullAction); } ContainerStore& Class::getContainerStore (const Ptr& ptr) const { throw std::runtime_error ("class does not have a container store"); } InventoryStore& Class::getInventoryStore (const Ptr& ptr) const { throw std::runtime_error ("class does not have an inventory store"); } bool Class::hasInventoryStore(const Ptr &ptr) const { return false; } void Class::lock (const Ptr& ptr, int lockLevel) const { throw std::runtime_error ("class does not support locking"); } void Class::unlock (const Ptr& ptr) const { throw std::runtime_error ("class does not support unlocking"); } bool Class::canLock(const ConstPtr &ptr) const { return false; } void Class::setRemainingUsageTime (const Ptr& ptr, float duration) const { throw std::runtime_error ("class does not support time-based uses"); } float Class::getRemainingUsageTime (const ConstPtr& ptr) const { return -1; } std::string Class::getScript (const ConstPtr& ptr) const { return ""; } float Class::getSpeed (const Ptr& ptr) const { return 0; } float Class::getJump (const Ptr& ptr) const { return 0; } int Class::getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const { throw std::runtime_error ("class does not support enchanting"); } MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const { throw std::runtime_error ("movement settings not supported by class"); } osg::Vec3f Class::getRotationVector (const Ptr& ptr) const { return osg::Vec3f (0, 0, 0); } std::pair, bool> Class::getEquipmentSlots (const ConstPtr& ptr) const { return std::make_pair (std::vector(), false); } int Class::getEquipmentSkill (const ConstPtr& ptr) const { return -1; } int Class::getValue (const ConstPtr& ptr) const { throw std::logic_error ("value not supported by this class"); } float Class::getCapacity (const MWWorld::Ptr& ptr) const { throw std::runtime_error ("capacity not supported by this class"); } float Class::getWeight(const ConstPtr &ptr) const { throw std::runtime_error ("weight not supported by this class"); } float Class::getEncumbrance (const MWWorld::Ptr& ptr) const { throw std::runtime_error ("encumbrance not supported by class"); } bool Class::isEssential (const MWWorld::ConstPtr& ptr) const { return false; } float Class::getArmorRating (const MWWorld::Ptr& ptr) const { throw std::runtime_error("Class does not support armor rating"); } const Class& Class::get (const std::string& key) { if (key.empty()) throw std::logic_error ("Class::get(): attempting to get an empty key"); std::map >::const_iterator iter = sClasses.find (key); if (iter==sClasses.end()) throw std::logic_error ("Class::get(): unknown class key: " + key); return *iter->second; } bool Class::isPersistent(const ConstPtr &ptr) const { throw std::runtime_error ("class does not support persistence"); } void Class::registerClass(const std::string& key, boost::shared_ptr instance) { instance->mTypeName = key; sClasses.insert(std::make_pair(key, instance)); } std::string Class::getUpSoundId (const ConstPtr& ptr) const { throw std::runtime_error ("class does not have an up sound"); } std::string Class::getDownSoundId (const ConstPtr& ptr) const { throw std::runtime_error ("class does not have an down sound"); } std::string Class::getSoundIdFromSndGen(const Ptr &ptr, const std::string &type) const { throw std::runtime_error("class does not support soundgen look up"); } std::string Class::getInventoryIcon (const MWWorld::ConstPtr& ptr) const { throw std::runtime_error ("class does not have any inventory icon"); } MWGui::ToolTipInfo Class::getToolTipInfo (const ConstPtr& ptr, int count) const { throw std::runtime_error ("class does not have a tool tip"); } bool Class::hasToolTip (const ConstPtr& ptr) const { return false; } std::string Class::getEnchantment (const ConstPtr& ptr) const { return ""; } void Class::adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const { } std::string Class::getModel(const MWWorld::ConstPtr &ptr) const { return ""; } std::string Class::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { throw std::runtime_error ("class can't be enchanted"); } std::pair Class::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { return std::make_pair (1, ""); } void Class::adjustPosition(const MWWorld::Ptr& ptr, bool force) const { } boost::shared_ptr Class::defaultItemActivate(const Ptr &ptr, const Ptr &actor) const { if(!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return boost::shared_ptr(new NullAction()); if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Sound *sound = store.get().searchRandom("WolfItem"); boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); if(sound) action->setSound(sound->mId); return action; } boost::shared_ptr action(new ActionTake(ptr)); action->setSound(getUpSoundId(ptr)); return action; } MWWorld::Ptr Class::copyToCellImpl(const ConstPtr &ptr, CellStore &cell) const { throw std::runtime_error("unable to copy class to cell"); } MWWorld::Ptr Class::copyToCell(const ConstPtr &ptr, CellStore &cell, int count) const { Ptr newPtr = copyToCellImpl(ptr, cell); newPtr.getCellRef().unsetRefNum(); // This RefNum is only valid within the original cell of the reference newPtr.getRefData().setCount(count); return newPtr; } MWWorld::Ptr Class::copyToCell(const ConstPtr &ptr, CellStore &cell, const ESM::Position &pos, int count) const { Ptr newPtr = copyToCell(ptr, cell, count); newPtr.getRefData().setPosition(pos); return newPtr; } bool Class::isBipedal(const ConstPtr &ptr) const { return false; } bool Class::canFly(const ConstPtr &ptr) const { return false; } bool Class::canSwim(const ConstPtr &ptr) const { return false; } bool Class::canWalk(const ConstPtr &ptr) const { return false; } bool Class::isPureWaterCreature(const MWWorld::Ptr& ptr) const { return canSwim(ptr) && !canWalk(ptr); } bool Class::isMobile(const MWWorld::Ptr& ptr) const { return canSwim(ptr) || canWalk(ptr) || canFly(ptr); } int Class::getSkill(const MWWorld::Ptr& ptr, int skill) const { throw std::runtime_error("class does not support skills"); } int Class::getBloodTexture (const MWWorld::ConstPtr& ptr) const { throw std::runtime_error("class does not support gore"); } void Class::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const {} void Class::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const {} int Class::getBaseGold(const MWWorld::ConstPtr& ptr) const { throw std::runtime_error("class does not support base gold"); } bool Class::isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const { return false; } int Class::getDoorState (const MWWorld::ConstPtr &ptr) const { throw std::runtime_error("this is not a door"); } void Class::setDoorState (const MWWorld::Ptr &ptr, int state) const { throw std::runtime_error("this is not a door"); } float Class::getNormalizedEncumbrance(const Ptr &ptr) const { float capacity = getCapacity(ptr); if (capacity == 0) return 1.f; return getEncumbrance(ptr) / capacity; } std::string Class::getSound(const MWWorld::ConstPtr&) const { return std::string(); } int Class::getBaseFightRating(const ConstPtr &ptr) const { throw std::runtime_error("class does not support fight rating"); } std::string Class::getPrimaryFaction (const MWWorld::ConstPtr& ptr) const { return std::string(); } int Class::getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const { return -1; } int Class::getEffectiveArmorRating(const ConstPtr &armor, const Ptr &actor) const { throw std::runtime_error("class does not support armor ratings"); } } openmw-openmw-0.38.0/apps/openmw/mwworld/class.hpp000066400000000000000000000366201264522266000222070ustar00rootroot00000000000000#ifndef GAME_MWWORLD_CLASS_H #define GAME_MWWORLD_CLASS_H #include #include #include #include #include "ptr.hpp" namespace ESM { struct ObjectState; } namespace MWRender { class RenderingInterface; } namespace MWPhysics { class PhysicsSystem; } namespace MWMechanics { class CreatureStats; class NpcStats; struct Movement; } namespace MWGui { struct ToolTipInfo; } namespace ESM { struct Position; } namespace MWWorld { class ContainerStore; class InventoryStore; class CellStore; class Action; /// \brief Base class for referenceable esm records class Class { static std::map > sClasses; std::string mTypeName; // not implemented Class (const Class&); Class& operator= (const Class&); protected: Class(); boost::shared_ptr defaultItemActivate(const Ptr &ptr, const Ptr &actor) const; ///< Generate default action for activating inventory items virtual Ptr copyToCellImpl(const ConstPtr &ptr, CellStore &cell) const; public: virtual ~Class(); const std::string& getTypeName() const { return mTypeName; } virtual void insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObject(const Ptr& ptr, const std::string& mesh, MWPhysics::PhysicsSystem& physics) const; ///< Add reference into a cell for rendering (default implementation: don't render anything). virtual std::string getName (const ConstPtr& ptr) const = 0; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const; ///< Adjust position to stand on ground. Must be called post model load /// @param force do this even if the ptr is flying virtual MWMechanics::CreatureStats& getCreatureStats (const Ptr& ptr) const; ///< Return creature stats or throw an exception, if class does not have creature stats /// (default implementation: throw an exception) virtual bool hasToolTip (const ConstPtr& ptr) const; ///< @return true if this object has a tooltip when focused (default implementation: false) virtual MWGui::ToolTipInfo getToolTipInfo (const ConstPtr& ptr, int count) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. virtual MWMechanics::NpcStats& getNpcStats (const Ptr& ptr) const; ///< Return NPC stats or throw an exception, if class does not have NPC stats /// (default implementation: throw an exception) virtual bool hasItemHealth (const ConstPtr& ptr) const; ///< \return Item health data available? (default implementation: false) virtual int getItemHealth (const ConstPtr& ptr) const; ///< Return current item health or throw an exception if class does not have item health virtual int getItemMaxHealth (const ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exception) virtual void hit(const Ptr& ptr, float attackStrength, int type=-1) const; ///< Execute a melee hit, using the current weapon. This will check the relevant skills /// of the given attacker, and whoever is hit. /// \param attackStrength how long the attack was charged for, a value in 0-1 range. /// \param type - type of attack, one of the MWMechanics::CreatureStats::AttackType /// enums. ignored for creature attacks. /// (default implementation: throw an exception) virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; ///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is /// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the /// actor responsible for the attack, and \a successful specifies if the hit is /// successful or not. virtual void block (const Ptr& ptr) const; ///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield /// (default implementation: throw an exception) virtual boost::shared_ptr activate (const Ptr& ptr, const Ptr& actor) const; ///< Generate action for activation (default implementation: return a null action). virtual boost::shared_ptr use (const Ptr& ptr) const; ///< Generate action for using via inventory menu (default implementation: return a /// null action). virtual ContainerStore& getContainerStore (const Ptr& ptr) const; ///< Return container store or throw an exception, if class does not have a /// container store (default implementation: throw an exception) virtual InventoryStore& getInventoryStore (const Ptr& ptr) const; ///< Return inventory store or throw an exception, if class does not have a /// inventory store (default implementation: throw an exception) virtual bool hasInventoryStore (const Ptr& ptr) const; ///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false) virtual void lock (const Ptr& ptr, int lockLevel) const; ///< Lock object (default implementation: throw an exception) virtual void unlock (const Ptr& ptr) const; ///< Unlock object (default implementation: throw an exception) virtual bool canLock (const ConstPtr& ptr) const; virtual void setRemainingUsageTime (const Ptr& ptr, float duration) const; ///< Sets the remaining duration of the object, such as an equippable light /// source. (default implementation: throw an exception) virtual float getRemainingUsageTime (const ConstPtr& ptr) const; ///< Returns the remaining duration of the object, such as an equippable light /// source. (default implementation: -1, i.e. infinite) virtual std::string getScript (const ConstPtr& ptr) const; ///< Return name of the script attached to ptr (default implementation: return an empty /// string). virtual float getSpeed (const Ptr& ptr) const; ///< Return movement speed. virtual float getJump(const MWWorld::Ptr &ptr) const; ///< Return jump velocity (not accounting for movement) virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const; ///< Return desired movement. virtual osg::Vec3f getRotationVector (const Ptr& ptr) const; ///< Return desired rotations, as euler angles. virtual std::pair, bool> getEquipmentSlots (const ConstPtr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? /// /// Default implementation: return (empty vector, false). virtual int getEquipmentSkill (const ConstPtr& ptr) const; /// Return the index of the skill this item corresponds to when equipped or -1, if there is /// no such skill. /// (default implementation: return -1) virtual int getValue (const ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. /// (default implementation: throws an exception) virtual float getCapacity (const MWWorld::Ptr& ptr) const; ///< Return total weight that fits into the object. Throws an exception, if the object can't /// hold other objects. /// (default implementation: throws an exception) virtual float getEncumbrance (const MWWorld::Ptr& ptr) const; ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. /// (default implementation: throws an exception) virtual float getNormalizedEncumbrance (const MWWorld::Ptr& ptr) const; ///< Returns encumbrance re-scaled to capacity virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id, const MWWorld::Ptr& actor) const; ///< Apply \a id on \a ptr. /// \param actor Actor that is resposible for the ID being applied to \a ptr. /// \return Any effect? /// /// (default implementation: ignore and return false) virtual void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor=1.f) const; ///< Inform actor \a ptr that a skill use has succeeded. /// /// (default implementations: throws an exception) virtual bool isEssential (const MWWorld::ConstPtr& ptr) const; ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) /// /// (default implementation: return false) virtual std::string getUpSoundId (const ConstPtr& ptr) const; ///< Return the up sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) virtual std::string getDownSoundId (const ConstPtr& ptr) const; ///< Return the down sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) virtual std::string getSoundIdFromSndGen(const Ptr &ptr, const std::string &type) const; ///< Returns the sound ID for \a ptr of the given soundgen \a type. virtual float getArmorRating (const MWWorld::Ptr& ptr) const; ///< @return combined armor rating of this actor virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const; ///< Return name of inventory icon. virtual std::string getEnchantment (const MWWorld::ConstPtr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string /// (default implementation: return empty string) virtual int getEnchantmentPoints (const MWWorld::ConstPtr& ptr) const; ///< @return the number of enchantment points available for possible enchanting virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const; /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh virtual bool canSell (const MWWorld::ConstPtr& item, int npcServices) const; ///< Determine whether or not \a item can be sold to an npc with the given \a npcServices virtual int getServices (const MWWorld::ConstPtr& actor) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. virtual std::pair canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const; ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. /// Second item in the pair specifies the error message virtual float getWeight (const MWWorld::ConstPtr& ptr) const; virtual bool isPersistent (const MWWorld::ConstPtr& ptr) const; virtual bool isKey (const MWWorld::ConstPtr& ptr) const { return false; } virtual bool isGold(const MWWorld::ConstPtr& ptr) const { return false; } /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; virtual Ptr copyToCell(const ConstPtr &ptr, CellStore &cell, int count) const; virtual Ptr copyToCell(const ConstPtr &ptr, CellStore &cell, const ESM::Position &pos, int count) const; virtual bool isActor() const { return false; } virtual bool isNpc() const { return false; } virtual bool isBipedal(const MWWorld::ConstPtr& ptr) const; virtual bool canFly(const MWWorld::ConstPtr& ptr) const; virtual bool canSwim(const MWWorld::ConstPtr& ptr) const; virtual bool canWalk(const MWWorld::ConstPtr& ptr) const; bool isPureWaterCreature(const MWWorld::Ptr& ptr) const; bool isMobile(const MWWorld::Ptr& ptr) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. virtual void writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. static void registerClass (const std::string& key, boost::shared_ptr instance); virtual int getBaseGold(const MWWorld::ConstPtr& ptr) const; virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const; /// 0 = nothing, 1 = opening, 2 = closing virtual int getDoorState (const MWWorld::ConstPtr &ptr) const; /// This does not actually cause the door to move. Use World::activateDoor instead. virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; virtual void respawn (const MWWorld::Ptr& ptr) const {} virtual void restock (const MWWorld::Ptr& ptr) const {} /// Returns sound id virtual std::string getSound(const MWWorld::ConstPtr& ptr) const; virtual int getBaseFightRating (const MWWorld::ConstPtr& ptr) const; virtual std::string getPrimaryFaction (const MWWorld::ConstPtr& ptr) const; virtual int getPrimaryFactionRank (const MWWorld::ConstPtr& ptr) const; /// Get the effective armor rating, factoring in the actor's skills, for the given armor. virtual int getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/containerstore.cpp000066400000000000000000001165371264522266000241420ustar00rootroot00000000000000#include "containerstore.hpp" #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/levelledlist.hpp" #include "../mwmechanics/actorutil.hpp" #include "manualref.hpp" #include "refdata.hpp" #include "class.hpp" #include "localscripts.hpp" #include "player.hpp" namespace { template float getTotalWeight (const MWWorld::CellRefList& cellRefList) { float sum = 0; for (typename MWWorld::CellRefList::List::const_iterator iter ( cellRefList.mList.begin()); iter!=cellRefList.mList.end(); ++iter) { if (iter->mData.getCount()>0) sum += iter->mData.getCount()*iter->mBase->mData.mWeight; } return sum; } template MWWorld::Ptr searchId (MWWorld::CellRefList& list, const std::string& id, MWWorld::ContainerStore *store) { std::string id2 = Misc::StringUtils::lowerCase (id); for (typename MWWorld::CellRefList::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); ++iter) { if (Misc::StringUtils::ciEqual(iter->mBase->mId, id2)) { MWWorld::Ptr ptr (&*iter, 0); ptr.setContainerStore (store); return ptr; } } return MWWorld::Ptr(); } } template MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList& collection, const ESM::ObjectState& state) { if (!LiveCellRef::checkState (state)) return ContainerStoreIterator (this); // not valid anymore with current content files -> skip const T *record = MWBase::Environment::get().getWorld()->getStore(). get().search (state.mRef.mRefID); if (!record) return ContainerStoreIterator (this); LiveCellRef ref (record); ref.load (state); collection.mList.push_back (ref); return ContainerStoreIterator (this, --collection.mList.end()); } void MWWorld::ContainerStore::storeEquipmentState(const MWWorld::LiveCellRefBase &ref, int index, ESM::InventoryState &inventory) const { } void MWWorld::ContainerStore::readEquipmentState(const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState &inventory) { } template void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::ObjectState& state) const { ref.save (state); } template void MWWorld::ContainerStore::storeStates (const CellRefList& collection, ESM::InventoryState& inventory, int& index, bool equipable) const { for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { if (iter->mData.getCount() == 0) continue; ESM::ObjectState state; storeState (*iter, state); if (equipable) storeEquipmentState(*iter, index, inventory); inventory.mItems.push_back (state); ++index; } } const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; MWWorld::ContainerStore::ContainerStore() : mCachedWeight (0), mWeightUpToDate (false) {} MWWorld::ContainerStore::~ContainerStore() {} MWWorld::ContainerStoreIterator MWWorld::ContainerStore::begin (int mask) { return ContainerStoreIterator (mask, this); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() { return ContainerStoreIterator (this); } int MWWorld::ContainerStore::count(const std::string &id) { int total=0; for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter) if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id)) total += iter->getRefData().getCount(); return total; } void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container) { if (ptr.getRefData().getCount() <= 1) return; MWWorld::ContainerStoreIterator it = addNewStack(ptr, ptr.getRefData().getCount()-1); const std::string script = it->getClass().getScript(*it); if (!script.empty()) MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it); remove(ptr, ptr.getRefData().getCount()-1, container); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item) { MWWorld::ContainerStoreIterator retval = end(); for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) { if (item == *iter) { retval = iter; break; } } if (retval == end()) throw std::runtime_error("item is not from this container"); for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) { if (stacks(*iter, item)) { iter->getRefData().setCount(iter->getRefData().getCount() + item.getRefData().getCount()); item.getRefData().setCount(0); retval = iter; break; } } return retval; } bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) { const MWWorld::Class& cls1 = ptr1.getClass(); const MWWorld::Class& cls2 = ptr2.getClass(); if (!Misc::StringUtils::ciEqual(ptr1.getCellRef().getRefId(), ptr2.getCellRef().getRefId())) return false; // If it has an enchantment, don't stack when some of the charge is already used if (!ptr1.getClass().getEnchantment(ptr1).empty()) { const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( ptr1.getClass().getEnchantment(ptr1)); float maxCharge = static_cast(enchantment->mData.mCharge); float enchantCharge1 = ptr1.getCellRef().getEnchantmentCharge() == -1 ? maxCharge : ptr1.getCellRef().getEnchantmentCharge(); float enchantCharge2 = ptr2.getCellRef().getEnchantmentCharge() == -1 ? maxCharge : ptr2.getCellRef().getEnchantmentCharge(); if (enchantCharge1 != maxCharge || enchantCharge2 != maxCharge) return false; } return ptr1 != ptr2 // an item never stacks onto itself && ptr1.getCellRef().getOwner() == ptr2.getCellRef().getOwner() && ptr1.getCellRef().getSoul() == ptr2.getCellRef().getSoul() && ptr1.getClass().getRemainingUsageTime(ptr1) == ptr2.getClass().getRemainingUsageTime(ptr2) && cls1.getScript(ptr1) == cls2.getScript(ptr2) // item that is already partly used up never stacks && (!cls1.hasItemHealth(ptr1) || ( cls1.getItemHealth(ptr1) == cls1.getItemMaxHealth(ptr1) && cls2.getItemHealth(ptr2) == cls2.getItemMaxHealth(ptr2))); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count); // a bit pointless to set owner for the player if (actorPtr != MWMechanics::getPlayer()) return add(ref.getPtr(), count, actorPtr, true); else return add(ref.getPtr(), count, actorPtr, false); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) { Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::ContainerStoreIterator it = end(); // HACK: Set owner on the original item, then reset it after we have copied it // If we set the owner on the copied item, it would not stack correctly... std::string oldOwner = itemPtr.getCellRef().getOwner(); if (!setOwner || actorPtr == MWMechanics::getPlayer()) // No point in setting owner to the player - NPCs will not respect this anyway { itemPtr.getCellRef().setOwner(""); } else { itemPtr.getCellRef().setOwner(actorPtr.getCellRef().getRefId()); } it = addImp(itemPtr, count); itemPtr.getCellRef().setOwner(oldOwner); // The copy of the original item we just made MWWorld::Ptr item = *it; // we may have copied an item from the world, so reset a few things first item.getRefData().setBaseNode(NULL); // Especially important, otherwise scripts on the item could think that it's actually in a cell ESM::Position pos; pos.rot[0] = 0; pos.rot[1] = 0; pos.rot[2] = 0; pos.pos[0] = 0; pos.pos[1] = 0; pos.pos[2] = 0; item.getCellRef().setPosition(pos); // reset ownership stuff, owner was already handled above item.getCellRef().resetGlobalVariable(); item.getCellRef().setFaction(""); item.getCellRef().setFactionRank(-1); // must reset the RefNum on the copied item, so that the RefNum on the original item stays unique // maybe we should do this in the copy constructor instead? item.getCellRef().unsetRefNum(); // destroy link to content file std::string script = item.getClass().getScript(item); if(script != "") { if (actorPtr == player) { // Items in player's inventory have cell set to 0, so their scripts will never be removed item.mCell = 0; } else { // Set mCell to the cell of the container/actor, so that the scripts are removed properly when // the cell of the container/actor goes inactive item.mCell = actorPtr.getCell(); } item.mContainerStore = this; MWBase::Environment::get().getWorld()->getLocalScripts().add(script, item); // Set OnPCAdd special variable, if it is declared // Make sure to do this *after* we have added the script to LocalScripts if (actorPtr == player) item.getRefData().getLocals().setVarByInt(script, "onpcadd", 1); } return it; } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, int count) { int type = getType(ptr); const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); // gold needs special handling: when it is inserted into a container, the base object automatically becomes Gold_001 // this ensures that gold piles of different sizes stack with each other (also, several scripts rely on Gold_001 for detecting player gold) if(ptr.getClass().isGold(ptr)) { int realCount = count * ptr.getClass().getValue(ptr); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { if (Misc::StringUtils::ciEqual((*iter).getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId)) { iter->getRefData().setCount(iter->getRefData().getCount() + realCount); flagAsModified(); return iter; } } MWWorld::ManualRef ref(esmStore, MWWorld::ContainerStore::sGoldId, realCount); return addNewStack(ref.getPtr(), realCount); } // determine whether to stack or not for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { if (stacks(*iter, ptr)) { // stack iter->getRefData().setCount( iter->getRefData().getCount() + count ); flagAsModified(); return iter; } } // if we got here, this means no stacking return addNewStack(ptr, count); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const ConstPtr& ptr, int count) { ContainerStoreIterator it = begin(); switch (getType(ptr)) { case Type_Potion: potions.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --potions.mList.end()); break; case Type_Apparatus: appas.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --appas.mList.end()); break; case Type_Armor: armors.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --armors.mList.end()); break; case Type_Book: books.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --books.mList.end()); break; case Type_Clothing: clothes.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --clothes.mList.end()); break; case Type_Ingredient: ingreds.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --ingreds.mList.end()); break; case Type_Light: lights.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --lights.mList.end()); break; case Type_Lockpick: lockpicks.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --lockpicks.mList.end()); break; case Type_Miscellaneous: miscItems.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --miscItems.mList.end()); break; case Type_Probe: probes.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --probes.mList.end()); break; case Type_Repair: repairs.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --repairs.mList.end()); break; case Type_Weapon: weapons.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --weapons.mList.end()); break; } it->getRefData().setCount(count); flagAsModified(); return it; } int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor) { int toRemove = count; for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter) if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), itemId)) toRemove -= remove(*iter, toRemove, actor); flagAsModified(); // number of removed items return count - toRemove; } int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor) { assert(this == item.getContainerStore()); int toRemove = count; RefData& itemRef = item.getRefData(); if (itemRef.getCount() <= toRemove) { toRemove -= itemRef.getCount(); itemRef.setCount(0); } else { itemRef.setCount(itemRef.getCount() - toRemove); toRemove = 0; } flagAsModified(); // number of removed items return count - toRemove; } void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner) { for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); ++iter) { std::string id = Misc::StringUtils::lowerCase(iter->mItem.toString()); addInitialItem(id, owner, iter->mCount); } flagAsModified(); } void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel, const std::string& levItem) { if (count == 0) return; //Don't restock with nothing. try { ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) { const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; if (topLevel && std::abs(count) > 1 && levItem->mFlags & ESM::ItemLevList::Each) { for (int i=0; i 0 ? 1 : -1, true, levItem->mId); return; } else { std::string id = MWMechanics::getLevelledItem(ref.getPtr().get()->mBase, false); if (id.empty()) return; addInitialItem(id, owner, count, false, levItem->mId); } } else { // A negative count indicates restocking items // For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks if (!levItem.empty() && count < 0) { //If there is no item in map, insert it std::map, int>::iterator itemInMap = mLevelledItemMap.insert(std::make_pair(std::make_pair(id, levItem), 0)).first; //Update spawned count itemInMap->second += std::abs(count); } count = std::abs(count); ref.getPtr().getCellRef().setOwner(owner); addImp (ref.getPtr(), count); } } catch (const std::exception& e) { std::cerr << "Error in MWWorld::ContainerStore::addInitialItem: " << e.what() << std::endl; } } void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner) { //allowedForReplace - Holds information about how many items from the list were not sold; // Hence, tells us how many items we don't need to restock. //allowedForReplace[list] <- How many items we should generate(how many of these were sold) std::map allowedForReplace; //Check which lists need restocking: for (std::map, int>::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end();) { int spawnedCount = it->second; //How many items should be in shop originally int itemCount = count(it->first.first); //How many items are there in shop now //If something was not sold if(itemCount >= spawnedCount) { const std::string& parent = it->first.second; // Security check for old saves: //If item is imported from old save(doesn't have an parent) and wasn't sold if(parent == "") { //Remove it, from shop, remove(it->first.first, itemCount, ptr);//ptr is the NPC //And remove it from map, so that when we restock, the new item will have proper parent. mLevelledItemMap.erase(it++); continue; } //Create the entry if it does not exist yet std::map::iterator listInMap = allowedForReplace.insert( std::make_pair(it->first.second, 0)).first; //And signal that we don't need to restock item from this list listInMap->second += std::abs(itemCount); } //If every of the item was sold else if (itemCount == 0) { mLevelledItemMap.erase(it++); continue; } //If some was sold, but some remain else { //Create entry if it does not exist yet std::map::iterator listInMap = allowedForReplace.insert( std::make_pair(it->first.second, 0)).first; //And signal that we don't need to restock all items from this list listInMap->second += std::abs(itemCount); //And update itemCount so we don't mistake it next time. it->second = itemCount; } ++it; } //Restock: //For every item that NPC could have for (std::vector::const_iterator it = items.mList.begin(); it != items.mList.end(); ++it) { //If he shouldn't have it restocked, don't restock it. if (it->mCount >= 0) continue; std::string itemOrList = Misc::StringUtils::lowerCase(it->mItem.toString()); //If it's levelled list, restock if there's need to do so. if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mItem.toString())) { std::map::iterator listInMap = allowedForReplace.find(itemOrList); int restockNum = std::abs(it->mCount); //If we know we must restock less, take it into account if(listInMap != allowedForReplace.end()) restockNum -= std::min(restockNum, listInMap->second); //restock addInitialItem(itemOrList, owner, restockNum, true); } else { //Restocking static item - just restock to the max count int currentCount = count(itemOrList); if (currentCount < std::abs(it->mCount)) addInitialItem(itemOrList, owner, std::abs(it->mCount) - currentCount, true); } } flagAsModified(); } void MWWorld::ContainerStore::clear() { for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) iter->getRefData().setCount (0); flagAsModified(); } void MWWorld::ContainerStore::flagAsModified() { mWeightUpToDate = false; } float MWWorld::ContainerStore::getWeight() const { if (!mWeightUpToDate) { mCachedWeight = 0; mCachedWeight += getTotalWeight (potions); mCachedWeight += getTotalWeight (appas); mCachedWeight += getTotalWeight (armors); mCachedWeight += getTotalWeight (books); mCachedWeight += getTotalWeight (clothes); mCachedWeight += getTotalWeight (ingreds); mCachedWeight += getTotalWeight (lights); mCachedWeight += getTotalWeight (lockpicks); mCachedWeight += getTotalWeight (miscItems); mCachedWeight += getTotalWeight (probes); mCachedWeight += getTotalWeight (repairs); mCachedWeight += getTotalWeight (weapons); mWeightUpToDate = true; } return mCachedWeight; } int MWWorld::ContainerStore::getType (const ConstPtr& ptr) { if (ptr.isEmpty()) throw std::runtime_error ("can't put a non-existent object into a container"); if (ptr.getTypeName()==typeid (ESM::Potion).name()) return Type_Potion; if (ptr.getTypeName()==typeid (ESM::Apparatus).name()) return Type_Apparatus; if (ptr.getTypeName()==typeid (ESM::Armor).name()) return Type_Armor; if (ptr.getTypeName()==typeid (ESM::Book).name()) return Type_Book; if (ptr.getTypeName()==typeid (ESM::Clothing).name()) return Type_Clothing; if (ptr.getTypeName()==typeid (ESM::Ingredient).name()) return Type_Ingredient; if (ptr.getTypeName()==typeid (ESM::Light).name()) return Type_Light; if (ptr.getTypeName()==typeid (ESM::Lockpick).name()) return Type_Lockpick; if (ptr.getTypeName()==typeid (ESM::Miscellaneous).name()) return Type_Miscellaneous; if (ptr.getTypeName()==typeid (ESM::Probe).name()) return Type_Probe; if (ptr.getTypeName()==typeid (ESM::Repair).name()) return Type_Repair; if (ptr.getTypeName()==typeid (ESM::Weapon).name()) return Type_Weapon; throw std::runtime_error ( "Object of type " + ptr.getTypeName() + " can not be placed into a container"); } MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id) { { Ptr ptr = searchId (potions, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (appas, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (armors, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (books, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (clothes, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (ingreds, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (lights, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (lockpicks, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (miscItems, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (probes, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (repairs, id, this); if (!ptr.isEmpty()) return ptr; } { Ptr ptr = searchId (weapons, id, this); if (!ptr.isEmpty()) return ptr; } return Ptr(); } void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const { state.mItems.clear(); int index = 0; storeStates (potions, state, index); storeStates (appas, state, index); storeStates (armors, state, index, true); storeStates (books, state, index, true); // not equipable as such, but for selectedEnchantItem storeStates (clothes, state, index, true); storeStates (ingreds, state, index); storeStates (lockpicks, state, index, true); storeStates (miscItems, state, index); storeStates (probes, state, index, true); storeStates (repairs, state, index); storeStates (weapons, state, index, true); storeStates (lights, state, index, true); state.mLevelledItemMap = mLevelledItemMap; } void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory) { clear(); int index = 0; for (std::vector::const_iterator iter (inventory.mItems.begin()); iter!=inventory.mItems.end(); ++iter) { const ESM::ObjectState& state = *iter; int type = MWBase::Environment::get().getWorld()->getStore().find(state.mRef.mRefID); int thisIndex = index++; switch (type) { case ESM::REC_ALCH: getState (potions, state); break; case ESM::REC_APPA: getState (appas, state); break; case ESM::REC_ARMO: readEquipmentState (getState (armors, state), thisIndex, inventory); break; case ESM::REC_BOOK: readEquipmentState (getState (books, state), thisIndex, inventory); break; // not equipable as such, but for selectedEnchantItem case ESM::REC_CLOT: readEquipmentState (getState (clothes, state), thisIndex, inventory); break; case ESM::REC_INGR: getState (ingreds, state); break; case ESM::REC_LOCK: readEquipmentState (getState (lockpicks, state), thisIndex, inventory); break; case ESM::REC_MISC: getState (miscItems, state); break; case ESM::REC_PROB: readEquipmentState (getState (probes, state), thisIndex, inventory); break; case ESM::REC_REPA: getState (repairs, state); break; case ESM::REC_WEAP: readEquipmentState (getState (weapons, state), thisIndex, inventory); break; case ESM::REC_LIGH: readEquipmentState (getState (lights, state), thisIndex, inventory); break; case 0: std::cerr << "Dropping reference to '" << state.mRef.mRefID << "' (object no longer exists)" << std::endl; break; default: std::cerr << "Invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; break; } } mLevelledItemMap = inventory.mLevelledItemMap; } MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container) : mType (-1), mMask (0), mContainer (container) {} MWWorld::ContainerStoreIterator::ContainerStoreIterator (int mask, ContainerStore *container) : mType (0), mMask (mask), mContainer (container) { nextType(); if (mType==-1 || (**this).getRefData().getCount()) return; ++*this; } MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Potion), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mPotion(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Apparatus), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mApparatus(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Armor), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mArmor(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Book), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mBook(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Clothing), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mClothing(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Ingredient), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mIngredient(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Light), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLight(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Lockpick), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLockpick(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Miscellaneous), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mMiscellaneous(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Probe), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mProbe(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Repair), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mRepair(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Weapon), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mWeapon(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator( const ContainerStoreIterator& src ) { copy(src); } void MWWorld::ContainerStoreIterator::incType() { if (mType==0) mType = 1; else if (mType!=-1) { mType <<= 1; if (mType>ContainerStore::Type_Last) mType = -1; } } void MWWorld::ContainerStoreIterator::nextType() { while (mType!=-1) { incType(); if ((mType & mMask) && mType>0) if (resetIterator()) break; } } bool MWWorld::ContainerStoreIterator::resetIterator() { switch (mType) { case ContainerStore::Type_Potion: mPotion = mContainer->potions.mList.begin(); return mPotion!=mContainer->potions.mList.end(); case ContainerStore::Type_Apparatus: mApparatus = mContainer->appas.mList.begin(); return mApparatus!=mContainer->appas.mList.end(); case ContainerStore::Type_Armor: mArmor = mContainer->armors.mList.begin(); return mArmor!=mContainer->armors.mList.end(); case ContainerStore::Type_Book: mBook = mContainer->books.mList.begin(); return mBook!=mContainer->books.mList.end(); case ContainerStore::Type_Clothing: mClothing = mContainer->clothes.mList.begin(); return mClothing!=mContainer->clothes.mList.end(); case ContainerStore::Type_Ingredient: mIngredient = mContainer->ingreds.mList.begin(); return mIngredient!=mContainer->ingreds.mList.end(); case ContainerStore::Type_Light: mLight = mContainer->lights.mList.begin(); return mLight!=mContainer->lights.mList.end(); case ContainerStore::Type_Lockpick: mLockpick = mContainer->lockpicks.mList.begin(); return mLockpick!=mContainer->lockpicks.mList.end(); case ContainerStore::Type_Miscellaneous: mMiscellaneous = mContainer->miscItems.mList.begin(); return mMiscellaneous!=mContainer->miscItems.mList.end(); case ContainerStore::Type_Probe: mProbe = mContainer->probes.mList.begin(); return mProbe!=mContainer->probes.mList.end(); case ContainerStore::Type_Repair: mRepair = mContainer->repairs.mList.begin(); return mRepair!=mContainer->repairs.mList.end(); case ContainerStore::Type_Weapon: mWeapon = mContainer->weapons.mList.begin(); return mWeapon!=mContainer->weapons.mList.end(); } return false; } bool MWWorld::ContainerStoreIterator::incIterator() { switch (mType) { case ContainerStore::Type_Potion: ++mPotion; return mPotion==mContainer->potions.mList.end(); case ContainerStore::Type_Apparatus: ++mApparatus; return mApparatus==mContainer->appas.mList.end(); case ContainerStore::Type_Armor: ++mArmor; return mArmor==mContainer->armors.mList.end(); case ContainerStore::Type_Book: ++mBook; return mBook==mContainer->books.mList.end(); case ContainerStore::Type_Clothing: ++mClothing; return mClothing==mContainer->clothes.mList.end(); case ContainerStore::Type_Ingredient: ++mIngredient; return mIngredient==mContainer->ingreds.mList.end(); case ContainerStore::Type_Light: ++mLight; return mLight==mContainer->lights.mList.end(); case ContainerStore::Type_Lockpick: ++mLockpick; return mLockpick==mContainer->lockpicks.mList.end(); case ContainerStore::Type_Miscellaneous: ++mMiscellaneous; return mMiscellaneous==mContainer->miscItems.mList.end(); case ContainerStore::Type_Probe: ++mProbe; return mProbe==mContainer->probes.mList.end(); case ContainerStore::Type_Repair: ++mRepair; return mRepair==mContainer->repairs.mList.end(); case ContainerStore::Type_Weapon: ++mWeapon; return mWeapon==mContainer->weapons.mList.end(); } return true; } MWWorld::Ptr *MWWorld::ContainerStoreIterator::operator->() const { mPtr = **this; return &mPtr; } MWWorld::Ptr MWWorld::ContainerStoreIterator::operator*() const { Ptr ptr; switch (mType) { case ContainerStore::Type_Potion: ptr = MWWorld::Ptr (&*mPotion, 0); break; case ContainerStore::Type_Apparatus: ptr = MWWorld::Ptr (&*mApparatus, 0); break; case ContainerStore::Type_Armor: ptr = MWWorld::Ptr (&*mArmor, 0); break; case ContainerStore::Type_Book: ptr = MWWorld::Ptr (&*mBook, 0); break; case ContainerStore::Type_Clothing: ptr = MWWorld::Ptr (&*mClothing, 0); break; case ContainerStore::Type_Ingredient: ptr = MWWorld::Ptr (&*mIngredient, 0); break; case ContainerStore::Type_Light: ptr = MWWorld::Ptr (&*mLight, 0); break; case ContainerStore::Type_Lockpick: ptr = MWWorld::Ptr (&*mLockpick, 0); break; case ContainerStore::Type_Miscellaneous: ptr = MWWorld::Ptr (&*mMiscellaneous, 0); break; case ContainerStore::Type_Probe: ptr = MWWorld::Ptr (&*mProbe, 0); break; case ContainerStore::Type_Repair: ptr = MWWorld::Ptr (&*mRepair, 0); break; case ContainerStore::Type_Weapon: ptr = MWWorld::Ptr (&*mWeapon, 0); break; } if (ptr.isEmpty()) throw std::runtime_error ("invalid iterator"); ptr.setContainerStore (mContainer); return ptr; } MWWorld::ContainerStoreIterator& MWWorld::ContainerStoreIterator::operator++() { do { if (incIterator()) nextType(); } while (mType!=-1 && !(**this).getRefData().getCount()); return *this; } MWWorld::ContainerStoreIterator MWWorld::ContainerStoreIterator::operator++ (int) { ContainerStoreIterator iter (*this); ++*this; return iter; } bool MWWorld::ContainerStoreIterator::isEqual (const ContainerStoreIterator& iter) const { if (mContainer!=iter.mContainer) return false; if (mType!=iter.mType) return false; switch (mType) { case ContainerStore::Type_Potion: return mPotion==iter.mPotion; case ContainerStore::Type_Apparatus: return mApparatus==iter.mApparatus; case ContainerStore::Type_Armor: return mArmor==iter.mArmor; case ContainerStore::Type_Book: return mBook==iter.mBook; case ContainerStore::Type_Clothing: return mClothing==iter.mClothing; case ContainerStore::Type_Ingredient: return mIngredient==iter.mIngredient; case ContainerStore::Type_Light: return mLight==iter.mLight; case ContainerStore::Type_Lockpick: return mLockpick==iter.mLockpick; case ContainerStore::Type_Miscellaneous: return mMiscellaneous==iter.mMiscellaneous; case ContainerStore::Type_Probe: return mProbe==iter.mProbe; case ContainerStore::Type_Repair: return mRepair==iter.mRepair; case ContainerStore::Type_Weapon: return mWeapon==iter.mWeapon; case -1: return true; } return false; } int MWWorld::ContainerStoreIterator::getType() const { return mType; } const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStore() const { return mContainer; } void MWWorld::ContainerStoreIterator::copy(const ContainerStoreIterator& src) { mType = src.mType; mMask = src.mMask; mContainer = src.mContainer; mPtr = src.mPtr; switch (mType) { case MWWorld::ContainerStore::Type_Potion: mPotion = src.mPotion; break; case MWWorld::ContainerStore::Type_Apparatus: mApparatus = src.mApparatus; break; case MWWorld::ContainerStore::Type_Armor: mArmor = src.mArmor; break; case MWWorld::ContainerStore::Type_Book: mBook = src.mBook; break; case MWWorld::ContainerStore::Type_Clothing: mClothing = src.mClothing; break; case MWWorld::ContainerStore::Type_Ingredient: mIngredient = src.mIngredient; break; case MWWorld::ContainerStore::Type_Light: mLight = src.mLight; break; case MWWorld::ContainerStore::Type_Lockpick: mLockpick = src.mLockpick; break; case MWWorld::ContainerStore::Type_Miscellaneous: mMiscellaneous = src.mMiscellaneous; break; case MWWorld::ContainerStore::Type_Probe: mProbe = src.mProbe; break; case MWWorld::ContainerStore::Type_Repair: mRepair = src.mRepair; break; case MWWorld::ContainerStore::Type_Weapon: mWeapon = src.mWeapon; break; case -1: break; default: assert(0); } } MWWorld::ContainerStoreIterator& MWWorld::ContainerStoreIterator::operator=( const ContainerStoreIterator& rhs ) { if (this!=&rhs) { copy(rhs); } return *this; } bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right) { return left.isEqual (right); } bool MWWorld::operator!= (const ContainerStoreIterator& left, const ContainerStoreIterator& right) { return !(left==right); } openmw-openmw-0.38.0/apps/openmw/mwworld/containerstore.hpp000066400000000000000000000264531264522266000241440ustar00rootroot00000000000000#ifndef GAME_MWWORLD_CONTAINERSTORE_H #define GAME_MWWORLD_CONTAINERSTORE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ptr.hpp" #include "cellreflist.hpp" namespace ESM { struct InventoryList; struct InventoryState; } namespace MWWorld { class ContainerStoreIterator; class ContainerStore { public: static const int Type_Potion = 0x0001; static const int Type_Apparatus = 0x0002; static const int Type_Armor = 0x0004; static const int Type_Book = 0x0008; static const int Type_Clothing = 0x0010; static const int Type_Ingredient = 0x0020; static const int Type_Light = 0x0040; static const int Type_Lockpick = 0x0080; static const int Type_Miscellaneous = 0x0100; static const int Type_Probe = 0x0200; static const int Type_Repair = 0x0400; static const int Type_Weapon = 0x0800; static const int Type_Last = Type_Weapon; static const int Type_All = 0xffff; static const std::string sGoldId; private: MWWorld::CellRefList potions; MWWorld::CellRefList appas; MWWorld::CellRefList armors; MWWorld::CellRefList books; MWWorld::CellRefList clothes; MWWorld::CellRefList ingreds; MWWorld::CellRefList lights; MWWorld::CellRefList lockpicks; MWWorld::CellRefList miscItems; MWWorld::CellRefList probes; MWWorld::CellRefList repairs; MWWorld::CellRefList weapons; std::map, int> mLevelledItemMap; ///< Stores result of levelled item spawns. <(refId, spawningGroup), count> /// This is used to restock levelled items(s) if the old item was sold. mutable float mCachedWeight; mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr, int count); void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = ""); template ContainerStoreIterator getState (CellRefList& collection, const ESM::ObjectState& state); template void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; template void storeStates (const CellRefList& collection, ESM::InventoryState& inventory, int& index, bool equipable = false) const; virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory); public: ContainerStore(); virtual ~ContainerStore(); virtual ContainerStore* clone() { return new ContainerStore(*this); } ContainerStoreIterator begin (int mask = Type_All); ContainerStoreIterator end(); virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// /// \note The item pointed to is not required to exist beyond this function call. /// /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! /// /// @param setOwner Set the owner of the added item to \a actorPtr? If false, the owner is reset to "". /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. ContainerStoreIterator add(const std::string& id, int count, const Ptr& actorPtr); ///< Utility to construct a ManualRef and call add(ptr, count, actorPtr, true) int remove(const std::string& itemId, int count, const Ptr& actor); ///< Remove \a count item(s) designated by \a itemId from this container. /// /// @return the number of items actually removed virtual int remove(const Ptr& item, int count, const Ptr& actor); ///< Remove \a count item(s) designated by \a item from this inventory. /// /// @return the number of items actually removed void unstack (const Ptr& ptr, const Ptr& container); ///< Unstack an item in this container. The item's count will be set to 1, then a new stack will be added with (origCount-1). MWWorld::ContainerStoreIterator restack (const MWWorld::Ptr& item); ///< Attempt to re-stack an item in this container. /// If a compatible stack is found, the item's count is added to that stack, then the original is deleted. /// @return If the item was stacked, return the stack, otherwise return the old (untouched) item. /// @return How many items with refID \a id are in this container? int count (const std::string& id); protected: ContainerStoreIterator addNewStack (const ConstPtr& ptr, int count); ///< Add the item to this container (do not try to stack it onto existing items) virtual void flagAsModified(); public: virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2); ///< @return true if the two specified objects can stack with each other void fill (const ESM::InventoryList& items, const std::string& owner); ///< Insert items into *this. void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner); virtual void clear(); ///< Empty container. float getWeight() const; ///< Return total weight of the items contained in *this. static int getType (const ConstPtr& ptr); ///< This function throws an exception, if ptr does not point to an object, that can be /// put into a container. Ptr search (const std::string& id); virtual void writeState (ESM::InventoryState& state) const; virtual void readState (const ESM::InventoryState& state); friend class ContainerStoreIterator; }; /// \brief Iteration over a subset of objects in a ContainerStore /// /// \note The iterator will automatically skip over deleted objects. class ContainerStoreIterator : public std::iterator { int mType; int mMask; ContainerStore *mContainer; mutable Ptr mPtr; MWWorld::CellRefList::List::iterator mPotion; MWWorld::CellRefList::List::iterator mApparatus; MWWorld::CellRefList::List::iterator mArmor; MWWorld::CellRefList::List::iterator mBook; MWWorld::CellRefList::List::iterator mClothing; MWWorld::CellRefList::List::iterator mIngredient; MWWorld::CellRefList::List::iterator mLight; MWWorld::CellRefList::List::iterator mLockpick; MWWorld::CellRefList::List::iterator mMiscellaneous; MWWorld::CellRefList::List::iterator mProbe; MWWorld::CellRefList::List::iterator mRepair; MWWorld::CellRefList::List::iterator mWeapon; private: ContainerStoreIterator (ContainerStore *container); ///< End-iterator ContainerStoreIterator (int mask, ContainerStore *container); ///< Begin-iterator // construct iterator using a CellRefList iterator ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); void copy (const ContainerStoreIterator& src); void incType(); void nextType(); bool resetIterator(); ///< Reset iterator for selected type. /// /// \return Type not empty? bool incIterator(); ///< Increment iterator for selected type. /// /// \return reached the end? public: ContainerStoreIterator(const ContainerStoreIterator& src); Ptr *operator->() const; Ptr operator*() const; ContainerStoreIterator& operator++(); ContainerStoreIterator operator++ (int); ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); bool isEqual (const ContainerStoreIterator& iter) const; int getType() const; const ContainerStore *getContainerStore() const; friend class ContainerStore; }; bool operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right); bool operator!= (const ContainerStoreIterator& left, const ContainerStoreIterator& right); } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/contentloader.hpp000066400000000000000000000012601264522266000237330ustar00rootroot00000000000000#ifndef CONTENTLOADER_HPP #define CONTENTLOADER_HPP #include #include #include #include "components/loadinglistener/loadinglistener.hpp" namespace MWWorld { struct ContentLoader { ContentLoader(Loading::Listener& listener) : mListener(listener) { } virtual ~ContentLoader() { } virtual void load(const boost::filesystem::path& filepath, int& index) { std::cout << "Loading content file " << filepath.string() << std::endl; mListener.setLabel(filepath.string()); } protected: Loading::Listener& mListener; }; } /* namespace MWWorld */ #endif /* CONTENTLOADER_HPP */ openmw-openmw-0.38.0/apps/openmw/mwworld/customdata.cpp000066400000000000000000000040571264522266000232400ustar00rootroot00000000000000#include "customdata.hpp" #include #include #include namespace MWWorld { MWClass::CreatureCustomData &CustomData::asCreatureCustomData() { std::stringstream error; error << "bad cast " << typeid(this).name() << " to CreatureCustomData"; throw std::logic_error(error.str()); } const MWClass::CreatureCustomData &CustomData::asCreatureCustomData() const { std::stringstream error; error << "bad cast " << typeid(this).name() << " to CreatureCustomData"; throw std::logic_error(error.str()); } MWClass::NpcCustomData &CustomData::asNpcCustomData() { std::stringstream error; error << "bad cast " << typeid(this).name() << " to NpcCustomData"; throw std::logic_error(error.str()); } const MWClass::NpcCustomData &CustomData::asNpcCustomData() const { std::stringstream error; error << "bad cast " << typeid(this).name() << " to NpcCustomData"; throw std::logic_error(error.str()); } MWClass::ContainerCustomData &CustomData::asContainerCustomData() { std::stringstream error; error << "bad cast " << typeid(this).name() << " to ContainerCustomData"; throw std::logic_error(error.str()); } MWClass::DoorCustomData &CustomData::asDoorCustomData() { std::stringstream error; error << "bad cast " << typeid(this).name() << " to DoorCustomData"; throw std::logic_error(error.str()); } const MWClass::DoorCustomData &CustomData::asDoorCustomData() const { std::stringstream error; error << "bad cast " << typeid(this).name() << " to DoorCustomData"; throw std::logic_error(error.str()); } MWClass::CreatureLevListCustomData &CustomData::asCreatureLevListCustomData() { std::stringstream error; error << "bad cast " << typeid(this).name() << " to CreatureLevListCustomData"; throw std::logic_error(error.str()); } const MWClass::CreatureLevListCustomData &CustomData::asCreatureLevListCustomData() const { std::stringstream error; error << "bad cast " << typeid(this).name() << " to CreatureLevListCustomData"; throw std::logic_error(error.str()); } } openmw-openmw-0.38.0/apps/openmw/mwworld/customdata.hpp000066400000000000000000000024011264522266000232340ustar00rootroot00000000000000#ifndef GAME_MWWORLD_CUSTOMDATA_H #define GAME_MWWORLD_CUSTOMDATA_H namespace MWClass { class CreatureCustomData; class NpcCustomData; class ContainerCustomData; class DoorCustomData; class CreatureLevListCustomData; } namespace MWWorld { /// \brief Base class for the MW-class-specific part of RefData class CustomData { public: virtual ~CustomData() {} virtual CustomData *clone() const = 0; // Fast version of dynamic_cast. Needs to be overridden in the respective class. virtual MWClass::CreatureCustomData& asCreatureCustomData(); virtual const MWClass::CreatureCustomData& asCreatureCustomData() const; virtual MWClass::NpcCustomData& asNpcCustomData(); virtual const MWClass::NpcCustomData& asNpcCustomData() const; virtual MWClass::ContainerCustomData& asContainerCustomData(); virtual MWClass::DoorCustomData& asDoorCustomData(); virtual const MWClass::DoorCustomData& asDoorCustomData() const; virtual MWClass::CreatureLevListCustomData& asCreatureLevListCustomData(); virtual const MWClass::CreatureLevListCustomData& asCreatureLevListCustomData() const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/esmloader.cpp000066400000000000000000000013071264522266000230420ustar00rootroot00000000000000#include "esmloader.hpp" #include "esmstore.hpp" #include namespace MWWorld { EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener) : ContentLoader(listener) , mEsm(readers) , mStore(store) , mEncoder(encoder) { } void EsmLoader::load(const boost::filesystem::path& filepath, int& index) { ContentLoader::load(filepath.filename(), index); ESM::ESMReader lEsm; lEsm.setEncoder(mEncoder); lEsm.setIndex(index); lEsm.setGlobalReaderList(&mEsm); lEsm.open(filepath.string()); mEsm[index] = lEsm; mStore.load(mEsm[index], &mListener); } } /* namespace MWWorld */ openmw-openmw-0.38.0/apps/openmw/mwworld/esmloader.hpp000066400000000000000000000012121264522266000230420ustar00rootroot00000000000000#ifndef ESMLOADER_HPP #define ESMLOADER_HPP #include #include "contentloader.hpp" namespace ToUTF8 { class Utf8Encoder; } namespace ESM { class ESMReader; } namespace MWWorld { class ESMStore; struct EsmLoader : public ContentLoader { EsmLoader(MWWorld::ESMStore& store, std::vector& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener); void load(const boost::filesystem::path& filepath, int& index); private: std::vector& mEsm; MWWorld::ESMStore& mStore; ToUTF8::Utf8Encoder* mEncoder; }; } /* namespace MWWorld */ #endif // ESMLOADER_HPP openmw-openmw-0.38.0/apps/openmw/mwworld/esmstore.cpp000066400000000000000000000173471264522266000227430ustar00rootroot00000000000000#include "esmstore.hpp" #include #include #include #include #include #include namespace MWWorld { static bool isCacheableRecord(int id) { if (id == ESM::REC_ACTI || id == ESM::REC_ALCH || id == ESM::REC_APPA || id == ESM::REC_ARMO || id == ESM::REC_BOOK || id == ESM::REC_CLOT || id == ESM::REC_CONT || id == ESM::REC_CREA || id == ESM::REC_DOOR || id == ESM::REC_INGR || id == ESM::REC_LEVC || id == ESM::REC_LEVI || id == ESM::REC_LIGH || id == ESM::REC_LOCK || id == ESM::REC_MISC || id == ESM::REC_NPC_ || id == ESM::REC_PROB || id == ESM::REC_REPA || id == ESM::REC_STAT || id == ESM::REC_WEAP || id == ESM::REC_BODY) { return true; } return false; } void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) { listener->setProgressRange(1000); ESM::Dialogue *dialogue = 0; // Land texture loading needs to use a separate internal store for each plugin. // We set the number of plugins here to avoid continual resizes during loading, // and so we can properly verify if valid plugin indices are being passed to the // LandTexture Store retrieval methods. mLandTextures.resize(esm.getGlobalReaderList()->size()); /// \todo Move this to somewhere else. ESMReader? // Cache parent esX files by tracking their indices in the global list of // all files/readers used by the engine. This will greaty accelerate // refnumber mangling, as required for handling moved references. const std::vector &masters = esm.getGameFiles(); std::vector *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { ESM::Header::MasterData &mast = const_cast(masters[j]); std::string fname = mast.name; int index = ~0; for (int i = 0; i < esm.getIndex(); i++) { const std::string &candidate = allPlugins->at(i).getContext().filename; std::string fnamecandidate = boost::filesystem::path(candidate).filename().string(); if (Misc::StringUtils::ciEqual(fname, fnamecandidate)) { index = i; break; } } if (index == (int)~0) { // Tried to load a parent file that has not been loaded yet. This is bad, // the launcher should have taken care of this. std::string fstring = "File " + esm.getName() + " asks for parent file " + masters[j].name + ", but it has not been loaded yet. Please check your load order."; esm.fail(fstring); } mast.index = index; } // Loop through all records while(esm.hasMoreRecs()) { ESM::NAME n = esm.getRecName(); esm.getRecHeader(); // Look up the record type. std::map::iterator it = mStores.find(n.val); if (it == mStores.end()) { if (n.val == ESM::REC_INFO) { if (dialogue) { dialogue->readInfo(esm, esm.getIndex() != 0); } else { std::cerr << "error: info record without dialog" << std::endl; esm.skipRecord(); } } else if (n.val == ESM::REC_MGEF) { mMagicEffects.load (esm); } else if (n.val == ESM::REC_SKIL) { mSkills.load (esm); } else if (n.val==ESM::REC_FILT || n.val == ESM::REC_DBGP) { // ignore project file only records esm.skipRecord(); } else { std::stringstream error; error << "Unknown record: " << n.toString(); throw std::runtime_error(error.str()); } } else { RecordId id = it->second->load(esm); if (id.mIsDeleted) { it->second->eraseStatic(id.mId); continue; } if (n.val==ESM::REC_DIAL) { dialogue = const_cast(mDialogs.find(id.mId)); } else { dialogue = 0; } } listener->setProgress(static_cast(esm.getFileOffset() / (float)esm.getFileSize() * 1000)); } } void ESMStore::setUp() { mIds.clear(); std::map::iterator storeIt = mStores.begin(); for (; storeIt != mStores.end(); ++storeIt) { storeIt->second->setUp(); if (isCacheableRecord(storeIt->first)) { std::vector identifiers; storeIt->second->listIdentifier(identifiers); for (std::vector::const_iterator record = identifiers.begin(); record != identifiers.end(); ++record) mIds[*record] = storeIt->first; } } mSkills.setUp(); mMagicEffects.setUp(); mAttributes.setUp(); mDialogs.setUp(); } int ESMStore::countSavedGameRecords() const { return 1 // DYNA (dynamic name counter) +mPotions.getDynamicSize() +mArmors.getDynamicSize() +mBooks.getDynamicSize() +mClasses.getDynamicSize() +mClothes.getDynamicSize() +mEnchants.getDynamicSize() +mNpcs.getDynamicSize() +mSpells.getDynamicSize() +mWeapons.getDynamicSize() +mCreatureLists.getDynamicSize() +mItemLists.getDynamicSize(); } void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { writer.startRecord(ESM::REC_DYNA); writer.startSubRecord("COUN"); writer.writeT(mDynamicCount); writer.endRecord("COUN"); writer.endRecord(ESM::REC_DYNA); mPotions.write (writer, progress); mArmors.write (writer, progress); mBooks.write (writer, progress); mClasses.write (writer, progress); mClothes.write (writer, progress); mEnchants.write (writer, progress); mSpells.write (writer, progress); mWeapons.write (writer, progress); mNpcs.write (writer, progress); mItemLists.write (writer, progress); mCreatureLists.write (writer, progress); } bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type) { switch (type) { case ESM::REC_ALCH: case ESM::REC_ARMO: case ESM::REC_BOOK: case ESM::REC_CLAS: case ESM::REC_CLOT: case ESM::REC_ENCH: case ESM::REC_SPEL: case ESM::REC_WEAP: case ESM::REC_NPC_: case ESM::REC_LEVI: case ESM::REC_LEVC: { mStores[type]->read (reader); } if (type==ESM::REC_NPC_) { // NPC record will always be last and we know that there can be only one // dynamic NPC record (player) -> We are done here with dynamic record loading setUp(); const ESM::NPC *player = mNpcs.find ("player"); if (!mRaces.find (player->mRace) || !mClasses.find (player->mClass)) throw std::runtime_error ("Invalid player record (race or class unavailable"); } return true; case ESM::REC_DYNA: reader.getSubNameIs("COUN"); reader.getHT(mDynamicCount); return true; default: return false; } } } // end namespace openmw-openmw-0.38.0/apps/openmw/mwworld/esmstore.hpp000066400000000000000000000344101264522266000227360ustar00rootroot00000000000000#ifndef OPENMW_MWWORLD_ESMSTORE_H #define OPENMW_MWWORLD_ESMSTORE_H #include #include #include #include "store.hpp" namespace Loading { class Listener; } namespace MWWorld { class ESMStore { Store mActivators; Store mPotions; Store mAppas; Store mArmors; Store mBodyParts; Store mBooks; Store mBirthSigns; Store mClasses; Store mClothes; Store mContainers; Store mCreatures; Store mDialogs; Store mDoors; Store mEnchants; Store mFactions; Store mGlobals; Store mIngreds; Store mCreatureLists; Store mItemLists; Store mLights; Store mLockpicks; Store mMiscItems; Store mNpcs; Store mProbes; Store mRaces; Store mRegions; Store mRepairs; Store mSoundGens; Store mSounds; Store mSpells; Store mStartScripts; Store mStatics; Store mWeapons; Store mGameSettings; Store mScripts; // Lists that need special rules Store mCells; Store mLands; Store mLandTextures; Store mPathgrids; Store mMagicEffects; Store mSkills; // Special entry which is hardcoded and not loaded from an ESM Store mAttributes; // Lookup of all IDs. Makes looking up references faster. Just // maps the id name to the record type. std::map mIds; std::map mStores; ESM::NPC mPlayerTemplate; unsigned int mDynamicCount; public: /// \todo replace with SharedIterator typedef std::map::const_iterator iterator; iterator begin() const { return mStores.begin(); } iterator end() const { return mStores.end(); } /// Look up the given ID in 'all'. Returns 0 if not found. /// \note id must be in lower case. int find(const std::string &id) const { std::map::const_iterator it = mIds.find(id); if (it == mIds.end()) { return 0; } return it->second; } ESMStore() : mDynamicCount(0) { mStores[ESM::REC_ACTI] = &mActivators; mStores[ESM::REC_ALCH] = &mPotions; mStores[ESM::REC_APPA] = &mAppas; mStores[ESM::REC_ARMO] = &mArmors; mStores[ESM::REC_BODY] = &mBodyParts; mStores[ESM::REC_BOOK] = &mBooks; mStores[ESM::REC_BSGN] = &mBirthSigns; mStores[ESM::REC_CELL] = &mCells; mStores[ESM::REC_CLAS] = &mClasses; mStores[ESM::REC_CLOT] = &mClothes; mStores[ESM::REC_CONT] = &mContainers; mStores[ESM::REC_CREA] = &mCreatures; mStores[ESM::REC_DIAL] = &mDialogs; mStores[ESM::REC_DOOR] = &mDoors; mStores[ESM::REC_ENCH] = &mEnchants; mStores[ESM::REC_FACT] = &mFactions; mStores[ESM::REC_GLOB] = &mGlobals; mStores[ESM::REC_GMST] = &mGameSettings; mStores[ESM::REC_INGR] = &mIngreds; mStores[ESM::REC_LAND] = &mLands; mStores[ESM::REC_LEVC] = &mCreatureLists; mStores[ESM::REC_LEVI] = &mItemLists; mStores[ESM::REC_LIGH] = &mLights; mStores[ESM::REC_LOCK] = &mLockpicks; mStores[ESM::REC_LTEX] = &mLandTextures; mStores[ESM::REC_MISC] = &mMiscItems; mStores[ESM::REC_NPC_] = &mNpcs; mStores[ESM::REC_PGRD] = &mPathgrids; mStores[ESM::REC_PROB] = &mProbes; mStores[ESM::REC_RACE] = &mRaces; mStores[ESM::REC_REGN] = &mRegions; mStores[ESM::REC_REPA] = &mRepairs; mStores[ESM::REC_SCPT] = &mScripts; mStores[ESM::REC_SNDG] = &mSoundGens; mStores[ESM::REC_SOUN] = &mSounds; mStores[ESM::REC_SPEL] = &mSpells; mStores[ESM::REC_SSCR] = &mStartScripts; mStores[ESM::REC_STAT] = &mStatics; mStores[ESM::REC_WEAP] = &mWeapons; mPathgrids.setCells(mCells); } void clearDynamic () { for (std::map::iterator it = mStores.begin(); it != mStores.end(); ++it) it->second->clearDynamic(); mNpcs.insert(mPlayerTemplate); } void movePlayerRecord () { mPlayerTemplate = *mNpcs.find("player"); mNpcs.eraseStatic(mPlayerTemplate.mId); mNpcs.insert(mPlayerTemplate); } void load(ESM::ESMReader &esm, Loading::Listener* listener); template const Store &get() const { throw std::runtime_error("Storage for this type not exist"); } /// Insert a custom record (i.e. with a generated ID that will not clash will pre-existing records) template const T *insert(const T &x) { std::ostringstream id; id << "$dynamic" << mDynamicCount++; Store &store = const_cast &>(get()); if (store.search(id.str()) != 0) { std::ostringstream msg; msg << "Try to override existing record '" << id.str() << "'"; throw std::runtime_error(msg.str()); } T record = x; record.mId = id.str(); T *ptr = store.insert(record); for (iterator it = mStores.begin(); it != mStores.end(); ++it) { if (it->second == &store) { mIds[ptr->mId] = it->first; } } return ptr; } /// Insert a record with set ID, and allow it to override a pre-existing static record. template const T *overrideRecord(const T &x) { Store &store = const_cast &>(get()); T *ptr = store.insert(x); for (iterator it = mStores.begin(); it != mStores.end(); ++it) { if (it->second == &store) { mIds[ptr->mId] = it->first; } } return ptr; } template const T *insertStatic(const T &x) { std::ostringstream id; id << "$dynamic" << mDynamicCount++; Store &store = const_cast &>(get()); if (store.search(id.str()) != 0) { std::ostringstream msg; msg << "Try to override existing record '" << id.str() << "'"; throw std::runtime_error(msg.str()); } T record = x; T *ptr = store.insertStatic(record); for (iterator it = mStores.begin(); it != mStores.end(); ++it) { if (it->second == &store) { mIds[ptr->mId] = it->first; } } return ptr; } // This method must be called once, after loading all master/plugin files. This can only be done // from the outside, so it must be public. void setUp(); int countSavedGameRecords() const; void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; bool readRecord (ESM::ESMReader& reader, uint32_t type); ///< \return Known type? }; template <> inline const ESM::Cell *ESMStore::insert(const ESM::Cell &cell) { return mCells.insert(cell); } template <> inline const ESM::NPC *ESMStore::insert(const ESM::NPC &npc) { std::ostringstream id; id << "$dynamic" << mDynamicCount++; if (Misc::StringUtils::ciEqual(npc.mId, "player")) { return mNpcs.insert(npc); } else if (mNpcs.search(id.str()) != 0) { std::ostringstream msg; msg << "Try to override existing record '" << id.str() << "'"; throw std::runtime_error(msg.str()); } ESM::NPC record = npc; record.mId = id.str(); ESM::NPC *ptr = mNpcs.insert(record); mIds[ptr->mId] = ESM::REC_NPC_; return ptr; } template <> inline const Store &ESMStore::get() const { return mActivators; } template <> inline const Store &ESMStore::get() const { return mPotions; } template <> inline const Store &ESMStore::get() const { return mAppas; } template <> inline const Store &ESMStore::get() const { return mArmors; } template <> inline const Store &ESMStore::get() const { return mBodyParts; } template <> inline const Store &ESMStore::get() const { return mBooks; } template <> inline const Store &ESMStore::get() const { return mBirthSigns; } template <> inline const Store &ESMStore::get() const { return mClasses; } template <> inline const Store &ESMStore::get() const { return mClothes; } template <> inline const Store &ESMStore::get() const { return mContainers; } template <> inline const Store &ESMStore::get() const { return mCreatures; } template <> inline const Store &ESMStore::get() const { return mDialogs; } template <> inline const Store &ESMStore::get() const { return mDoors; } template <> inline const Store &ESMStore::get() const { return mEnchants; } template <> inline const Store &ESMStore::get() const { return mFactions; } template <> inline const Store &ESMStore::get() const { return mGlobals; } template <> inline const Store &ESMStore::get() const { return mIngreds; } template <> inline const Store &ESMStore::get() const { return mCreatureLists; } template <> inline const Store &ESMStore::get() const { return mItemLists; } template <> inline const Store &ESMStore::get() const { return mLights; } template <> inline const Store &ESMStore::get() const { return mLockpicks; } template <> inline const Store &ESMStore::get() const { return mMiscItems; } template <> inline const Store &ESMStore::get() const { return mNpcs; } template <> inline const Store &ESMStore::get() const { return mProbes; } template <> inline const Store &ESMStore::get() const { return mRaces; } template <> inline const Store &ESMStore::get() const { return mRegions; } template <> inline const Store &ESMStore::get() const { return mRepairs; } template <> inline const Store &ESMStore::get() const { return mSoundGens; } template <> inline const Store &ESMStore::get() const { return mSounds; } template <> inline const Store &ESMStore::get() const { return mSpells; } template <> inline const Store &ESMStore::get() const { return mStartScripts; } template <> inline const Store &ESMStore::get() const { return mStatics; } template <> inline const Store &ESMStore::get() const { return mWeapons; } template <> inline const Store &ESMStore::get() const { return mGameSettings; } template <> inline const Store &ESMStore::get() const { return mScripts; } template <> inline const Store &ESMStore::get() const { return mCells; } template <> inline const Store &ESMStore::get() const { return mLands; } template <> inline const Store &ESMStore::get() const { return mLandTextures; } template <> inline const Store &ESMStore::get() const { return mPathgrids; } template <> inline const Store &ESMStore::get() const { return mMagicEffects; } template <> inline const Store &ESMStore::get() const { return mSkills; } template <> inline const Store &ESMStore::get() const { return mAttributes; } } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/failedaction.cpp000066400000000000000000000010031264522266000235020ustar00rootroot00000000000000#include "failedaction.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/actorutil.hpp" namespace MWWorld { FailedAction::FailedAction(const std::string &msg) : Action(false), mMessage(msg) { } void FailedAction::executeImp(const Ptr &actor) { if(actor == MWMechanics::getPlayer() && !mMessage.empty()) MWBase::Environment::get().getWindowManager()->messageBox(mMessage); } } openmw-openmw-0.38.0/apps/openmw/mwworld/failedaction.hpp000066400000000000000000000005461264522266000235220ustar00rootroot00000000000000#ifndef GAME_MWWORLD_FAILEDACTION_H #define GAME_MWWORLD_FAILEDACTION_H #include "action.hpp" #include "ptr.hpp" namespace MWWorld { class FailedAction : public Action { std::string mMessage; virtual void executeImp(const Ptr &actor); public: FailedAction(const std::string &message = std::string()); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/fallback.cpp000066400000000000000000000034731264522266000226340ustar00rootroot00000000000000#include "fallback.hpp" #include namespace MWWorld { Fallback::Fallback(const std::map& fallback):mFallbackMap(fallback) {} std::string Fallback::getFallbackString(const std::string& fall) const { std::map::const_iterator it; if((it = mFallbackMap.find(fall)) == mFallbackMap.end()) { return ""; } return it->second; } float Fallback::getFallbackFloat(const std::string& fall) const { std::string fallback=getFallbackString(fall); if(fallback.empty()) return 0; else return boost::lexical_cast(fallback); } int Fallback::getFallbackInt(const std::string& fall) const { std::string fallback=getFallbackString(fall); if(fallback.empty()) return 0; else return boost::lexical_cast(fallback); } bool Fallback::getFallbackBool(const std::string& fall) const { std::string fallback=getFallbackString(fall); if(fallback.empty()) return false; else return boost::lexical_cast(fallback); } osg::Vec4f Fallback::getFallbackColour(const std::string& fall) const { std::string sum=getFallbackString(fall); if(sum.empty()) return osg::Vec4f(0.f,0.f,0.f,1.f); else { std::string ret[3]; unsigned int j=0; for(unsigned int i=0;i(ret[0])/255.f,boost::lexical_cast(ret[1])/255.f,boost::lexical_cast(ret[2])/255.f, 1.f); } } } openmw-openmw-0.38.0/apps/openmw/mwworld/fallback.hpp000066400000000000000000000012471264522266000226360ustar00rootroot00000000000000#ifndef GAME_MWWORLD_FALLBACK_H #define GAME_MWWORLD_FALLBACK_H #include #include #include namespace MWWorld { class Fallback { const std::map mFallbackMap; public: Fallback(const std::map& fallback); std::string getFallbackString(const std::string& fall) const; float getFallbackFloat(const std::string& fall) const; int getFallbackInt(const std::string& fall) const; bool getFallbackBool(const std::string& fall) const; osg::Vec4f getFallbackColour(const std::string& fall) const; }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/globals.cpp000066400000000000000000000061271264522266000225170ustar00rootroot00000000000000#include "globals.hpp" #include #include #include #include #include "esmstore.hpp" namespace MWWorld { Globals::Collection::const_iterator Globals::find (const std::string& name) const { Collection::const_iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name)); if (iter==mVariables.end()) throw std::runtime_error ("unknown global variable: " + name); return iter; } Globals::Collection::iterator Globals::find (const std::string& name) { Collection::iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name)); if (iter==mVariables.end()) throw std::runtime_error ("unknown global variable: " + name); return iter; } void Globals::fill (const MWWorld::ESMStore& store) { mVariables.clear(); const MWWorld::Store& globals = store.get(); for (MWWorld::Store::iterator iter = globals.begin(); iter!=globals.end(); ++iter) { mVariables.insert (std::make_pair (Misc::StringUtils::lowerCase (iter->mId), *iter)); } } const ESM::Variant& Globals::operator[] (const std::string& name) const { return find (Misc::StringUtils::lowerCase (name))->second.mValue; } ESM::Variant& Globals::operator[] (const std::string& name) { return find (Misc::StringUtils::lowerCase (name))->second.mValue; } char Globals::getType (const std::string& name) const { Collection::const_iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name)); if (iter==mVariables.end()) return ' '; switch (iter->second.mValue.getType()) { case ESM::VT_Short: return 's'; case ESM::VT_Long: return 'l'; case ESM::VT_Float: return 'f'; default: return ' '; } } int Globals::countSavedGameRecords() const { return mVariables.size(); } void Globals::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { for (Collection::const_iterator iter (mVariables.begin()); iter!=mVariables.end(); ++iter) { writer.startRecord (ESM::REC_GLOB); iter->second.save (writer); writer.endRecord (ESM::REC_GLOB); } } bool Globals::readRecord (ESM::ESMReader& reader, uint32_t type) { if (type==ESM::REC_GLOB) { ESM::Global global; bool isDeleted = false; // This readRecord() method is used when reading a saved game. // Deleted globals can't appear there, so isDeleted will be ignored here. global.load(reader, isDeleted); Misc::StringUtils::lowerCaseInPlace(global.mId); Collection::iterator iter = mVariables.find (global.mId); if (iter!=mVariables.end()) iter->second = global; return true; } return false; } } openmw-openmw-0.38.0/apps/openmw/mwworld/globals.hpp000066400000000000000000000026661264522266000225300ustar00rootroot00000000000000#ifndef GAME_MWWORLD_GLOBALS_H #define GAME_MWWORLD_GLOBALS_H #include #include #include #include #include #include namespace ESM { class ESMWriter; class ESMReader; } namespace Loading { class Listener; } namespace MWWorld { class ESMStore; class Globals { private: typedef std::map Collection; Collection mVariables; // type, value Collection::const_iterator find (const std::string& name) const; Collection::iterator find (const std::string& name); public: const ESM::Variant& operator[] (const std::string& name) const; ESM::Variant& operator[] (const std::string& name); char getType (const std::string& name) const; ///< If there is no global variable with this name, ' ' is returned. void fill (const MWWorld::ESMStore& store); ///< Replace variables with variables from \a store with default values. int countSavedGameRecords() const; void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; bool readRecord (ESM::ESMReader& reader, uint32_t type); ///< Records for variables that do not exist are dropped silently. /// /// \return Known type? }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/inventorystore.cpp000066400000000000000000000652011264522266000242040ustar00rootroot00000000000000#include "inventorystore.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwgui/inventorywindow.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/actorutil.hpp" #include "esmstore.hpp" #include "class.hpp" void MWWorld::InventoryStore::copySlots (const InventoryStore& store) { // some const-trickery, required because of a flaw in the handling of MW-references and the // resulting workarounds for (std::vector::const_iterator iter ( const_cast (store).mSlots.begin()); iter!=const_cast (store).mSlots.end(); ++iter) { std::size_t distance = std::distance (const_cast (store).begin(), *iter); ContainerStoreIterator slot = begin(); std::advance (slot, distance); mSlots.push_back (slot); } // some const-trickery, required because of a flaw in the handling of MW-references and the // resulting workarounds std::size_t distance = std::distance (const_cast (store).begin(), const_cast (store).mSelectedEnchantItem); ContainerStoreIterator slot = begin(); std::advance (slot, distance); mSelectedEnchantItem = slot; } void MWWorld::InventoryStore::initSlots (TSlots& slots_) { for (int i=0; i (mSlots.size()); ++i) if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref) { inventory.mEquipmentSlots[index] = i; } if (mSelectedEnchantItem.getType()!=-1 && mSelectedEnchantItem->getBase() == &ref) inventory.mSelectedEnchantItem = index; } void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIterator &iter, int index, const ESM::InventoryState &inventory) { if (index == inventory.mSelectedEnchantItem) mSelectedEnchantItem = iter; std::map::const_iterator found = inventory.mEquipmentSlots.find(index); if (found != inventory.mEquipmentSlots.end()) { if (found->second < 0 || found->second >= MWWorld::InventoryStore::Slots) throw std::runtime_error("Invalid slot index in inventory state"); // make sure the item can actually be equipped in this slot int slot = found->second; std::pair, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter); if (!allowedSlots.first.size()) return; if (std::find(allowedSlots.first.begin(), allowedSlots.first.end(), slot) == allowedSlots.first.end()) slot = allowedSlots.first.front(); // unstack if required if (!allowedSlots.second && iter->getRefData().getCount() > 1) { MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1); iter->getRefData().setCount(iter->getRefData().getCount()-1); mSlots[slot] = newIter; } else mSlots[slot] = iter; } } MWWorld::InventoryStore::InventoryStore() : mListener(NULL) , mUpdatesEnabled (true) , mFirstAutoEquip(true) , mSelectedEnchantItem(end()) , mRechargingItemsUpToDate(false) { initSlots (mSlots); } MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) : ContainerStore (store) , mMagicEffects(store.mMagicEffects) , mListener(store.mListener) , mUpdatesEnabled(store.mUpdatesEnabled) , mFirstAutoEquip(store.mFirstAutoEquip) , mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes) , mSelectedEnchantItem(end()) , mRechargingItemsUpToDate(false) { copySlots (store); } MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store) { mListener = store.mListener; mMagicEffects = store.mMagicEffects; mFirstAutoEquip = store.mFirstAutoEquip; mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes; mRechargingItemsUpToDate = false; ContainerStore::operator= (store); mSlots.clear(); copySlots (store); return *this; } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) { const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner); // Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves if (actorPtr != MWMechanics::getPlayer() && !(actorPtr.getClass().isNpc() && actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())) { std::string type = itemPtr.getTypeName(); if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name()) autoEquip(actorPtr); } return retVal; } void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor) { if (iterator == end()) throw std::runtime_error ("can't equip end() iterator, use unequip function instead"); if (slot<0 || slot>=static_cast (mSlots.size())) throw std::runtime_error ("slot number out of range"); if (iterator.getContainerStore()!=this) throw std::runtime_error ("attempt to equip an item that is not in the inventory"); std::pair, bool> slots_; slots_ = iterator->getClass().getEquipmentSlots (*iterator); if (std::find (slots_.first.begin(), slots_.first.end(), slot)==slots_.first.end()) throw std::runtime_error ("invalid slot"); if (mSlots[slot] != end()) unequipSlot(slot, actor); // unstack item pointed to by iterator if required if (iterator!=end() && !slots_.second && iterator->getRefData().getCount() > 1) // if slots.second is true, item can stay stacked when equipped { unstack(*iterator, actor); } mSlots[slot] = iterator; flagAsModified(); fireEquipmentChangedEvent(actor); updateMagicEffects(actor); } void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor) { mUpdatesEnabled = false; for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) unequipSlot(slot, actor); mUpdatesEnabled = true; fireEquipmentChangedEvent(actor); updateMagicEffects(actor); } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) { if (slot<0 || slot>=static_cast (mSlots.size())) throw std::runtime_error ("slot number out of range"); if (mSlots[slot]==end()) return end(); if (mSlots[slot]->getRefData().getCount()<1) { // Object has been deleted // This should no longer happen, since the new remove function will unequip first throw std::runtime_error("Invalid slot, make sure you are not calling RefData::setCount for a container object"); } return mSlots[slot]; } void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { TSlots slots_; initSlots (slots_); // Disable model update during auto-equip mUpdatesEnabled = false; for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) { Ptr test = *iter; // Don't autoEquip lights. Handled in Actors::updateEquippedLight based on environment light. if (test.getTypeName() == typeid(ESM::Light).name()) { continue; } // Only autoEquip if we are the original owner of the item. // This stops merchants from auto equipping anything you sell to them. // ...unless this is a companion, he should always equip items given to him. if (!Misc::StringUtils::ciEqual(test.getCellRef().getOwner(), actor.getCellRef().getRefId()) && (actor.getClass().getScript(actor).empty() || !actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion")) && !actor.getClass().getCreatureStats(actor).isDead() // Corpses can be dressed up by the player as desired ) { continue; } int testSkill = test.getClass().getEquipmentSkill (test); std::pair, bool> itemsSlots = iter->getClass().getEquipmentSlots (*iter); for (std::vector::const_iterator iter2 (itemsSlots.first.begin()); iter2!=itemsSlots.first.end(); ++iter2) { if (*iter2 == Slot_CarriedRight) // Items in right hand are situational use, so don't equip them. // Equipping weapons is handled by AiCombat. Anything else (lockpicks, probes) can't be used by NPCs anyway (yet) continue; if (slots_.at (*iter2)!=end()) { Ptr old = *slots_.at (*iter2); // check skill int oldSkill = old.getClass().getEquipmentSkill (old); bool use = false; if (testSkill!=-1 && oldSkill==-1) use = true; else if (testSkill!=-1 && oldSkill!=-1 && testSkill!=oldSkill) { if (actor.getClass().getSkill(actor, oldSkill) > actor.getClass().getSkill (actor, testSkill)) continue; // rejected, because old item better matched the NPC's skills. if (actor.getClass().getSkill(actor, oldSkill) < actor.getClass().getSkill (actor, testSkill)) use = true; } if (!use) { // check value if (old.getClass().getValue (old)>= test.getClass().getValue (test)) { continue; } } } switch(test.getClass().canBeEquipped (test, actor).first) { case 0: continue; default: break; } if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped { // unstack item pointed to by iterator if required if (iter->getRefData().getCount() > 1) { unstack(*iter, actor); } } slots_[*iter2] = iter; break; } } bool changed = false; for (std::size_t i=0; igetClass().getEnchantment (**iter); if (!enchantmentId.empty()) { const ESM::Enchantment& enchantment = *MWBase::Environment::get().getWorld()->getStore().get().find (enchantmentId); if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect) continue; std::vector params; bool existed = (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().getRefId()) != mPermanentMagicEffectMagnitudes.end()); if (!existed) { // Roll some dice, one for each effect params.resize(enchantment.mEffects.mList.size()); for (unsigned int i=0; i::const_iterator effectIt (enchantment.mEffects.mList.begin()); effectIt!=enchantment.mEffects.mList.end(); ++effectIt) { params[i].mMultiplier = MWMechanics::getEffectMultiplier(effectIt->mEffectID, actor, actor); ++i; } // Note that using the RefID as a key here is not entirely correct. // Consider equipping the same item twice (e.g. a ring) // However, permanent enchantments with a random magnitude are kind of an exploit anyway, // so it doesn't really matter if both items will get the same magnitude. *Extreme* edge case. mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()] = params; } else params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()]; int i=0; for (std::vector::const_iterator effectIt (enchantment.mEffects.mList.begin()); effectIt!=enchantment.mEffects.mList.end(); ++effectIt, ++i) { const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( effectIt->mEffectID); // Fully resisted? if (params[i].mMultiplier == 0) continue; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; magnitude *= params[i].mMultiplier; if (!existed) { // During first auto equip, we don't play any sounds. // Basically we don't want sounds when the actor is first loaded, // the items should appear as if they'd always been equipped. mListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip, !mFirstAutoEquip && effectIt == enchantment.mEffects.mList.begin()); } if (magnitude) mMagicEffects.add (*effectIt, magnitude); } } } // Now drop expired effects for (TEffectMagnitudes::iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end();) { bool found = false; for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) { if (*iter == end()) continue; if ((**iter).getCellRef().getRefId() == it->first) { found = true; } } if (!found) mPermanentMagicEffectMagnitudes.erase(it++); else ++it; } // Magic effects are normally not updated when paused, but we need this to make resistances work immediately after equipping MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor); mFirstAutoEquip = false; } void MWWorld::InventoryStore::flagAsModified() { ContainerStore::flagAsModified(); mRechargingItemsUpToDate = false; } bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) { bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2); if (!canStack) return false; // don't stack if either item is currently equipped for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) { if (*iter != end() && (ptr1 == **iter || ptr2 == **iter)) { bool stackWhenEquipped = (*iter)->getClass().getEquipmentSlots(**iter).second; if (!stackWhenEquipped) return false; } } return true; } void MWWorld::InventoryStore::setSelectedEnchantItem(const ContainerStoreIterator& iterator) { mSelectedEnchantItem = iterator; } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem() { return mSelectedEnchantItem; } int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor) { int retCount = ContainerStore::remove(item, count, actor); bool wasEquipped = false; if (!item.getRefData().getCount()) { for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) { if (mSlots[slot] == end()) continue; if (*mSlots[slot] == item) { unequipSlot(slot, actor); wasEquipped = true; break; } } } // If an armor/clothing item is removed, try to find a replacement, // but not for the player nor werewolves. if (wasEquipped && (actor != MWMechanics::getPlayer()) && !(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())) { std::string type = item.getTypeName(); if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name()) autoEquip(actor); } if (item.getRefData().getCount() == 0 && mSelectedEnchantItem != end() && *mSelectedEnchantItem == item) { mSelectedEnchantItem = end(); } return retCount; } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) { ContainerStoreIterator it = mSlots[slot]; if (it != end()) { ContainerStoreIterator retval = it; // empty this slot mSlots[slot] = end(); if (it->getRefData().getCount()) { retval = restack(*it); if (actor == MWMechanics::getPlayer()) { // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared const std::string& script = it->getClass().getScript(*it); if (script != "") (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); } if ((mSelectedEnchantItem != end()) && (mSelectedEnchantItem == it)) { mSelectedEnchantItem = end(); } } fireEquipmentChangedEvent(actor); updateMagicEffects(actor); return retval; } return it; } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(const MWWorld::Ptr& item, const MWWorld::Ptr& actor) { for (int slot=0; slotequipmentChanged(); // if player, update inventory window /* if (actor == MWMechanics::getPlayer()) { MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); } */ } void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisitor &visitor) { for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) { if (*iter==end()) continue; std::string enchantmentId = (*iter)->getClass().getEnchantment (**iter); if (enchantmentId.empty()) continue; const ESM::Enchantment& enchantment = *MWBase::Environment::get().getWorld()->getStore().get().find (enchantmentId); if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect) continue; if (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().getRefId()) == mPermanentMagicEffectMagnitudes.end()) continue; int i=0; for (std::vector::const_iterator effectIt (enchantment.mEffects.mList.begin()); effectIt!=enchantment.mEffects.mList.end(); ++effectIt) { const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i]; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; magnitude *= params.mMultiplier; if (magnitude > 0) visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), (**iter).getCellRef().getRefId(), -1, magnitude); ++i; } } } void MWWorld::InventoryStore::updateRechargingItems() { mRechargingItems.clear(); for (ContainerStoreIterator it = begin(); it != end(); ++it) { if (it->getClass().getEnchantment(*it) != "") { std::string enchantmentId = it->getClass().getEnchantment(*it); const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().search( enchantmentId); if (!enchantment) { std::cerr << "Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId() << std::endl; continue; } if (enchantment->mData.mType == ESM::Enchantment::WhenUsed || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) mRechargingItems.push_back(std::make_pair(it, static_cast(enchantment->mData.mCharge))); } } } void MWWorld::InventoryStore::rechargeItems(float duration) { if (!mRechargingItemsUpToDate) { updateRechargingItems(); mRechargingItemsUpToDate = true; } for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it) { if (it->first->getCellRef().getEnchantmentCharge() == -1 || it->first->getCellRef().getEnchantmentCharge() == it->second) continue; static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get().find( "fMagicItemRechargePerSecond")->getFloat(); if (it->first->getCellRef().getEnchantmentCharge() <= it->second) { it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration, it->second)); // attempt to restack when fully recharged if (it->first->getCellRef().getEnchantmentCharge() == it->second) it->first = restack(*it->first); } } } void MWWorld::InventoryStore::purgeEffect(short effectId) { mMagicEffects.remove(MWMechanics::EffectKey(effectId)); } void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId) { TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId); if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end()) return; for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) { if (*iter==end()) continue; if ((*iter)->getCellRef().getRefId() != sourceId) continue; std::string enchantmentId = (*iter)->getClass().getEnchantment (**iter); if (!enchantmentId.empty()) { const ESM::Enchantment& enchantment = *MWBase::Environment::get().getWorld()->getStore().get().find (enchantmentId); if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect) continue; std::vector& params = effectMagnitudeIt->second; int i=0; for (std::vector::const_iterator effectIt (enchantment.mEffects.mList.begin()); effectIt!=enchantment.mEffects.mList.end(); ++effectIt, ++i) { if (effectIt->mEffectID != effectId) continue; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; magnitude *= params[i].mMultiplier; if (magnitude) mMagicEffects.add (*effectIt, -magnitude); params[i].mMultiplier = 0; break; } } } } void MWWorld::InventoryStore::clear() { mSlots.clear(); initSlots (mSlots); ContainerStore::clear(); } bool MWWorld::InventoryStore::isEquipped(const MWWorld::ConstPtr &item) { for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) { if (getSlot(i) != end() && *getSlot(i) == item) return true; } return false; } void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const { MWWorld::ContainerStore::writeState(state); for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it) { std::vector > params; for (std::vector::const_iterator pIt = it->second.begin(); pIt != it->second.end(); ++pIt) { params.push_back(std::make_pair(pIt->mRandom, pIt->mMultiplier)); } state.mPermanentMagicEffectMagnitudes[it->first] = params; } } void MWWorld::InventoryStore::readState(const ESM::InventoryState &state) { MWWorld::ContainerStore::readState(state); for (ESM::InventoryState::TEffectMagnitudes::const_iterator it = state.mPermanentMagicEffectMagnitudes.begin(); it != state.mPermanentMagicEffectMagnitudes.end(); ++it) { std::vector params; for (std::vector >::const_iterator pIt = it->second.begin(); pIt != it->second.end(); ++pIt) { EffectParams p; p.mRandom = pIt->first; p.mMultiplier = pIt->second; params.push_back(p); } mPermanentMagicEffectMagnitudes[it->first] = params; } } openmw-openmw-0.38.0/apps/openmw/mwworld/inventorystore.hpp000066400000000000000000000205741264522266000242150ustar00rootroot00000000000000#ifndef GAME_MWWORLD_INVENTORYSTORE_H #define GAME_MWWORLD_INVENTORYSTORE_H #include "containerstore.hpp" #include "../mwmechanics/magiceffects.hpp" namespace ESM { struct MagicEffect; } namespace MWMechanics { class NpcStats; } namespace MWWorld { class InventoryStoreListener { public: /** * Fired when items are equipped or unequipped */ virtual void equipmentChanged () {} /** * @param effect * @param isNew Is this effect new (e.g. the item for it was just now manually equipped) * or was it loaded from a savegame / initial game state? \n * If it isn't new, non-looping VFX should not be played. * @param playSound Play effect sound? */ virtual void permanentEffectAdded (const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) {} }; ///< \brief Variant of the ContainerStore for NPCs class InventoryStore : public ContainerStore { public: static const int Slot_Helmet = 0; static const int Slot_Cuirass = 1; static const int Slot_Greaves = 2; static const int Slot_LeftPauldron = 3; static const int Slot_RightPauldron = 4; static const int Slot_LeftGauntlet = 5; static const int Slot_RightGauntlet = 6; static const int Slot_Boots = 7; static const int Slot_Shirt = 8; static const int Slot_Pants = 9; static const int Slot_Skirt = 10; static const int Slot_Robe = 11; static const int Slot_LeftRing = 12; static const int Slot_RightRing = 13; static const int Slot_Amulet = 14; static const int Slot_Belt = 15; static const int Slot_CarriedRight = 16; static const int Slot_CarriedLeft = 17; static const int Slot_Ammunition = 18; static const int Slots = 19; static const int Slot_NoSlot = -1; private: MWMechanics::MagicEffects mMagicEffects; InventoryStoreListener* mListener; // Enables updates of magic effects and actor model whenever items are equipped or unequipped. // This is disabled during autoequip to avoid excessive updates bool mUpdatesEnabled; bool mFirstAutoEquip; // Vanilla allows permanent effects with a random magnitude, so it needs to be stored here. // We also need this to only play sounds and particle effects when the item is equipped, rather than on every update. struct EffectParams { // Modifier to scale between min and max magnitude float mRandom; // Multiplier for when an effect was fully or partially resisted float mMultiplier; }; typedef std::map > TEffectMagnitudes; TEffectMagnitudes mPermanentMagicEffectMagnitudes; typedef std::vector TSlots; TSlots mSlots; // selected magic item (for using enchantments of type "Cast once" or "Cast when used") ContainerStoreIterator mSelectedEnchantItem; // (item, max charge) typedef std::vector > TRechargingItems; TRechargingItems mRechargingItems; bool mRechargingItemsUpToDate; void copySlots (const InventoryStore& store); void initSlots (TSlots& slots_); void updateMagicEffects(const Ptr& actor); void updateRechargingItems(); void fireEquipmentChangedEvent(const Ptr& actor); virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory); public: InventoryStore(); InventoryStore (const InventoryStore& store); InventoryStore& operator= (const InventoryStore& store); virtual InventoryStore* clone() { return new InventoryStore(*this); } virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// Auto-equip items if specific conditions are fulfilled (see the implementation). /// /// \note The item pointed to is not required to exist beyond this function call. /// /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! /// /// @param setOwner Set the owner of the added item to \a actorPtr? /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor); ///< \warning \a iterator can not be an end()-iterator, use unequip function instead bool isEquipped(const MWWorld::ConstPtr& item); ///< Utility function, returns true if the given item is equipped in any slot void setSelectedEnchantItem(const ContainerStoreIterator& iterator); ///< set the selected magic item (for using enchantments of type "Cast once" or "Cast when used") /// \note to unset the selected item, call this method with end() iterator ContainerStoreIterator getSelectedEnchantItem(); ///< @return selected magic item (for using enchantments of type "Cast once" or "Cast when used") /// \note if no item selected, return end() iterator ContainerStoreIterator getSlot (int slot); void unequipAll(const MWWorld::Ptr& actor); ///< Unequip all currently equipped items. void autoEquip (const MWWorld::Ptr& actor); ///< Auto equip items according to stats and item value. const MWMechanics::MagicEffects& getMagicEffects() const; ///< Return magic effects from worn items. virtual void flagAsModified(); ///< \attention This function is internal to the world model and should not be called from /// outside. virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2); ///< @return true if the two specified objects can stack with each other virtual int remove(const Ptr& item, int count, const Ptr& actor); ///< Remove \a count item(s) designated by \a item from this inventory. /// /// @return the number of items actually removed ContainerStoreIterator unequipSlot(int slot, const Ptr& actor); ///< Unequip \a slot. /// /// @return an iterator to the item that was previously in the slot ContainerStoreIterator unequipItem(const Ptr& item, const Ptr& actor); ///< Unequip an item identified by its Ptr. An exception is thrown /// if the item is not currently equipped. /// /// @return an iterator to the item that was previously in the slot /// (it can be re-stacked so its count may be different than when it /// was equipped). void setListener (InventoryStoreListener* listener, const Ptr& actor); ///< Set a listener for various events, see \a InventoryStoreListener InventoryStoreListener* getListener(); void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor); void rechargeItems (float duration); ///< Restore charge on enchanted items. Note this should only be done for the player. void purgeEffect (short effectId); ///< Remove a magic effect void purgeEffect (short effectId, const std::string& sourceId); ///< Remove a magic effect virtual void clear(); ///< Empty container. virtual void writeState (ESM::InventoryState& state) const; virtual void readState (const ESM::InventoryState& state); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/livecellref.cpp000066400000000000000000000035031264522266000233630ustar00rootroot00000000000000#include "livecellref.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "ptr.hpp" #include "class.hpp" #include "esmstore.hpp" MWWorld::LiveCellRefBase::LiveCellRefBase(const std::string& type, const ESM::CellRef &cref) : mClass(&Class::get(type)), mRef(cref), mData(cref) { } void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) { mRef = state.mRef; mData = RefData (state, mData.isDeletedByContentFile()); Ptr ptr (this); if (state.mHasLocals) { std::string scriptId = mClass->getScript (ptr); // Make sure we still have a script. It could have been coming from a content file that is no longer active. if (!scriptId.empty()) { if (const ESM::Script* script = MWBase::Environment::get().getWorld()->getStore().get().search (scriptId)) { try { mData.setLocals (*script); mData.getLocals().read (state.mLocals, scriptId); } catch (const std::exception& exception) { std::cerr << "failed to load state for local script " << scriptId << " because an exception has been thrown: " << exception.what() << std::endl; } } } } mClass->readAdditionalState (ptr, state); } void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const { mRef.writeState(state); ConstPtr ptr (this); mData.write (state, mClass->getScript (ptr)); mClass->writeAdditionalState (ptr, state); } bool MWWorld::LiveCellRefBase::checkStateImp (const ESM::ObjectState& state) { return true; } openmw-openmw-0.38.0/apps/openmw/mwworld/livecellref.hpp000066400000000000000000000072261264522266000233760ustar00rootroot00000000000000#ifndef GAME_MWWORLD_LIVECELLREF_H #define GAME_MWWORLD_LIVECELLREF_H #include #include "cellref.hpp" #include "refdata.hpp" namespace ESM { struct ObjectState; } namespace MWWorld { class Ptr; class ESMStore; class Class; /// Used to create pointers to hold any type of LiveCellRef<> object. struct LiveCellRefBase { const Class *mClass; /** Information about this instance, such as 3D location and rotation * and individual type-dependent data. */ MWWorld::CellRef mRef; /** runtime-data */ RefData mData; LiveCellRefBase(const std::string& type, const ESM::CellRef &cref=ESM::CellRef()); /* Need this for the class to be recognized as polymorphic */ virtual ~LiveCellRefBase() { } virtual void load (const ESM::ObjectState& state) = 0; ///< Load state into a LiveCellRef, that has already been initialised with base and class. /// /// \attention Must not be called with an invalid \a state. virtual void save (ESM::ObjectState& state) const = 0; ///< Save LiveCellRef state into \a state. protected: void loadImp (const ESM::ObjectState& state); ///< Load state into a LiveCellRef, that has already been initialised with base and /// class. /// /// \attention Must not be called with an invalid \a state. void saveImp (ESM::ObjectState& state) const; ///< Save LiveCellRef state into \a state. static bool checkStateImp (const ESM::ObjectState& state); ///< Check if state is valid and report errors. /// /// \return Valid? /// /// \note Does not check if the RefId exists. }; inline bool operator== (const LiveCellRefBase& cellRef, const ESM::RefNum refNum) { return cellRef.mRef.getRefNum()==refNum; } /// A reference to one object (of any type) in a cell. /// /// Constructing this with a CellRef instance in the constructor means that /// in practice (where D is RefData) the possibly mutable data is copied /// across to mData. If later adding data (such as position) to CellRef /// this would have to be manually copied across. template struct LiveCellRef : public LiveCellRefBase { LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) : LiveCellRefBase(typeid(X).name(), cref), mBase(b) {} LiveCellRef(const X* b = NULL) : LiveCellRefBase(typeid(X).name()), mBase(b) {} // The object that this instance is based on. const X* mBase; virtual void load (const ESM::ObjectState& state); ///< Load state into a LiveCellRef, that has already been initialised with base and class. /// /// \attention Must not be called with an invalid \a state. virtual void save (ESM::ObjectState& state) const; ///< Save LiveCellRef state into \a state. static bool checkState (const ESM::ObjectState& state); ///< Check if state is valid and report errors. /// /// \return Valid? /// /// \note Does not check if the RefId exists. }; template void LiveCellRef::load (const ESM::ObjectState& state) { loadImp (state); } template void LiveCellRef::save (ESM::ObjectState& state) const { saveImp (state); } template bool LiveCellRef::checkState (const ESM::ObjectState& state) { return checkStateImp (state); } } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/localscripts.cpp000066400000000000000000000110641264522266000235720ustar00rootroot00000000000000#include "localscripts.hpp" #include #include "esmstore.hpp" #include "cellstore.hpp" #include "class.hpp" #include "containerstore.hpp" namespace { struct AddScriptsVisitor { AddScriptsVisitor(MWWorld::LocalScripts& scripts) : mScripts(scripts) { } MWWorld::LocalScripts& mScripts; bool operator()(const MWWorld::Ptr& ptr) { if (ptr.getRefData().isDeleted()) return true; std::string script = ptr.getClass().getScript(ptr); if (!script.empty()) mScripts.add(script, ptr); return true; } }; struct AddContainerItemScriptsVisitor { AddContainerItemScriptsVisitor(MWWorld::LocalScripts& scripts) : mScripts(scripts) { } MWWorld::LocalScripts& mScripts; bool operator()(const MWWorld::Ptr& containerPtr) { MWWorld::ContainerStore& container = containerPtr.getClass().getContainerStore(containerPtr); for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) { std::string script = it->getClass().getScript(*it); if(script != "") { MWWorld::Ptr item = *it; item.mCell = containerPtr.getCell(); mScripts.add (script, item); } } return true; } }; } MWWorld::LocalScripts::LocalScripts (const MWWorld::ESMStore& store) : mStore (store) { mIter = mScripts.end(); } void MWWorld::LocalScripts::setIgnore (const ConstPtr& ptr) { mIgnore = ptr; } void MWWorld::LocalScripts::startIteration() { mIter = mScripts.begin(); } bool MWWorld::LocalScripts::isFinished() const { if (mIter==mScripts.end()) return true; if (!mIgnore.isEmpty() && mIter->second==mIgnore) { std::list >::iterator iter = mIter; return ++iter==mScripts.end(); } return false; } std::pair MWWorld::LocalScripts::getNext() { assert (!isFinished()); std::list >::iterator iter = mIter++; if (mIgnore.isEmpty() || iter->second!=mIgnore) return *iter; return getNext(); } void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) { if (const ESM::Script *script = mStore.get().search (scriptName)) { try { ptr.getRefData().setLocals (*script); mScripts.push_back (std::make_pair (scriptName, ptr)); } catch (const std::exception& exception) { std::cerr << "failed to add local script " << scriptName << " because an exception has been thrown: " << exception.what() << std::endl; } } else std::cerr << "failed to add local script " << scriptName << " because the script does not exist." << std::endl; } void MWWorld::LocalScripts::addCell (CellStore *cell) { AddScriptsVisitor addScriptsVisitor(*this); cell->forEach(addScriptsVisitor); AddContainerItemScriptsVisitor addContainerItemScriptsVisitor(*this); cell->forEachType(addContainerItemScriptsVisitor); cell->forEachType(addContainerItemScriptsVisitor); cell->forEachType(addContainerItemScriptsVisitor); } void MWWorld::LocalScripts::clear() { mScripts.clear(); } void MWWorld::LocalScripts::clearCell (CellStore *cell) { std::list >::iterator iter = mScripts.begin(); while (iter!=mScripts.end()) { if (iter->second.mCell==cell) { if (iter==mIter) ++mIter; mScripts.erase (iter++); } else ++iter; } } void MWWorld::LocalScripts::remove (RefData *ref) { for (std::list >::iterator iter = mScripts.begin(); iter!=mScripts.end(); ++iter) if (&(iter->second.getRefData()) == ref) { if (iter==mIter) ++mIter; mScripts.erase (iter); break; } } void MWWorld::LocalScripts::remove (const Ptr& ptr) { for (std::list >::iterator iter = mScripts.begin(); iter!=mScripts.end(); ++iter) if (iter->second==ptr) { if (iter==mIter) ++mIter; mScripts.erase (iter); break; } } openmw-openmw-0.38.0/apps/openmw/mwworld/localscripts.hpp000066400000000000000000000032571264522266000236040ustar00rootroot00000000000000#ifndef GAME_MWWORLD_LOCALSCRIPTS_H #define GAME_MWWORLD_LOCALSCRIPTS_H #include #include #include "ptr.hpp" namespace MWWorld { class ESMStore; class CellStore; class RefData; /// \brief List of active local scripts class LocalScripts { std::list > mScripts; std::list >::iterator mIter; MWWorld::ConstPtr mIgnore; const MWWorld::ESMStore& mStore; public: LocalScripts (const MWWorld::ESMStore& store); void setIgnore (const ConstPtr& ptr); ///< Mark a single reference for ignoring during iteration over local scripts (will revoke /// previous ignores). void startIteration(); ///< Set the iterator to the begin of the script list. bool isFinished() const; ///< Is iteration finished? std::pair getNext(); ///< Get next local script (must not be called if isFinished()) void add (const std::string& scriptName, const Ptr& ptr); ///< Add script to collection of active local scripts. void addCell (CellStore *cell); ///< Add all local scripts in a cell. void clear(); ///< Clear active local scripts collection. void clearCell (CellStore *cell); ///< Remove all scripts belonging to \a cell. void remove (RefData *ref); void remove (const Ptr& ptr); ///< Remove script for given reference (ignored if reference does not have a script listed). }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/manualref.cpp000066400000000000000000000061461264522266000230470ustar00rootroot00000000000000#include "manualref.hpp" #include "esmstore.hpp" namespace { template void create(const MWWorld::Store& list, const std::string& name, boost::any& refValue, MWWorld::Ptr& ptrValue) { const T* base = list.find(name); ESM::CellRef cellRef; cellRef.mRefNum.unset(); cellRef.mRefID = name; cellRef.mScale = 1; cellRef.mFactionRank = 0; cellRef.mChargeInt = -1; cellRef.mGoldValue = 1; cellRef.mEnchantmentCharge = -1; cellRef.mTeleport = false; cellRef.mLockLevel = 0; cellRef.mReferenceBlocked = 0; MWWorld::LiveCellRef ref(cellRef, base); refValue = ref; ptrValue = MWWorld::Ptr(&boost::any_cast&>(refValue), 0); } } MWWorld::ManualRef::ManualRef(const MWWorld::ESMStore& store, const std::string& name, const int count) { std::string lowerName = Misc::StringUtils::lowerCase(name); switch (store.find(lowerName)) { case ESM::REC_ACTI: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_ALCH: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_APPA: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_ARMO: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_BOOK: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_CLOT: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_CONT: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_CREA: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_DOOR: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_INGR: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_LEVC: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_LEVI: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_LIGH: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_LOCK: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_MISC: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_NPC_: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_PROB: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_REPA: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_STAT: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_WEAP: create(store.get(), lowerName, mRef, mPtr); break; case ESM::REC_BODY: create(store.get(), lowerName, mRef, mPtr); break; case 0: throw std::logic_error("failed to create manual cell ref for " + lowerName + " (unknown ID)"); default: throw std::logic_error("failed to create manual cell ref for " + lowerName + " (unknown type)"); } mPtr.getRefData().setCount(count); } openmw-openmw-0.38.0/apps/openmw/mwworld/manualref.hpp000066400000000000000000000011131264522266000230410ustar00rootroot00000000000000#ifndef GAME_MWWORLD_MANUALREF_H #define GAME_MWWORLD_MANUALREF_H #include #include "ptr.hpp" namespace MWWorld { /// \brief Manually constructed live cell ref class ManualRef { boost::any mRef; Ptr mPtr; ManualRef (const ManualRef&); ManualRef& operator= (const ManualRef&); public: ManualRef(const MWWorld::ESMStore& store, const std::string& name, const int count = 1); const Ptr& getPtr() const { return mPtr; } }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/nullaction.hpp000066400000000000000000000004101264522266000232360ustar00rootroot00000000000000#ifndef GAME_MWWORLD_NULLACTION_H #define GAME_MWWORLD_NULLACTION_H #include "action.hpp" namespace MWWorld { /// \brief Action: do nothing class NullAction : public Action { virtual void executeImp (const Ptr& actor) {} }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/player.cpp000066400000000000000000000323131264522266000223640ustar00rootroot00000000000000#include "player.hpp" #include #include #include #include #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" #include "class.hpp" #include "ptr.hpp" #include "cellstore.hpp" namespace MWWorld { Player::Player (const ESM::NPC *player) : mCellStore(0), mLastKnownExteriorPosition(0,0,0), mMarkedCell(NULL), mAutoMove(false), mForwardBackward(0), mTeleported(false), mCurrentCrimeId(-1), mPaidCrimeId(-1), mAttackingOrSpell(false) { ESM::CellRef cellRef; cellRef.blank(); cellRef.mRefID = "player"; mPlayer = LiveCellRef(cellRef, player); ESM::Position playerPos = mPlayer.mData.getPosition(); playerPos.pos[0] = playerPos.pos[1] = playerPos.pos[2] = 0; mPlayer.mData.setPosition(playerPos); } void Player::saveSkillsAttributes() { MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); for (int i=0; i& gmst = MWBase::Environment::get().getWorld()->getStore().get(); MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); for(size_t i = 0;i < ESM::Attribute::Length;++i) { // Oh, Bethesda. It's "Intelligence". std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") : ESM::Attribute::sAttributeNames[i]); MWMechanics::AttributeValue value = stats.getAttribute(i); value.setBase(int(gmst.find(name)->getFloat())); stats.setAttribute(i, value); } for(size_t i = 0;i < ESM::Skill::Length;i++) { // Acrobatics is set separately for some reason. if(i == ESM::Skill::Acrobatics) continue; // "Mercantile"! >_< std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") : ESM::Skill::sSkillNames[i]); MWMechanics::SkillValue value = stats.getSkill(i); value.setBase(int(gmst.find(name)->getFloat())); stats.setSkill(i, value); } } void Player::set(const ESM::NPC *player) { mPlayer.mBase = player; } void Player::setCell (MWWorld::CellStore *cellStore) { mCellStore = cellStore; } MWWorld::Ptr Player::getPlayer() { MWWorld::Ptr ptr (&mPlayer, mCellStore); return ptr; } void Player::setBirthSign (const std::string &sign) { mSign = sign; } const std::string& Player::getBirthSign() const { return mSign; } void Player::setDrawState (MWMechanics::DrawState_ state) { MWWorld::Ptr ptr = getPlayer(); ptr.getClass().getNpcStats(ptr).setDrawState (state); } bool Player::getAutoMove() const { return mAutoMove; } void Player::setAutoMove (bool enable) { MWWorld::Ptr ptr = getPlayer(); mAutoMove = enable; int value = mForwardBackward; if (mAutoMove) value = 1; ptr.getClass().getMovementSettings(ptr).mPosition[1] = static_cast(value); } void Player::setLeftRight (int value) { MWWorld::Ptr ptr = getPlayer(); ptr.getClass().getMovementSettings(ptr).mPosition[0] = static_cast(value); } void Player::setForwardBackward (int value) { MWWorld::Ptr ptr = getPlayer(); mForwardBackward = value; if (mAutoMove) value = 1; ptr.getClass().getMovementSettings(ptr).mPosition[1] = static_cast(value); } void Player::setUpDown(int value) { MWWorld::Ptr ptr = getPlayer(); ptr.getClass().getMovementSettings(ptr).mPosition[2] = static_cast(value); } void Player::setRunState(bool run) { MWWorld::Ptr ptr = getPlayer(); ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, run); } void Player::setSneak(bool sneak) { MWWorld::Ptr ptr = getPlayer(); ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, sneak); } void Player::yaw(float yaw) { MWWorld::Ptr ptr = getPlayer(); ptr.getClass().getMovementSettings(ptr).mRotation[2] += yaw; } void Player::pitch(float pitch) { MWWorld::Ptr ptr = getPlayer(); ptr.getClass().getMovementSettings(ptr).mRotation[0] += pitch; } void Player::roll(float roll) { MWWorld::Ptr ptr = getPlayer(); ptr.getClass().getMovementSettings(ptr).mRotation[1] += roll; } MWMechanics::DrawState_ Player::getDrawState() { MWWorld::Ptr ptr = getPlayer(); return ptr.getClass().getNpcStats(ptr).getDrawState(); } void Player::activate() { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; MWWorld::Ptr player = getPlayer(); const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player); if (playerStats.isParalyzed() || playerStats.getKnockedDown()) return; MWWorld::Ptr toActivate = MWBase::Environment::get().getWorld()->getFacedObject(); if (toActivate.isEmpty()) return; if (toActivate.getClass().getName(toActivate) == "") // objects without name presented to user can never be activated return; if (toActivate.getClass().isActor()) { MWMechanics::CreatureStats &stats = toActivate.getClass().getCreatureStats(toActivate); if (stats.getAiSequence().isInCombat() && !stats.isDead()) return; } MWBase::Environment::get().getWorld()->activate(toActivate, player); } bool Player::wasTeleported() const { return mTeleported; } void Player::setTeleported(bool teleported) { mTeleported = teleported; } void Player::setAttackingOrSpell(bool attackingOrSpell) { mAttackingOrSpell = attackingOrSpell; } bool Player::getAttackingOrSpell() const { return mAttackingOrSpell; } bool Player::isInCombat() { return MWBase::Environment::get().getMechanicsManager()->getActorsFighting(getPlayer()).size() != 0; } void Player::markPosition(CellStore *markedCell, ESM::Position markedPosition) { mMarkedCell = markedCell; mMarkedPosition = markedPosition; } void Player::getMarkedPosition(CellStore*& markedCell, ESM::Position &markedPosition) const { markedCell = mMarkedCell; if (mMarkedCell) markedPosition = mMarkedPosition; } void Player::clear() { mCellStore = 0; mSign.clear(); mMarkedCell = 0; mAutoMove = false; mForwardBackward = 0; mTeleported = false; } void Player::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { ESM::Player player; mPlayer.save (player.mObject); player.mCellId = mCellStore->getCell()->getCellId(); player.mCurrentCrimeId = mCurrentCrimeId; player.mPaidCrimeId = mPaidCrimeId; player.mBirthsign = mSign; player.mLastKnownExteriorPosition[0] = mLastKnownExteriorPosition.x(); player.mLastKnownExteriorPosition[1] = mLastKnownExteriorPosition.y(); player.mLastKnownExteriorPosition[2] = mLastKnownExteriorPosition.z(); if (mMarkedCell) { player.mHasMark = true; player.mMarkedPosition = mMarkedPosition; player.mMarkedCell = mMarkedCell->getCell()->getCellId(); } else player.mHasMark = false; player.mAutoMove = mAutoMove ? 1 : 0; for (int i=0; iindexToPosition(0, 0, pos.pos[0], pos.pos[1], true); pos.pos[2] = 0; mPlayer.mData.setPosition(pos); mCellStore = world.getExterior(0,0); } if (!player.mBirthsign.empty()) { const ESM::BirthSign* sign = world.getStore().get().search (player.mBirthsign); if (!sign) throw std::runtime_error ("invalid player state record (birthsign does not exist)"); // To handle the case where a birth sign was edited in between play sessions (does not yet handle removing the old spells) // Also needed for ess-imported savegames which do not specify the birtsign spells in the player's spell list. for (std::vector::const_iterator iter (sign->mPowers.mList.begin()); iter!=sign->mPowers.mList.end(); ++iter) { getPlayer().getClass().getCreatureStats(getPlayer()).getSpells().add (*iter); } } mCurrentCrimeId = player.mCurrentCrimeId; mPaidCrimeId = player.mPaidCrimeId; mSign = player.mBirthsign; mLastKnownExteriorPosition.x() = player.mLastKnownExteriorPosition[0]; mLastKnownExteriorPosition.y() = player.mLastKnownExteriorPosition[1]; mLastKnownExteriorPosition.z() = player.mLastKnownExteriorPosition[2]; if (player.mHasMark && !player.mMarkedCell.mPaged) { // interior cell -> need to check if it exists (exterior cell will be // generated on the fly) if (!world.getStore().get().search (player.mMarkedCell.mWorldspace)) player.mHasMark = false; // drop mark silently } if (player.mHasMark) { mMarkedPosition = player.mMarkedPosition; mMarkedCell = world.getCell (player.mMarkedCell); } else { mMarkedCell = 0; } mAutoMove = player.mAutoMove!=0; mForwardBackward = 0; mTeleported = false; return true; } return false; } int Player::getNewCrimeId() { return ++mCurrentCrimeId; } void Player::recordCrimeId() { mPaidCrimeId = mCurrentCrimeId; } int Player::getCrimeId() const { return mPaidCrimeId; } } openmw-openmw-0.38.0/apps/openmw/mwworld/player.hpp000066400000000000000000000072061264522266000223740ustar00rootroot00000000000000#ifndef GAME_MWWORLD_PLAYER_H #define GAME_MWWORLD_PLAYER_H #include "../mwworld/refdata.hpp" #include "../mwworld/livecellref.hpp" #include "../mwmechanics/drawstate.hpp" #include "../mwmechanics/stat.hpp" #include #include namespace ESM { struct NPC; class ESMWriter; class ESMReader; } namespace Loading { class Listener; } namespace MWWorld { class CellStore; /// \brief NPC object representing the player and additional player data class Player { LiveCellRef mPlayer; MWWorld::CellStore *mCellStore; std::string mSign; osg::Vec3f mLastKnownExteriorPosition; ESM::Position mMarkedPosition; // If no position was marked, this is NULL CellStore* mMarkedCell; bool mAutoMove; int mForwardBackward; bool mTeleported; int mCurrentCrimeId; // the id assigned witnesses int mPaidCrimeId; // the last id paid off (0 bounty) // Saved skills and attributes prior to becoming a werewolf MWMechanics::SkillValue mSaveSkills[ESM::Skill::Length]; MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length]; bool mAttackingOrSpell; public: Player(const ESM::NPC *player); void saveSkillsAttributes(); void restoreSkillsAttributes(); void setWerewolfSkillsAttributes(); // For mark/recall magic effects void markPosition (CellStore* markedCell, ESM::Position markedPosition); void getMarkedPosition (CellStore*& markedCell, ESM::Position& markedPosition) const; /// Interiors can not always be mapped to a world position. However /// world position is still required for divine / almsivi magic effects /// and the player arrow on the global map. void setLastKnownExteriorPosition (const osg::Vec3f& position) { mLastKnownExteriorPosition = position; } osg::Vec3f getLastKnownExteriorPosition() const { return mLastKnownExteriorPosition; } void set (const ESM::NPC *player); void setCell (MWWorld::CellStore *cellStore); MWWorld::Ptr getPlayer(); void setBirthSign(const std::string &sign); const std::string &getBirthSign() const; void setDrawState (MWMechanics::DrawState_ state); MWMechanics::DrawState_ getDrawState(); /// \todo constness /// Activate the object under the crosshair, if any void activate(); bool getAutoMove() const; void setAutoMove (bool enable); void setLeftRight (int value); void setForwardBackward (int value); void setUpDown(int value); void setRunState(bool run); void setSneak(bool sneak); void yaw(float yaw); void pitch(float pitch); void roll(float roll); bool wasTeleported() const; void setTeleported(bool teleported); void setAttackingOrSpell(bool attackingOrSpell); bool getAttackingOrSpell() const; ///Checks all nearby actors to see if anyone has an aipackage against you bool isInCombat(); void clear(); void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; bool readRecord (ESM::ESMReader& reader, uint32_t type); int getNewCrimeId(); // get new id for witnesses void recordCrimeId(); // record the paid crime id when bounty is 0 int getCrimeId() const; // get the last paid crime id }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/projectilemanager.cpp000066400000000000000000000403701264522266000245650ustar00rootroot00000000000000#include "projectilemanager.hpp" #include #include #include #include #include #include #include #include #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwmechanics/combat.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwrender/effectmanager.hpp" #include "../mwrender/animation.hpp" #include "../mwrender/vismask.hpp" #include "../mwrender/renderingmanager.hpp" #include "../mwsound/sound.hpp" #include "../mwphysics/physicssystem.hpp" namespace MWWorld { ProjectileManager::ProjectileManager(osg::Group* parent, Resource::ResourceSystem* resourceSystem, MWRender::RenderingManager* rendering, MWPhysics::PhysicsSystem* physics) : mParent(parent) , mResourceSystem(resourceSystem) , mRendering(rendering) , mPhysics(physics) { } /// Rotates an osg::PositionAttitudeTransform over time. class RotateCallback : public osg::NodeCallback { public: RotateCallback(const osg::Vec3f& axis = osg::Vec3f(0,-1,0), float rotateSpeed = osg::PI*2) : mAxis(axis) , mRotateSpeed(rotateSpeed) { } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::PositionAttitudeTransform* transform = static_cast(node); double time = nv->getFrameStamp()->getSimulationTime(); osg::Quat orient = osg::Quat(time * mRotateSpeed, mAxis); transform->setAttitude(orient); traverse(node, nv); } private: osg::Vec3f mAxis; float mRotateSpeed; }; void ProjectileManager::createModel(State &state, const std::string &model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate) { state.mNode = new osg::PositionAttitudeTransform; state.mNode->setNodeMask(MWRender::Mask_Effect); state.mNode->setPosition(pos); state.mNode->setAttitude(orient); osg::Group* attachTo = state.mNode; if (rotate) { osg::ref_ptr rotateNode (new osg::PositionAttitudeTransform); rotateNode->addUpdateCallback(new RotateCallback()); state.mNode->addChild(rotateNode); attachTo = rotateNode; } mResourceSystem->getSceneManager()->createInstance(model, attachTo); SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; state.mNode->accept(disableFreezeOnCullVisitor); state.mNode->addCullCallback(new SceneUtil::LightListCallback); mParent->addChild(state.mNode); state.mEffectAnimationTime.reset(new MWRender::EffectAnimationTime); SceneUtil::AssignControllerSourcesVisitor assignVisitor (state.mEffectAnimationTime); state.mNode->accept(assignVisitor); } void ProjectileManager::update(State& state, float duration) { state.mEffectAnimationTime->addTime(duration); } void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound, const std::string &spellId, float speed, bool stack, const ESM::EffectList &effects, const Ptr &caster, const std::string &sourceName, const osg::Vec3f& fallbackDirection) { osg::Vec3f pos = mPhysics->getPosition(caster) + osg::Vec3f(0,0,mPhysics->getHalfExtents(caster).z() * 0.5); // Spawn at 0.75 * ActorHeight if (MWBase::Environment::get().getWorld()->isUnderwater(caster.getCell(), pos)) // Underwater casting not possible return; osg::Quat orient; if (caster.getClass().isActor()) orient = osg::Quat(caster.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(caster.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1)); else orient.makeRotate(osg::Vec3f(0,1,0), osg::Vec3f(fallbackDirection)); MagicBoltState state; state.mSourceName = sourceName; state.mId = model; state.mSpellId = spellId; state.mCasterHandle = caster; if (caster.getClass().isActor()) state.mActorId = caster.getClass().getCreatureStats(caster).getActorId(); else state.mActorId = -1; state.mSpeed = speed; state.mStack = stack; state.mSoundId = sound; // Only interested in "on target" effects for (std::vector::const_iterator iter (effects.mList.begin()); iter!=effects.mList.end(); ++iter) { if (iter->mRange == ESM::RT_Target) state.mEffects.mList.push_back(*iter); } MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), model); MWWorld::Ptr ptr = ref.getPtr(); createModel(state, ptr.getClass().getModel(ptr), pos, orient, true); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); state.mSound = sndMgr->playSound3D(pos, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); mMagicBolts.push_back(state); } void ProjectileManager::launchProjectile(Ptr actor, ConstPtr projectile, const osg::Vec3f &pos, const osg::Quat &orient, Ptr bow, float speed, float attackStrength) { ProjectileState state; state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); state.mBowId = bow.getCellRef().getRefId(); state.mVelocity = orient * osg::Vec3f(0,1,0) * speed; state.mId = projectile.getCellRef().getRefId(); state.mCasterHandle = actor; state.mAttackStrength = attackStrength; MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().getRefId()); MWWorld::Ptr ptr = ref.getPtr(); createModel(state, ptr.getClass().getModel(ptr), pos, orient, false); mProjectiles.push_back(state); } void ProjectileManager::update(float dt) { moveProjectiles(dt); moveMagicBolts(dt); } void ProjectileManager::moveMagicBolts(float duration) { for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) { osg::Quat orient = it->mNode->getAttitude(); static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() .find("fTargetSpellMaxSpeed")->getFloat(); float speed = fTargetSpellMaxSpeed * it->mSpeed; osg::Vec3f direction = orient * osg::Vec3f(0,1,0); direction.normalize(); osg::Vec3f pos(it->mNode->getPosition()); osg::Vec3f newPos = pos + direction * duration * speed; if (it->mSound.get()) it->mSound->setPosition(newPos); it->mNode->setPosition(newPos); update(*it, duration); MWWorld::Ptr caster = it->getCaster(); // Check for impact // TODO: use a proper btRigidBody / btGhostObject? MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, 0xff, MWPhysics::CollisionType_Projectile); bool hit = false; if (result.mHit) { hit = true; if (result.mHitObject.isEmpty()) { // terrain } else { MWMechanics::CastSpell cast(caster, result.mHitObject); cast.mHitPosition = pos; cast.mId = it->mSpellId; cast.mSourceName = it->mSourceName; cast.mStack = it->mStack; cast.inflict(result.mHitObject, caster, it->mEffects, ESM::RT_Target, false, true); } } // Explodes when hitting water if (MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos)) hit = true; if (hit) { MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, ESM::RT_Target, it->mSpellId, it->mSourceName); MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); mParent->removeChild(it->mNode); it = mMagicBolts.erase(it); continue; } else ++it; } } void ProjectileManager::moveProjectiles(float duration) { for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end();) { // gravity constant - must be way lower than the gravity affecting actors, since we're not // simulating aerodynamics at all it->mVelocity -= osg::Vec3f(0, 0, 627.2f * 0.1f) * duration; osg::Vec3f pos(it->mNode->getPosition()); osg::Vec3f newPos = pos + it->mVelocity * duration; osg::Quat orient; orient.makeRotate(osg::Vec3f(0,1,0), it->mVelocity); it->mNode->setAttitude(orient); it->mNode->setPosition(newPos); update(*it, duration); MWWorld::Ptr caster = it->getCaster(); // Check for impact // TODO: use a proper btRigidBody / btGhostObject? MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, 0xff, MWPhysics::CollisionType_Projectile); bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos); if (result.mHit || underwater) { if (result.mHit) { MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId); // Try to get a Ptr to the bow that was used. It might no longer exist. MWWorld::Ptr bow = projectileRef.getPtr(); if (!caster.isEmpty()) { MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster); MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), it->mBowId)) bow = *invIt; } if (caster.isEmpty()) caster = result.mHitObject; MWMechanics::projectileHit(caster, result.mHitObject, bow, projectileRef.getPtr(), result.mHitPos, it->mAttackStrength); } if (underwater) mRendering->emitWaterRipple(newPos); mParent->removeChild(it->mNode); it = mProjectiles.erase(it); continue; } ++it; } } void ProjectileManager::clear() { for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) { mParent->removeChild(it->mNode); } mProjectiles.clear(); for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) { mParent->removeChild(it->mNode); MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); } mMagicBolts.clear(); } void ProjectileManager::write(ESM::ESMWriter &writer, Loading::Listener &progress) const { for (std::vector::const_iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) { writer.startRecord(ESM::REC_PROJ); ESM::ProjectileState state; state.mId = it->mId; state.mPosition = ESM::Vector3(osg::Vec3f(it->mNode->getPosition())); state.mOrientation = ESM::Quaternion(osg::Quat(it->mNode->getAttitude())); state.mActorId = it->mActorId; state.mBowId = it->mBowId; state.mVelocity = it->mVelocity; state.mAttackStrength = it->mAttackStrength; state.save(writer); writer.endRecord(ESM::REC_PROJ); } for (std::vector::const_iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) { writer.startRecord(ESM::REC_MPRJ); ESM::MagicBoltState state; state.mId = it->mId; state.mPosition = ESM::Vector3(osg::Vec3f(it->mNode->getPosition())); state.mOrientation = ESM::Quaternion(osg::Quat(it->mNode->getAttitude())); state.mActorId = it->mActorId; state.mSpellId = it->mSpellId; state.mEffects = it->mEffects; state.mSound = it->mSoundId; state.mSourceName = it->mSourceName; state.mSpeed = it->mSpeed; state.mStack = it->mStack; state.save(writer); writer.endRecord(ESM::REC_MPRJ); } } bool ProjectileManager::readRecord(ESM::ESMReader &reader, uint32_t type) { if (type == ESM::REC_PROJ) { ESM::ProjectileState esm; esm.load(reader); ProjectileState state; state.mActorId = esm.mActorId; state.mBowId = esm.mBowId; state.mVelocity = esm.mVelocity; state.mId = esm.mId; state.mAttackStrength = esm.mAttackStrength; std::string model; try { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId); MWWorld::Ptr ptr = ref.getPtr(); model = ptr.getClass().getModel(ptr); } catch(...) { return true; } createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), false); mProjectiles.push_back(state); return true; } else if (type == ESM::REC_MPRJ) { ESM::MagicBoltState esm; esm.load(reader); MagicBoltState state; state.mSourceName = esm.mSourceName; state.mId = esm.mId; state.mSpellId = esm.mSpellId; state.mActorId = esm.mActorId; state.mSpeed = esm.mSpeed; state.mStack = esm.mStack; state.mEffects = esm.mEffects; std::string model; try { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId); MWWorld::Ptr ptr = ref.getPtr(); model = ptr.getClass().getModel(ptr); } catch(...) { return true; } createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); state.mSound = sndMgr->playSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); state.mSoundId = esm.mSound; mMagicBolts.push_back(state); return true; } return false; } int ProjectileManager::countSavedGameRecords() const { return mMagicBolts.size() + mProjectiles.size(); } MWWorld::Ptr ProjectileManager::State::getCaster() { if (!mCasterHandle.isEmpty()) return mCasterHandle; return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); } } openmw-openmw-0.38.0/apps/openmw/mwworld/projectilemanager.hpp000066400000000000000000000072151264522266000245730ustar00rootroot00000000000000#ifndef OPENMW_MWWORLD_PROJECTILEMANAGER_H #define OPENMW_MWWORLD_PROJECTILEMANAGER_H #include #include #include #include #include "../mwbase/soundmanager.hpp" #include "ptr.hpp" namespace MWPhysics { class PhysicsSystem; } namespace Loading { class Listener; } namespace osg { class Group; class Quat; } namespace Resource { class ResourceSystem; } namespace MWRender { class EffectAnimationTime; class RenderingManager; } namespace MWWorld { class ProjectileManager { public: ProjectileManager (osg::Group* parent, Resource::ResourceSystem* resourceSystem, MWRender::RenderingManager* rendering, MWPhysics::PhysicsSystem* physics); /// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used. void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection); void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& pos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength); void update(float dt); /// Removes all current projectiles. Should be called when switching to a new worldspace. void clear(); void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; bool readRecord (ESM::ESMReader& reader, uint32_t type); int countSavedGameRecords() const; private: osg::ref_ptr mParent; Resource::ResourceSystem* mResourceSystem; MWRender::RenderingManager* mRendering; MWPhysics::PhysicsSystem* mPhysics; struct State { osg::ref_ptr mNode; boost::shared_ptr mEffectAnimationTime; int mActorId; // TODO: this will break when the game is saved and reloaded, since there is currently // no way to write identifiers for non-actors to a savegame. MWWorld::Ptr mCasterHandle; MWWorld::Ptr getCaster(); // MW-id of this projectile std::string mId; }; struct MagicBoltState : public State { std::string mSpellId; // Name of item to display as effect source in magic menu (in case we casted an enchantment) std::string mSourceName; ESM::EffectList mEffects; float mSpeed; bool mStack; MWBase::SoundPtr mSound; std::string mSoundId; }; struct ProjectileState : public State { // RefID of the bow or crossbow the actor was using when this projectile was fired (may be empty) std::string mBowId; osg::Vec3f mVelocity; float mAttackStrength; }; std::vector mMagicBolts; std::vector mProjectiles; void moveProjectiles(float dt); void moveMagicBolts(float dt); void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate); void update (State& state, float duration); void operator=(const ProjectileManager&); ProjectileManager(const ProjectileManager&); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/ptr.cpp000066400000000000000000000031341264522266000216740ustar00rootroot00000000000000#include "ptr.hpp" #include #include "containerstore.hpp" #include "class.hpp" #include "livecellref.hpp" const std::string& MWWorld::Ptr::getTypeName() const { if(mRef != 0) return mRef->mClass->getTypeName(); throw std::runtime_error("Can't get type name from an empty object."); } MWWorld::LiveCellRefBase *MWWorld::Ptr::getBase() const { if (!mRef) throw std::runtime_error ("Can't access cell ref pointed to by null Ptr"); return mRef; } MWWorld::CellRef& MWWorld::Ptr::getCellRef() const { assert(mRef); return mRef->mRef; } MWWorld::RefData& MWWorld::Ptr::getRefData() const { assert(mRef); return mRef->mData; } void MWWorld::Ptr::setContainerStore (ContainerStore *store) { assert (store); assert (!mCell); mContainerStore = store; } MWWorld::ContainerStore *MWWorld::Ptr::getContainerStore() const { return mContainerStore; } MWWorld::Ptr::operator const void *() { return mRef; } // ------------------------------------------------------------------------------- const std::string &MWWorld::ConstPtr::getTypeName() const { if(mRef != 0) return mRef->mClass->getTypeName(); throw std::runtime_error("Can't get type name from an empty object."); } const MWWorld::LiveCellRefBase *MWWorld::ConstPtr::getBase() const { if (!mRef) throw std::runtime_error ("Can't access cell ref pointed to by null Ptr"); return mRef; } const MWWorld::ContainerStore *MWWorld::ConstPtr::getContainerStore() const { return mContainerStore; } MWWorld::ConstPtr::operator const void *() { return mRef; } openmw-openmw-0.38.0/apps/openmw/mwworld/ptr.hpp000066400000000000000000000134721264522266000217070ustar00rootroot00000000000000#ifndef GAME_MWWORLD_PTR_H #define GAME_MWWORLD_PTR_H #include #include #include #include "livecellref.hpp" namespace MWWorld { class ContainerStore; class CellStore; struct LiveCellRefBase; /// \brief Pointer to a LiveCellRef class Ptr { public: MWWorld::LiveCellRefBase *mRef; CellStore *mCell; ContainerStore *mContainerStore; public: Ptr(MWWorld::LiveCellRefBase *liveCellRef=0, CellStore *cell=0) : mRef(liveCellRef), mCell(cell), mContainerStore(0) { } bool isEmpty() const { return mRef == 0; } const std::string& getTypeName() const; const Class& getClass() const { if(mRef != 0) return *(mRef->mClass); throw std::runtime_error("Cannot get class of an empty object"); } template MWWorld::LiveCellRef *get() const { MWWorld::LiveCellRef *ref = dynamic_cast*>(mRef); if(ref) return ref; std::stringstream str; str<< "Bad LiveCellRef cast to "<mClass); throw std::runtime_error("Cannot get class of an empty object"); } template const MWWorld::LiveCellRef *get() const { const MWWorld::LiveCellRef *ref = dynamic_cast*>(mRef); if(ref) return ref; std::stringstream str; str<< "Bad LiveCellRef cast to "<mRef; } const RefData& getRefData() const { assert(mRef); return mRef->mData; } const CellStore *getCell() const { assert(mCell); return mCell; } bool isInCell() const { return (mContainerStore == 0) && (mCell != 0); } const ContainerStore *getContainerStore() const; ///< May return a 0-pointer, if reference is not in a container. operator const void *(); ///< Return a 0-pointer, if Ptr is empty; return a non-0-pointer, if Ptr is not empty }; inline bool operator== (const Ptr& left, const Ptr& right) { return left.mRef==right.mRef; } inline bool operator!= (const Ptr& left, const Ptr& right) { return !(left==right); } inline bool operator< (const Ptr& left, const Ptr& right) { return left.mRef= (const Ptr& left, const Ptr& right) { return !(left (const Ptr& left, const Ptr& right) { return rightright); } inline bool operator== (const ConstPtr& left, const ConstPtr& right) { return left.mRef==right.mRef; } inline bool operator!= (const ConstPtr& left, const ConstPtr& right) { return !(left==right); } inline bool operator< (const ConstPtr& left, const ConstPtr& right) { return left.mRef= (const ConstPtr& left, const ConstPtr& right) { return !(left (const ConstPtr& left, const ConstPtr& right) { return rightright); } } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/recordcmp.hpp000066400000000000000000000016531264522266000230560ustar00rootroot00000000000000#ifndef OPENMW_MWWORLD_RECORDCMP_H #define OPENMW_MWWORLD_RECORDCMP_H #include #include #include namespace MWWorld { struct RecordCmp { template bool operator()(const T &x, const T& y) const { return x.mId < y.mId; } }; template <> inline bool RecordCmp::operator()(const ESM::Dialogue &x, const ESM::Dialogue &y) const { return Misc::StringUtils::ciLess(x.mId, y.mId); } template <> inline bool RecordCmp::operator()(const ESM::Cell &x, const ESM::Cell &y) const { return Misc::StringUtils::ciLess(x.mName, y.mName); } template <> inline bool RecordCmp::operator()(const ESM::Pathgrid &x, const ESM::Pathgrid &y) const { return Misc::StringUtils::ciLess(x.mCell, y.mCell); } } // end namespace #endif openmw-openmw-0.38.0/apps/openmw/mwworld/refdata.cpp000066400000000000000000000112721264522266000224770ustar00rootroot00000000000000#include "refdata.hpp" #include #include "customdata.hpp" #include "cellstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" namespace MWWorld { void RefData::copy (const RefData& refData) { mBaseNode = refData.mBaseNode; mLocals = refData.mLocals; mEnabled = refData.mEnabled; mCount = refData.mCount; mPosition = refData.mPosition; mChanged = refData.mChanged; mDeletedByContentFile = refData.mDeletedByContentFile; mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; } void RefData::cleanup() { mBaseNode = 0; delete mCustomData; mCustomData = 0; } RefData::RefData() : mBaseNode(0), mDeletedByContentFile(false), mEnabled (true), mCount (1), mCustomData (0), mChanged(false) { for (int i=0; i<3; ++i) { mPosition.pos[i] = 0; mPosition.rot[i] = 0; } } RefData::RefData (const ESM::CellRef& cellRef) : mBaseNode(0), mDeletedByContentFile(false), mEnabled (true), mCount (1), mPosition (cellRef.mPos), mCustomData (0), mChanged(false) // Loading from ESM/ESP files -> assume unchanged { } RefData::RefData (const ESM::ObjectState& objectState, bool deletedByContentFile) : mBaseNode(0), mDeletedByContentFile(deletedByContentFile), mEnabled (objectState.mEnabled != 0), mCount (objectState.mCount), mPosition (objectState.mPosition), mCustomData (0), mChanged(true) // Loading from a savegame -> assume changed { } RefData::RefData (const RefData& refData) : mBaseNode(0), mCustomData (0) { try { copy (refData); } catch (...) { cleanup(); throw; } } void RefData::write (ESM::ObjectState& objectState, const std::string& scriptId) const { objectState.mHasLocals = mLocals.write (objectState.mLocals, scriptId); objectState.mEnabled = mEnabled; objectState.mCount = mCount; objectState.mPosition = mPosition; } RefData& RefData::operator= (const RefData& refData) { try { cleanup(); copy (refData); } catch (...) { cleanup(); throw; } return *this; } RefData::~RefData() { try { cleanup(); } catch (...) {} } void RefData::setBaseNode(SceneUtil::PositionAttitudeTransform *base) { mBaseNode = base; } SceneUtil::PositionAttitudeTransform* RefData::getBaseNode() { return mBaseNode; } const SceneUtil::PositionAttitudeTransform* RefData::getBaseNode() const { return mBaseNode; } int RefData::getCount() const { return mCount; } void RefData::setLocals (const ESM::Script& script) { if (mLocals.configure (script) && !mLocals.isEmpty()) mChanged = true; } void RefData::setCount (int count) { if(count == 0) MWBase::Environment::get().getWorld()->removeRefScript(this); mChanged = true; mCount = count; } void RefData::setDeletedByContentFile(bool deleted) { mDeletedByContentFile = deleted; } bool RefData::isDeleted() const { return mDeletedByContentFile || mCount == 0; } bool RefData::isDeletedByContentFile() const { return mDeletedByContentFile; } MWScript::Locals& RefData::getLocals() { return mLocals; } bool RefData::isEnabled() const { return mEnabled; } void RefData::enable() { if (!mEnabled) { mChanged = true; mEnabled = true; } } void RefData::disable() { if (mEnabled) { mChanged = true; mEnabled = false; } } void RefData::setPosition(const ESM::Position& pos) { mChanged = true; mPosition = pos; } const ESM::Position& RefData::getPosition() const { return mPosition; } void RefData::setCustomData (CustomData *data) { mChanged = true; // We do not currently track CustomData, so assume anything with a CustomData is changed delete mCustomData; mCustomData = data; } CustomData *RefData::getCustomData() { return mCustomData; } const CustomData *RefData::getCustomData() const { return mCustomData; } bool RefData::hasChanged() const { return mChanged; } } openmw-openmw-0.38.0/apps/openmw/mwworld/refdata.hpp000066400000000000000000000075541264522266000225140ustar00rootroot00000000000000#ifndef GAME_MWWORLD_REFDATA_H #define GAME_MWWORLD_REFDATA_H #include #include "../mwscript/locals.hpp" #include namespace SceneUtil { class PositionAttitudeTransform; } namespace ESM { class Script; class CellRef; struct ObjectState; } namespace MWWorld { class CustomData; class RefData { SceneUtil::PositionAttitudeTransform* mBaseNode; MWScript::Locals mLocals; /// separate delete flag used for deletion by a content file /// @note not stored in the save game file. bool mDeletedByContentFile; bool mEnabled; /// 0: deleted int mCount; ESM::Position mPosition; CustomData *mCustomData; void copy (const RefData& refData); void cleanup(); bool mChanged; public: RefData(); /// @param cellRef Used to copy constant data such as position into this class where it can /// be altered without affecting the original data. This makes it possible /// to reset the position as the original data is still held in the CellRef RefData (const ESM::CellRef& cellRef); RefData (const ESM::ObjectState& objectState, bool deletedByContentFile); ///< Ignores local variables and custom data (not enough context available here to /// perform these operations). RefData (const RefData& refData); ~RefData(); void write (ESM::ObjectState& objectState, const std::string& scriptId = "") const; ///< Ignores custom data (not enough context available here to /// perform this operations). RefData& operator= (const RefData& refData); /// Return base node (can be a null pointer). SceneUtil::PositionAttitudeTransform* getBaseNode(); /// Return base node (can be a null pointer). const SceneUtil::PositionAttitudeTransform* getBaseNode() const; /// Set base node (can be a null pointer). void setBaseNode (SceneUtil::PositionAttitudeTransform* base); int getCount() const; void setLocals (const ESM::Script& script); void setCount (int count); ///< Set object count (an object pile is a simple object with a count >1). /// /// \warning Do not call setCount() to add or remove objects from a /// container or an actor's inventory. Call ContainerStore::add() or /// ContainerStore::remove() instead. /// This flag is only used for content stack loading and will not be stored in the savegame. /// If the object was deleted by gameplay, then use setCount(0) instead. void setDeletedByContentFile(bool deleted); /// Returns true if the object was either deleted by the content file or by gameplay. bool isDeleted() const; /// Returns true if the object was deleted by a content file. bool isDeletedByContentFile() const; MWScript::Locals& getLocals(); bool isEnabled() const; void enable(); void disable(); void setPosition (const ESM::Position& pos); const ESM::Position& getPosition() const; void setCustomData (CustomData *data); ///< Set custom data (potentially replacing old custom data). The ownership of \a data is /// transferred to this. CustomData *getCustomData(); ///< May return a 0-pointer. The ownership of the return data object is not transferred. const CustomData *getCustomData() const; bool hasChanged() const; ///< Has this RefData changed since it was originally loaded? }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/scene.cpp000066400000000000000000000521231264522266000221660ustar00rootroot00000000000000#include "scene.hpp" #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwrender/renderingmanager.hpp" #include "../mwphysics/physicssystem.hpp" #include "player.hpp" #include "localscripts.hpp" #include "esmstore.hpp" #include "class.hpp" #include "cellvisitors.hpp" #include "cellstore.hpp" namespace { void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering) { std::string model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr), rendering.getResourceSystem()->getVFS()); std::string id = ptr.getCellRef().getRefId(); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player ptr.getClass().insertObjectRendering(ptr, model, rendering); ptr.getClass().insertObject (ptr, model, physics); if (ptr.getClass().isActor()) rendering.addWaterRippleEmitter(ptr); } void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering, bool inverseRotationOrder) { if (ptr.getRefData().getBaseNode() != NULL) { osg::Quat worldRotQuat(ptr.getRefData().getPosition().rot[2], osg::Vec3(0,0,-1)); if (!ptr.getClass().isActor()) { float xr = ptr.getRefData().getPosition().rot[0]; float yr = ptr.getRefData().getPosition().rot[1]; if (!inverseRotationOrder) worldRotQuat = worldRotQuat * osg::Quat(yr, osg::Vec3(0,-1,0)) * osg::Quat(xr, osg::Vec3(-1,0,0)); else worldRotQuat = osg::Quat(xr, osg::Vec3(-1,0,0)) * osg::Quat(yr, osg::Vec3(0,-1,0)) * worldRotQuat; } rendering.rotateObject(ptr, worldRotQuat); physics.updateRotation(ptr); } } void updateObjectScale(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering) { if (ptr.getRefData().getBaseNode() != NULL) { float scale = ptr.getCellRef().getScale(); osg::Vec3f scaleVec (scale, scale, scale); ptr.getClass().adjustScale(ptr, scaleVec, true); rendering.scaleObject(ptr, scaleVec); physics.updateScale(ptr); } } struct InsertVisitor { MWWorld::CellStore& mCell; bool mRescale; Loading::Listener& mLoadingListener; MWPhysics::PhysicsSystem& mPhysics; MWRender::RenderingManager& mRendering; InsertVisitor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering); bool operator() (const MWWorld::Ptr& ptr); }; InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering) : mCell (cell), mRescale (rescale), mLoadingListener (loadingListener), mPhysics (physics), mRendering (rendering) {} bool InsertVisitor::operator() (const MWWorld::Ptr& ptr) { if (mRescale) { if (ptr.getCellRef().getScale()<0.5) ptr.getCellRef().setScale(0.5); else if (ptr.getCellRef().getScale()>2) ptr.getCellRef().setScale(2); } if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) { try { addObject(ptr, mPhysics, mRendering); updateObjectRotation(ptr, mPhysics, mRendering, false); } catch (const std::exception& e) { std::string error ("error during rendering '" + ptr.getCellRef().getRefId() + "': "); std::cerr << error + e.what() << std::endl; } } mLoadingListener.increaseProgress (1); return true; } struct AdjustPositionVisitor { bool operator() (const MWWorld::Ptr& ptr) { if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) ptr.getClass().adjustPosition (ptr, false); return true; } }; } namespace MWWorld { void Scene::updateObjectRotation (const Ptr& ptr, bool inverseRotationOrder) { ::updateObjectRotation(ptr, *mPhysics, mRendering, inverseRotationOrder); } void Scene::updateObjectScale(const Ptr &ptr) { ::updateObjectScale(ptr, *mPhysics, mRendering); } void Scene::getGridCenter(int &cellX, int &cellY) { int maxX = std::numeric_limits::min(); int maxY = std::numeric_limits::min(); int minX = std::numeric_limits::max(); int minY = std::numeric_limits::max(); CellStoreCollection::iterator iter = mActiveCells.begin(); while (iter!=mActiveCells.end()) { assert ((*iter)->getCell()->isExterior()); int x = (*iter)->getCell()->getGridX(); int y = (*iter)->getCell()->getGridY(); maxX = std::max(x, maxX); maxY = std::max(y, maxY); minX = std::min(x, minX); minY = std::min(y, minY); ++iter; } cellX = (minX + maxX) / 2; cellY = (minY + maxY) / 2; } void Scene::update (float duration, bool paused) { if (mNeedMapUpdate) { // Note: exterior cell maps must be updated, even if they were visited before, because the set of surrounding cells might be different // (and objects in a different cell can "bleed" into another cells map if they cross the border) std::set cellsToUpdate; for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active) { cellsToUpdate.insert(*active); } MWBase::Environment::get().getWindowManager()->requestMap(cellsToUpdate); mNeedMapUpdate = false; if (mCurrentCell->isExterior()) { int cellX, cellY; getGridCenter(cellX, cellY); MWBase::Environment::get().getWindowManager()->setActiveMap(cellX,cellY,false); } } mRendering.update (duration, paused); } void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; ListAndResetObjectsVisitor visitor; (*iter)->forEach(visitor); for (std::vector::const_iterator iter2 (visitor.mObjects.begin()); iter2!=visitor.mObjects.end(); ++iter2) { mPhysics->remove(*iter2); } if ((*iter)->getCell()->isExterior()) { ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search( (*iter)->getCell()->getGridX(), (*iter)->getCell()->getGridY() ); if (land && land->mDataTypes&ESM::Land::DATA_VHGT) mPhysics->removeHeightField ((*iter)->getCell()->getGridX(), (*iter)->getCell()->getGridY()); } MWBase::Environment::get().getMechanicsManager()->drop (*iter); mRendering.removeCell(*iter); MWBase::Environment::get().getWindowManager()->removeCell(*iter); MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); MWBase::Environment::get().getSoundManager()->stopSound (*iter); mActiveCells.erase(*iter); } void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener) { std::pair result = mActiveCells.insert(cell); if(result.second) { std::cout << "Loading cell " << cell->getCell()->getDescription() << std::endl; float verts = ESM::Land::LAND_SIZE; float worldsize = ESM::Land::REAL_SIZE; // Load terrain physics first... if (cell->getCell()->isExterior()) { ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search( cell->getCell()->getGridX(), cell->getCell()->getGridY() ); if (land && land->mDataTypes&ESM::Land::DATA_VHGT) { // Actually only VHGT is needed here, but we'll need the rest for rendering anyway. // Load everything now to reduce IO overhead. const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; const ESM::Land::LandData *data = land->getLandData (flags); mPhysics->addHeightField (data->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(), worldsize / (verts-1), verts); } } cell->respawn(); // ... then references. This is important for adjustPosition to work correctly. /// \todo rescale depending on the state of a new GMST insertCell (*cell, true, loadingListener); mRendering.addCell(cell); bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior(); float waterLevel = cell->getWaterLevel(); mRendering.setWaterEnabled(waterEnabled); if (waterEnabled) { mPhysics->enableWater(waterLevel); mRendering.setWaterHeight(waterLevel); } else mPhysics->disableWater(); if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) mRendering.configureAmbient(cell->getCell()); } // register local scripts // ??? Should this go into the above if block ??? MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); } void Scene::changeToVoid() { CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) unloadCell (active++); assert(mActiveCells.empty()); mCurrentCell = NULL; } void Scene::playerMoved(const osg::Vec3f &pos) { if (!mCurrentCell || !mCurrentCell->isExterior()) return; // figure out the center of the current cell grid (*not* necessarily mCurrentCell, which is the cell the player is in) int cellX, cellY; getGridCenter(cellX, cellY); float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); const float maxDistance = 8192/2 + 1024; // 1/2 cell size + threshold float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); if (distance > maxDistance) { int newX, newY; MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newX, newY); changeCellGrid(newX, newY); //mRendering.updateTerrain(); } } void Scene::changeCellGrid (int X, int Y) { Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); //mRendering.enableTerrain(true); std::string loadingExteriorText = "#{sLoadingMessage3}"; loadingListener->setLabel(loadingExteriorText); const int halfGridSize = Settings::Manager::getInt("exterior cell load distance", "Cells"); CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) { if ((*active)->getCell()->isExterior()) { if (std::abs (X-(*active)->getCell()->getGridX())<=halfGridSize && std::abs (Y-(*active)->getCell()->getGridY())<=halfGridSize) { // keep cells within the new grid ++active; continue; } } unloadCell (active++); } int refsToLoad = 0; // get the number of refs to load for (int x=X-halfGridSize; x<=X+halfGridSize; ++x) { for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y) { CellStoreCollection::iterator iter = mActiveCells.begin(); while (iter!=mActiveCells.end()) { assert ((*iter)->getCell()->isExterior()); if (x==(*iter)->getCell()->getGridX() && y==(*iter)->getCell()->getGridY()) break; ++iter; } if (iter==mActiveCells.end()) refsToLoad += MWBase::Environment::get().getWorld()->getExterior(x, y)->count(); } } loadingListener->setProgressRange(refsToLoad); // Load cells for (int x=X-halfGridSize; x<=X+halfGridSize; ++x) { for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y) { CellStoreCollection::iterator iter = mActiveCells.begin(); while (iter!=mActiveCells.end()) { assert ((*iter)->getCell()->isExterior()); if (x==(*iter)->getCell()->getGridX() && y==(*iter)->getCell()->getGridY()) break; ++iter; } if (iter==mActiveCells.end()) { CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); loadCell (cell, loadingListener); } } } CellStore* current = MWBase::Environment::get().getWorld()->getExterior(X,Y); MWBase::Environment::get().getWindowManager()->changeCell(current); mCellChanged = true; // Delay the map update until scripts have been given a chance to run. // If we don't do this, objects that should be disabled will still appear on the map. mNeedMapUpdate = true; mRendering.getResourceSystem()->clearCache(); } void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) { mCurrentCell = cell; MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr old = world->getPlayerPtr(); world->getPlayer().setCell(cell); MWWorld::Ptr player = world->getPlayerPtr(); mRendering.updatePlayerPtr(player); if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); float x = pos.rot[0]; float y = pos.rot[1]; float z = pos.rot[2]; world->rotateObject(player, x, y, z); player.getClass().adjustPosition(player, true); } MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->updateCell(old, player); mechMgr->watchActor(player); MWBase::Environment::get().getWorld()->adjustSky(); } Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNeedMapUpdate(false) { } Scene::~Scene() { } bool Scene::hasCellChanged() const { return mCellChanged; } const Scene::CellStoreCollection& Scene::getActiveCells() const { return mActiveCells; } void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); bool loadcell = (mCurrentCell == NULL); if(!loadcell) loadcell = *mCurrentCell != *cell; MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); std::string loadingInteriorText = "#{sLoadingMessage2}"; loadingListener->setLabel(loadingInteriorText); Loading::ScopedLoad load(loadingListener); //mRendering.enableTerrain(false); if(!loadcell) { MWBase::World *world = MWBase::Environment::get().getWorld(); world->moveObject(world->getPlayerPtr(), position.pos[0], position.pos[1], position.pos[2]); float x = position.rot[0]; float y = position.rot[1]; float z = position.rot[2]; world->rotateObject(world->getPlayerPtr(), x, y, z); world->getPlayerPtr().getClass().adjustPosition(world->getPlayerPtr(), true); MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); return; } std::cout << "Changing to interior\n"; // unload int current = 0; CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) { unloadCell (active++); ++current; } int refsToLoad = cell->count(); loadingListener->setProgressRange(refsToLoad); // Load cell. loadCell (cell, loadingListener); changePlayerCell(cell, position, true); // adjust fog mRendering.configureFog(mCurrentCell->getCell()); // Sky system MWBase::Environment::get().getWorld()->adjustSky(); mCellChanged = true; MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); // Delay the map update until scripts have been given a chance to run. // If we don't do this, objects that should be disabled will still appear on the map. mNeedMapUpdate = true; mRendering.getResourceSystem()->clearCache(); } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos) { int x = 0; int y = 0; MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y); changeCellGrid(x, y); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); //mRendering.updateTerrain(); } CellStore* Scene::getCurrentCell () { return mCurrentCell; } void Scene::markCellAsUnchanged() { mCellChanged = false; } void Scene::insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener) { InsertVisitor insertVisitor (cell, rescale, *loadingListener, *mPhysics, mRendering); cell.forEach (insertVisitor); // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order AdjustPositionVisitor adjustPosVisitor; cell.forEach (adjustPosVisitor); } void Scene::addObjectToScene (const Ptr& ptr) { try { addObject(ptr, *mPhysics, mRendering); updateObjectRotation(ptr, false); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); } catch (std::exception& e) { std::cerr << "error during rendering '" << ptr.getCellRef().getRefId() << "': " << e.what() << std::endl; } } void Scene::removeObjectFromScene (const Ptr& ptr) { MWBase::Environment::get().getMechanicsManager()->remove (ptr); MWBase::Environment::get().getSoundManager()->stopSound3D (ptr); mPhysics->remove(ptr); mRendering.removeObject (ptr); if (ptr.getClass().isActor()) mRendering.removeWaterRippleEmitter(ptr); } bool Scene::isCellActive(const CellStore &cell) { CellStoreCollection::iterator active = mActiveCells.begin(); while (active != mActiveCells.end()) { if (**active == cell) { return true; } ++active; } return false; } Ptr Scene::searchPtrViaActorId (int actorId) { for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); iter!=mActiveCells.end(); ++iter) if (Ptr ptr = (*iter)->searchViaActorId (actorId)) return ptr; return Ptr(); } } openmw-openmw-0.38.0/apps/openmw/mwworld/scene.hpp000066400000000000000000000054641264522266000222010ustar00rootroot00000000000000#ifndef GAME_MWWORLD_SCENE_H #define GAME_MWWORLD_SCENE_H //#include "../mwrender/renderingmanager.hpp" #include "ptr.hpp" #include "globals.hpp" #include namespace osg { class Vec3f; } namespace ESM { struct Position; } namespace Files { class Collections; } namespace Loading { class Listener; } namespace MWRender { class SkyManager; class RenderingManager; } namespace MWPhysics { class PhysicsSystem; } namespace MWWorld { class Player; class CellStore; class Scene { public: typedef std::set CellStoreCollection; private: CellStore* mCurrentCell; // the cell the player is in CellStoreCollection mActiveCells; bool mCellChanged; MWPhysics::PhysicsSystem *mPhysics; MWRender::RenderingManager& mRendering; bool mNeedMapUpdate; void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center void changeCellGrid (int X, int Y); void getGridCenter(int& cellX, int& cellY); public: Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics); ~Scene(); void unloadCell (CellStoreCollection::iterator iter); void loadCell (CellStore *cell, Loading::Listener* loadingListener); void playerMoved (const osg::Vec3f& pos); void changePlayerCell (CellStore* newCell, const ESM::Position& position, bool adjustPlayerPos); CellStore *getCurrentCell(); const CellStoreCollection& getActiveCells () const; bool hasCellChanged() const; ///< Has the set of active cells changed, since the last frame? void changeToInteriorCell (const std::string& cellName, const ESM::Position& position); ///< Move to interior cell. void changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos); ///< Move to exterior cell. void changeToVoid(); ///< Change into a void void markCellAsUnchanged(); void update (float duration, bool paused); void addObjectToScene (const Ptr& ptr); ///< Add an object that already exists in the world model to the scene. void removeObjectFromScene (const Ptr& ptr); ///< Remove an object from the scene, but not from the world model. void updateObjectRotation (const Ptr& ptr, bool inverseRotationOrder); void updateObjectScale(const Ptr& ptr); bool isCellActive(const CellStore &cell); Ptr searchPtrViaActorId (int actorId); }; } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/store.cpp000066400000000000000000001071521264522266000222300ustar00rootroot00000000000000#include "store.hpp" #include #include #include #include #include #include namespace { template class GetRecords { const std::string mFind; std::vector *mRecords; public: GetRecords(const std::string &str, std::vector *records) : mFind(Misc::StringUtils::lowerCase(str)), mRecords(records) { } void operator()(const T *item) { if(Misc::StringUtils::ciCompareLen(mFind, item->mId, mFind.size()) == 0) mRecords->push_back(item); } }; struct Compare { bool operator()(const ESM::Land *x, const ESM::Land *y) { if (x->mX == y->mX) { return x->mY < y->mY; } return x->mX < y->mX; } }; } namespace MWWorld { RecordId::RecordId(const std::string &id, bool isDeleted) : mId(id), mIsDeleted(isDeleted) {} template IndexedStore::IndexedStore() { } template typename IndexedStore::iterator IndexedStore::begin() const { return mStatic.begin(); } template typename IndexedStore::iterator IndexedStore::end() const { return mStatic.end(); } template void IndexedStore::load(ESM::ESMReader &esm) { T record; bool isDeleted = false; record.load(esm, isDeleted); // Try to overwrite existing record std::pair ret = mStatic.insert(std::make_pair(record.mIndex, record)); if (!ret.second) ret.first->second = record; } template int IndexedStore::getSize() const { return mStatic.size(); } template void IndexedStore::setUp() { } template const T *IndexedStore::search(int index) const { typename Static::const_iterator it = mStatic.find(index); if (it != mStatic.end()) return &(it->second); return NULL; } template const T *IndexedStore::find(int index) const { const T *ptr = search(index); if (ptr == 0) { std::ostringstream msg; msg << T::getRecordType() << " with index " << index << " not found"; throw std::runtime_error(msg.str()); } return ptr; } // Need to instantiate these before they're used template class IndexedStore; template class IndexedStore; template Store::Store() { } template Store::Store(const Store& orig) : mStatic(orig.mStatic) { } template void Store::clearDynamic() { // remove the dynamic part of mShared assert(mShared.size() >= mStatic.size()); mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); mDynamic.clear(); } template const T *Store::search(const std::string &id) const { T item; item.mId = Misc::StringUtils::lowerCase(id); typename Dynamic::const_iterator dit = mDynamic.find(item.mId); if (dit != mDynamic.end()) { return &dit->second; } typename std::map::const_iterator it = mStatic.find(item.mId); if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { return &(it->second); } return 0; } template bool Store::isDynamic(const std::string &id) const { typename Dynamic::const_iterator dit = mDynamic.find(id); return (dit != mDynamic.end()); } template const T *Store::searchRandom(const std::string &id) const { std::vector results; std::for_each(mShared.begin(), mShared.end(), GetRecords(id, &results)); if(!results.empty()) return results[Misc::Rng::rollDice(results.size())]; return NULL; } template const T *Store::find(const std::string &id) const { const T *ptr = search(id); if (ptr == 0) { std::ostringstream msg; msg << T::getRecordType() << " '" << id << "' not found"; throw std::runtime_error(msg.str()); } return ptr; } template const T *Store::findRandom(const std::string &id) const { const T *ptr = searchRandom(id); if(ptr == 0) { std::ostringstream msg; msg << T::getRecordType() << " starting with '"< RecordId Store::load(ESM::ESMReader &esm) { T record; bool isDeleted = false; record.load(esm, isDeleted); Misc::StringUtils::lowerCaseInPlace(record.mId); std::pair inserted = mStatic.insert(std::make_pair(record.mId, record)); if (inserted.second) mShared.push_back(&inserted.first->second); else inserted.first->second = record; return RecordId(record.mId, isDeleted); } template void Store::setUp() { } template typename Store::iterator Store::begin() const { return mShared.begin(); } template typename Store::iterator Store::end() const { return mShared.end(); } template size_t Store::getSize() const { return mShared.size(); } template int Store::getDynamicSize() const { return mDynamic.size(); } template void Store::listIdentifier(std::vector &list) const { list.reserve(list.size() + getSize()); typename std::vector::const_iterator it = mShared.begin(); for (; it != mShared.end(); ++it) { list.push_back((*it)->mId); } } template T *Store::insert(const T &item) { std::string id = Misc::StringUtils::lowerCase(item.mId); std::pair result = mDynamic.insert(std::pair(id, item)); T *ptr = &result.first->second; if (result.second) { mShared.push_back(ptr); } else { *ptr = item; } return ptr; } template T *Store::insertStatic(const T &item) { std::string id = Misc::StringUtils::lowerCase(item.mId); std::pair result = mStatic.insert(std::pair(id, item)); T *ptr = &result.first->second; if (result.second) { mShared.push_back(ptr); } else { *ptr = item; } return ptr; } template bool Store::eraseStatic(const std::string &id) { T item; item.mId = Misc::StringUtils::lowerCase(id); typename std::map::iterator it = mStatic.find(item.mId); if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { // delete from the static part of mShared typename std::vector::iterator sharedIter = mShared.begin(); typename std::vector::iterator end = sharedIter + mStatic.size(); while (sharedIter != mShared.end() && sharedIter != end) { if((*sharedIter)->mId == item.mId) { mShared.erase(sharedIter); break; } ++sharedIter; } mStatic.erase(it); } return true; } template bool Store::erase(const std::string &id) { std::string key = Misc::StringUtils::lowerCase(id); typename Dynamic::iterator it = mDynamic.find(key); if (it == mDynamic.end()) { return false; } mDynamic.erase(it); // have to reinit the whole shared part assert(mShared.size() >= mStatic.size()); mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); for (it = mDynamic.begin(); it != mDynamic.end(); ++it) { mShared.push_back(&it->second); } return true; } template bool Store::erase(const T &item) { return erase(item.mId); } template void Store::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { for (typename Dynamic::const_iterator iter (mDynamic.begin()); iter!=mDynamic.end(); ++iter) { writer.startRecord (T::sRecordId); iter->second.save (writer); writer.endRecord (T::sRecordId); } } template RecordId Store::read(ESM::ESMReader& reader) { T record; bool isDeleted = false; record.load (reader, isDeleted); insert (record); return RecordId(record.mId, isDeleted); } // LandTexture //========================================================================= Store::Store() { mStatic.push_back(LandTextureList()); LandTextureList <exl = mStatic[0]; // More than enough to hold Morrowind.esm. Extra lists for plugins will we // added on-the-fly in a different method. ltexl.reserve(128); } const ESM::LandTexture *Store::search(size_t index, size_t plugin) const { assert(plugin < mStatic.size()); const LandTextureList <exl = mStatic[plugin]; if (index >= ltexl.size()) return NULL; return <exl[index]; } const ESM::LandTexture *Store::find(size_t index, size_t plugin) const { const ESM::LandTexture *ptr = search(index, plugin); if (ptr == 0) { std::ostringstream msg; msg << "Land texture with index " << index << " not found"; throw std::runtime_error(msg.str()); } return ptr; } size_t Store::getSize() const { return mStatic.size(); } size_t Store::getSize(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].size(); } RecordId Store::load(ESM::ESMReader &esm, size_t plugin) { ESM::LandTexture lt; bool isDeleted = false; lt.load(esm, isDeleted); assert(plugin < mStatic.size()); LandTextureList <exl = mStatic[plugin]; if(lt.mIndex + 1 > (int)ltexl.size()) ltexl.resize(lt.mIndex+1); // Store it ltexl[lt.mIndex] = lt; return RecordId(lt.mId, isDeleted); } RecordId Store::load(ESM::ESMReader &esm) { return load(esm, esm.getIndex()); } Store::iterator Store::begin(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].begin(); } Store::iterator Store::end(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].end(); } void Store::resize(size_t num) { if (mStatic.size() < num) mStatic.resize(num); } // Land //========================================================================= Store::~Store() { for (std::vector::const_iterator it = mStatic.begin(); it != mStatic.end(); ++it) { delete *it; } } size_t Store::getSize() const { return mStatic.size(); } Store::iterator Store::begin() const { return iterator(mStatic.begin()); } Store::iterator Store::end() const { return iterator(mStatic.end()); } ESM::Land *Store::search(int x, int y) const { ESM::Land land; land.mX = x, land.mY = y; std::vector::const_iterator it = std::lower_bound(mStatic.begin(), mStatic.end(), &land, Compare()); if (it != mStatic.end() && (*it)->mX == x && (*it)->mY == y) { return const_cast(*it); } return 0; } ESM::Land *Store::find(int x, int y) const { ESM::Land *ptr = search(x, y); if (ptr == 0) { std::ostringstream msg; msg << "Land at (" << x << ", " << y << ") not found"; throw std::runtime_error(msg.str()); } return ptr; } RecordId Store::load(ESM::ESMReader &esm) { ESM::Land *ptr = new ESM::Land(); bool isDeleted = false; ptr->load(esm, isDeleted); // Same area defined in multiple plugins? -> last plugin wins // Can't use search() because we aren't sorted yet - is there any other way to speed this up? for (std::vector::iterator it = mStatic.begin(); it != mStatic.end(); ++it) { if ((*it)->mX == ptr->mX && (*it)->mY == ptr->mY) { delete *it; mStatic.erase(it); break; } } mStatic.push_back(ptr); return RecordId("", isDeleted); } void Store::setUp() { std::sort(mStatic.begin(), mStatic.end(), Compare()); } // Cell //========================================================================= const ESM::Cell *Store::search(const ESM::Cell &cell) const { if (cell.isExterior()) { return search(cell.getGridX(), cell.getGridY()); } return search(cell.mName); } void Store::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell) { //Handling MovedCellRefs, there is no way to do it inside loadcell while (esm.isNextSub("MVRF")) { ESM::CellRef ref; ESM::MovedCellRef cMRef; cell->getNextMVRF(esm, cMRef); ESM::Cell *cellAlt = const_cast(searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following // implementation when the oher implementation works as well. bool deleted = false; cell->getNextRef(esm, ref, deleted); // Add data required to make reference appear in the correct cell. // We should not need to test for duplicates, as this part of the code is pre-cell merge. cell->mMovedRefs.push_back(cMRef); // But there may be duplicates here! if (!deleted) { ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefNum); if (iter == cellAlt->mLeasedRefs.end()) cellAlt->mLeasedRefs.push_back(ref); else *iter = ref; } } } const ESM::Cell *Store::search(const std::string &id) const { ESM::Cell cell; cell.mName = Misc::StringUtils::lowerCase(id); std::map::const_iterator it = mInt.find(cell.mName); if (it != mInt.end() && Misc::StringUtils::ciEqual(it->second.mName, id)) { return &(it->second); } DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName); if (dit != mDynamicInt.end()) { return &dit->second; } return 0; } const ESM::Cell *Store::search(int x, int y) const { ESM::Cell cell; cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } DynamicExt::const_iterator dit = mDynamicExt.find(key); if (dit != mDynamicExt.end()) { return &dit->second; } return 0; } const ESM::Cell *Store::searchOrCreate(int x, int y) { std::pair key(x, y); DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } DynamicExt::const_iterator dit = mDynamicExt.find(key); if (dit != mDynamicExt.end()) { return &dit->second; } ESM::Cell newCell; newCell.mData.mX = x; newCell.mData.mY = y; newCell.mData.mFlags = ESM::Cell::HasWater; newCell.mAmbi.mAmbient = 0; newCell.mAmbi.mSunlight = 0; newCell.mAmbi.mFog = 0; newCell.mAmbi.mFogDensity = 0; return &mExt.insert(std::make_pair(key, newCell)).first->second; } const ESM::Cell *Store::find(const std::string &id) const { const ESM::Cell *ptr = search(id); if (ptr == 0) { std::ostringstream msg; msg << "Interior cell '" << id << "' not found"; throw std::runtime_error(msg.str()); } return ptr; } const ESM::Cell *Store::find(int x, int y) const { const ESM::Cell *ptr = search(x, y); if (ptr == 0) { std::ostringstream msg; msg << "Exterior at (" << x << ", " << y << ") not found"; throw std::runtime_error(msg.str()); } return ptr; } void Store::setUp() { typedef DynamicExt::iterator ExtIterator; typedef std::map::iterator IntIterator; mSharedInt.clear(); mSharedInt.reserve(mInt.size()); for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) { mSharedInt.push_back(&(it->second)); } mSharedExt.clear(); mSharedExt.reserve(mExt.size()); for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) { mSharedExt.push_back(&(it->second)); } } RecordId Store::load(ESM::ESMReader &esm) { // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell, // and we merge all this data into one Cell object. However, we can't simply search for the cell id, // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they // are not available until both cells have been loaded at least partially! // All cells have a name record, even nameless exterior cells. ESM::Cell cell; bool isDeleted = false; // Load the (x,y) coordinates of the cell, if it is an exterior cell, // so we can find the cell we need to merge with cell.loadNameAndData(esm, isDeleted); std::string idLower = Misc::StringUtils::lowerCase(cell.mName); if(cell.mData.mFlags & ESM::Cell::Interior) { // Store interior cell by name, try to merge with existing parent data. ESM::Cell *oldcell = const_cast(search(idLower)); if (oldcell) { // merge new cell into old cell // push the new references on the list of references to manage (saveContext = true) oldcell->mData = cell.mData; oldcell->mName = cell.mName; // merge name just to be sure (ID will be the same, but case could have been changed) oldcell->loadCell(esm, true); } else { // spawn a new cell cell.loadCell(esm, true); mInt[idLower] = cell; } } else { // Store exterior cells by grid position, try to merge with existing parent data. ESM::Cell *oldcell = const_cast(search(cell.getGridX(), cell.getGridY())); if (oldcell) { // merge new cell into old cell oldcell->mData = cell.mData; oldcell->mName = cell.mName; oldcell->loadCell(esm, false); // handle moved ref (MVRF) subrecords handleMovedCellRefs (esm, &cell); // push the new references on the list of references to manage oldcell->postLoad(esm); // merge lists of leased references, use newer data in case of conflict for (ESM::MovedCellRefTracker::const_iterator it = cell.mMovedRefs.begin(); it != cell.mMovedRefs.end(); ++it) { // remove reference from current leased ref tracker and add it to new cell ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum); if (itold != oldcell->mMovedRefs.end()) { ESM::MovedCellRef target0 = *itold; ESM::Cell *wipecell = const_cast(search(target0.mTarget[0], target0.mTarget[1])); ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefNum); wipecell->mLeasedRefs.erase(it_lease); *itold = *it; } else oldcell->mMovedRefs.push_back(*it); } // We don't need to merge mLeasedRefs of cell / oldcell. This list is filled when another cell moves a // reference to this cell, so the list for the new cell should be empty. The list for oldcell, // however, could have leased refs in it and so should be kept. } else { // spawn a new cell cell.loadCell(esm, false); // handle moved ref (MVRF) subrecords handleMovedCellRefs (esm, &cell); // push the new references on the list of references to manage cell.postLoad(esm); mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell; } } return RecordId(cell.mName, isDeleted); } Store::iterator Store::intBegin() const { return iterator(mSharedInt.begin()); } Store::iterator Store::intEnd() const { return iterator(mSharedInt.end()); } Store::iterator Store::extBegin() const { return iterator(mSharedExt.begin()); } Store::iterator Store::extEnd() const { return iterator(mSharedExt.end()); } const ESM::Cell *Store::searchExtByName(const std::string &id) const { ESM::Cell *cell = 0; std::vector::const_iterator it = mSharedExt.begin(); for (; it != mSharedExt.end(); ++it) { if (Misc::StringUtils::ciEqual((*it)->mName, id)) { if ( cell == 0 || ( (*it)->mData.mX > cell->mData.mX ) || ( (*it)->mData.mX == cell->mData.mX && (*it)->mData.mY > cell->mData.mY ) ) { cell = *it; } } } return cell; } const ESM::Cell *Store::searchExtByRegion(const std::string &id) const { ESM::Cell *cell = 0; std::vector::const_iterator it = mSharedExt.begin(); for (; it != mSharedExt.end(); ++it) { if (Misc::StringUtils::ciEqual((*it)->mRegion, id)) { if ( cell == 0 || ( (*it)->mData.mX > cell->mData.mX ) || ( (*it)->mData.mX == cell->mData.mX && (*it)->mData.mY > cell->mData.mY ) ) { cell = *it; } } } return cell; } size_t Store::getSize() const { return mSharedInt.size() + mSharedExt.size(); } void Store::listIdentifier(std::vector &list) const { list.reserve(list.size() + mSharedInt.size()); std::vector::const_iterator it = mSharedInt.begin(); for (; it != mSharedInt.end(); ++it) { list.push_back((*it)->mName); } } ESM::Cell *Store::insert(const ESM::Cell &cell) { if (search(cell) != 0) { std::ostringstream msg; msg << "Failed to create "; msg << ((cell.isExterior()) ? "exterior" : "interior"); msg << " cell"; throw std::runtime_error(msg.str()); } ESM::Cell *ptr; if (cell.isExterior()) { std::pair key(cell.getGridX(), cell.getGridY()); // duplicate insertions are avoided by search(ESM::Cell &) std::pair result = mDynamicExt.insert(std::make_pair(key, cell)); ptr = &result.first->second; mSharedExt.push_back(ptr); } else { std::string key = Misc::StringUtils::lowerCase(cell.mName); // duplicate insertions are avoided by search(ESM::Cell &) std::pair result = mDynamicInt.insert(std::make_pair(key, cell)); ptr = &result.first->second; mSharedInt.push_back(ptr); } return ptr; } bool Store::erase(const ESM::Cell &cell) { if (cell.isExterior()) { return erase(cell.getGridX(), cell.getGridY()); } return erase(cell.mName); } bool Store::erase(const std::string &id) { std::string key = Misc::StringUtils::lowerCase(id); DynamicInt::iterator it = mDynamicInt.find(key); if (it == mDynamicInt.end()) { return false; } mDynamicInt.erase(it); mSharedInt.erase( mSharedInt.begin() + mSharedInt.size(), mSharedInt.end() ); for (it = mDynamicInt.begin(); it != mDynamicInt.end(); ++it) { mSharedInt.push_back(&it->second); } return true; } bool Store::erase(int x, int y) { std::pair key(x, y); DynamicExt::iterator it = mDynamicExt.find(key); if (it == mDynamicExt.end()) { return false; } mDynamicExt.erase(it); mSharedExt.erase( mSharedExt.begin() + mSharedExt.size(), mSharedExt.end() ); for (it = mDynamicExt.begin(); it != mDynamicExt.end(); ++it) { mSharedExt.push_back(&it->second); } return true; } // Pathgrid //========================================================================= Store::Store() : mCells(NULL) { } void Store::setCells(Store& cells) { mCells = &cells; } RecordId Store::load(ESM::ESMReader &esm) { ESM::Pathgrid pathgrid; bool isDeleted = false; pathgrid.load(esm, isDeleted); // Unfortunately the Pathgrid record model does not specify whether the pathgrid belongs to an interior or exterior cell. // For interior cells, mCell is the cell name, but for exterior cells it is either the cell name or if that doesn't exist, the cell's region name. // mX and mY will be (0,0) for interior cells, but there is also an exterior cell with the coordinates of (0,0), so that doesn't help. // Check whether mCell is an interior cell. This isn't perfect, will break if a Region with the same name as an interior cell is created. // A proper fix should be made for future versions of the file format. bool interior = mCells->search(pathgrid.mCell) != NULL; // Try to overwrite existing record if (interior) { std::pair ret = mInt.insert(std::make_pair(pathgrid.mCell, pathgrid)); if (!ret.second) ret.first->second = pathgrid; } else { std::pair ret = mExt.insert(std::make_pair(std::make_pair(pathgrid.mData.mX, pathgrid.mData.mY), pathgrid)); if (!ret.second) ret.first->second = pathgrid; } return RecordId("", isDeleted); } size_t Store::getSize() const { return mInt.size() + mExt.size(); } void Store::setUp() { } const ESM::Pathgrid *Store::search(int x, int y) const { Exterior::const_iterator it = mExt.find(std::make_pair(x,y)); if (it != mExt.end()) return &(it->second); return NULL; } const ESM::Pathgrid *Store::search(const std::string& name) const { Interior::const_iterator it = mInt.find(name); if (it != mInt.end()) return &(it->second); return NULL; } const ESM::Pathgrid *Store::find(int x, int y) const { const ESM::Pathgrid* pathgrid = search(x,y); if (!pathgrid) { std::ostringstream msg; msg << "Pathgrid in cell '" << x << " " << y << "' not found"; throw std::runtime_error(msg.str()); } return pathgrid; } const ESM::Pathgrid* Store::find(const std::string& name) const { const ESM::Pathgrid* pathgrid = search(name); if (!pathgrid) { std::ostringstream msg; msg << "Pathgrid in cell '" << name << "' not found"; throw std::runtime_error(msg.str()); } return pathgrid; } const ESM::Pathgrid *Store::search(const ESM::Cell &cell) const { if (!(cell.mData.mFlags & ESM::Cell::Interior)) return search(cell.mData.mX, cell.mData.mY); else return search(cell.mName); } const ESM::Pathgrid *Store::find(const ESM::Cell &cell) const { if (!(cell.mData.mFlags & ESM::Cell::Interior)) return find(cell.mData.mX, cell.mData.mY); else return find(cell.mName); } // Skill //========================================================================= Store::Store() { } // Magic effect //========================================================================= Store::Store() { } // Attribute //========================================================================= Store::Store() { mStatic.reserve(ESM::Attribute::Length); } const ESM::Attribute *Store::search(size_t index) const { if (index >= mStatic.size()) { return 0; } return &mStatic.at(index); } const ESM::Attribute *Store::find(size_t index) const { const ESM::Attribute *ptr = search(index); if (ptr == 0) { std::ostringstream msg; msg << "Attribute with index " << index << " not found"; throw std::runtime_error(msg.str()); } return ptr; } void Store::setUp() { for (int i = 0; i < ESM::Attribute::Length; ++i) { mStatic.push_back( ESM::Attribute( ESM::Attribute::sAttributeIds[i], ESM::Attribute::sGmstAttributeIds[i], ESM::Attribute::sGmstAttributeDescIds[i] ) ); } } size_t Store::getSize() const { return mStatic.size(); } Store::iterator Store::begin() const { return mStatic.begin(); } Store::iterator Store::end() const { return mStatic.end(); } // Dialogue //========================================================================= template<> void Store::setUp() { // DialInfos marked as deleted are kept during the loading phase, so that the linked list // structure is kept intact for inserting further INFOs. Delete them now that loading is done. for (Static::iterator it = mStatic.begin(); it != mStatic.end(); ++it) { ESM::Dialogue& dial = it->second; dial.clearDeletedInfos(); } mShared.clear(); mShared.reserve(mStatic.size()); std::map::iterator it = mStatic.begin(); for (; it != mStatic.end(); ++it) { mShared.push_back(&(it->second)); } } template <> inline RecordId Store::load(ESM::ESMReader &esm) { // The original letter case of a dialogue ID is saved, because it's printed ESM::Dialogue dialogue; bool isDeleted = false; dialogue.loadId(esm); std::string idLower = Misc::StringUtils::lowerCase(dialogue.mId); std::map::iterator found = mStatic.find(idLower); if (found == mStatic.end()) { dialogue.loadData(esm, isDeleted); mStatic.insert(std::make_pair(idLower, dialogue)); } else { found->second.loadData(esm, isDeleted); dialogue = found->second; } return RecordId(dialogue.mId, isDeleted); } } template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; openmw-openmw-0.38.0/apps/openmw/mwworld/store.hpp000066400000000000000000000247331264522266000222400ustar00rootroot00000000000000#ifndef OPENMW_MWWORLD_STORE_H #define OPENMW_MWWORLD_STORE_H #include #include #include #include "recordcmp.hpp" namespace ESM { struct Land; } namespace Loading { class Listener; } namespace MWWorld { struct RecordId { std::string mId; bool mIsDeleted; RecordId(const std::string &id = "", bool isDeleted = false); }; class StoreBase { public: virtual ~StoreBase() {} virtual void setUp() {} /// List identifiers of records contained in this Store (case-smashed). No-op for Stores that don't use string IDs. virtual void listIdentifier(std::vector &list) const {} virtual size_t getSize() const = 0; virtual int getDynamicSize() const { return 0; } virtual RecordId load(ESM::ESMReader &esm) = 0; virtual bool eraseStatic(const std::string &id) {return false;} virtual void clearDynamic() {} virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const {} virtual RecordId read (ESM::ESMReader& reader) { return RecordId(); } ///< Read into dynamic storage }; template class IndexedStore { protected: typedef typename std::map Static; Static mStatic; public: typedef typename std::map::const_iterator iterator; IndexedStore(); iterator begin() const; iterator end() const; void load(ESM::ESMReader &esm); int getSize() const; void setUp(); const T *search(int index) const; const T *find(int index) const; }; template class SharedIterator { typedef typename std::vector::const_iterator Iter; Iter mIter; public: SharedIterator() {} SharedIterator(const SharedIterator &orig) : mIter(orig.mIter) {} SharedIterator(const Iter &iter) : mIter(iter) {} SharedIterator &operator++() { ++mIter; return *this; } SharedIterator operator++(int) { SharedIterator iter = *this; ++mIter; return iter; } SharedIterator &operator--() { --mIter; return *this; } SharedIterator operator--(int) { SharedIterator iter = *this; --mIter; return iter; } bool operator==(const SharedIterator &x) const { return mIter == x.mIter; } bool operator!=(const SharedIterator &x) const { return !(*this == x); } const T &operator*() const { return **mIter; } const T *operator->() const { return &(**mIter); } }; class ESMStore; template class Store : public StoreBase { std::map mStatic; std::vector mShared; // Preserves the record order as it came from the content files (this // is relevant for the spell autocalc code and selection order // for heads/hairs in the character creation) std::map mDynamic; typedef std::map Dynamic; typedef std::map Static; friend class ESMStore; public: Store(); Store(const Store &orig); typedef SharedIterator iterator; // setUp needs to be called again after virtual void clearDynamic(); void setUp(); const T *search(const std::string &id) const; /** * Does the record with this ID come from the dynamic store? */ bool isDynamic(const std::string &id) const; /** Returns a random record that starts with the named ID, or NULL if not found. */ const T *searchRandom(const std::string &id) const; const T *find(const std::string &id) const; /** Returns a random record that starts with the named ID. An exception is thrown if none * are found. */ const T *findRandom(const std::string &id) const; iterator begin() const; iterator end() const; size_t getSize() const; int getDynamicSize() const; /// @note The record identifiers are listed in the order that the records were defined by the content files. void listIdentifier(std::vector &list) const; T *insert(const T &item); T *insertStatic(const T &item); bool eraseStatic(const std::string &id); bool erase(const std::string &id); bool erase(const T &item); RecordId load(ESM::ESMReader &esm); void write(ESM::ESMWriter& writer, Loading::Listener& progress) const; RecordId read(ESM::ESMReader& reader); }; template <> class Store : public StoreBase { // For multiple ESM/ESP files we need one list per file. typedef std::vector LandTextureList; std::vector mStatic; public: Store(); typedef std::vector::const_iterator iterator; // Must be threadsafe! Called from terrain background loading threads. // Not a big deal here, since ESM::LandTexture can never be modified or inserted/erased const ESM::LandTexture *search(size_t index, size_t plugin) const; const ESM::LandTexture *find(size_t index, size_t plugin) const; /// Resize the internal store to hold at least \a num plugins. void resize(size_t num); size_t getSize() const; size_t getSize(size_t plugin) const; RecordId load(ESM::ESMReader &esm, size_t plugin); RecordId load(ESM::ESMReader &esm); iterator begin(size_t plugin) const; iterator end(size_t plugin) const; }; template <> class Store : public StoreBase { std::vector mStatic; public: typedef SharedIterator iterator; virtual ~Store(); size_t getSize() const; iterator begin() const; iterator end() const; // Must be threadsafe! Called from terrain background loading threads. // Not a big deal here, since ESM::Land can never be modified or inserted/erased ESM::Land *search(int x, int y) const; ESM::Land *find(int x, int y) const; RecordId load(ESM::ESMReader &esm); void setUp(); }; template <> class Store : public StoreBase { struct DynamicExtCmp { bool operator()(const std::pair &left, const std::pair &right) const { if (left.first == right.first && left.second == right.second) return false; if (left.first == right.first) return left.second > right.second; // Exterior cells are listed in descending, row-major order, // this is a workaround for an ambiguous chargen_plank reference in the vanilla game. // there is one at -22,16 and one at -2,-9, the latter should be used. return left.first > right.first; } }; typedef std::map DynamicInt; typedef std::map, ESM::Cell, DynamicExtCmp> DynamicExt; DynamicInt mInt; DynamicExt mExt; std::vector mSharedInt; std::vector mSharedExt; DynamicInt mDynamicInt; DynamicExt mDynamicExt; const ESM::Cell *search(const ESM::Cell &cell) const; void handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell); public: typedef SharedIterator iterator; const ESM::Cell *search(const std::string &id) const; const ESM::Cell *search(int x, int y) const; const ESM::Cell *searchOrCreate(int x, int y); const ESM::Cell *find(const std::string &id) const; const ESM::Cell *find(int x, int y) const; void setUp(); RecordId load(ESM::ESMReader &esm); iterator intBegin() const; iterator intEnd() const; iterator extBegin() const; iterator extEnd() const; // Return the northernmost cell in the easternmost column. const ESM::Cell *searchExtByName(const std::string &id) const; // Return the northernmost cell in the easternmost column. const ESM::Cell *searchExtByRegion(const std::string &id) const; size_t getSize() const; void listIdentifier(std::vector &list) const; ESM::Cell *insert(const ESM::Cell &cell); bool erase(const ESM::Cell &cell); bool erase(const std::string &id); bool erase(int x, int y); }; template <> class Store : public StoreBase { private: typedef std::map Interior; typedef std::map, ESM::Pathgrid> Exterior; Interior mInt; Exterior mExt; Store* mCells; public: Store(); void setCells(Store& cells); RecordId load(ESM::ESMReader &esm); size_t getSize() const; void setUp(); const ESM::Pathgrid *search(int x, int y) const; const ESM::Pathgrid *search(const std::string& name) const; const ESM::Pathgrid *find(int x, int y) const; const ESM::Pathgrid* find(const std::string& name) const; const ESM::Pathgrid *search(const ESM::Cell &cell) const; const ESM::Pathgrid *find(const ESM::Cell &cell) const; }; template <> class Store : public IndexedStore { public: Store(); }; template <> class Store : public IndexedStore { public: Store(); }; template <> class Store : public IndexedStore { std::vector mStatic; public: typedef std::vector::const_iterator iterator; Store(); const ESM::Attribute *search(size_t index) const; const ESM::Attribute *find(size_t index) const; void setUp(); size_t getSize() const; iterator begin() const; iterator end() const; }; } //end namespace #endif openmw-openmw-0.38.0/apps/openmw/mwworld/timestamp.cpp000066400000000000000000000051331264522266000230730ustar00rootroot00000000000000#include "timestamp.hpp" #include #include #include namespace MWWorld { TimeStamp::TimeStamp (float hour, int day) : mHour (hour), mDay (day) { if (hour<0 || hour>=24 || day<0) throw std::runtime_error ("invalid time stamp"); } float TimeStamp::getHour() const { return mHour; } int TimeStamp::getDay() const { return mDay; } TimeStamp& TimeStamp::operator+= (double hours) { if (hours<0) throw std::runtime_error ("can't move time stamp backwards in time"); hours += mHour; mHour = static_cast (std::fmod (hours, 24)); mDay += static_cast(hours / 24); return *this; } bool operator== (const TimeStamp& left, const TimeStamp& right) { return left.getHour()==right.getHour() && left.getDay()==right.getDay(); } bool operator!= (const TimeStamp& left, const TimeStamp& right) { return !(left==right); } bool operator< (const TimeStamp& left, const TimeStamp& right) { if (left.getDay()right.getDay()) return false; return left.getHour() (const TimeStamp& left, const TimeStamp& right) { return !(left<=right); } bool operator>= (const TimeStamp& left, const TimeStamp& right) { return !(left=0 explicit TimeStamp (const ESM::TimeStamp& esm); ESM::TimeStamp toEsm () const; float getHour() const; int getDay() const; TimeStamp& operator+= (double hours); ///< \param hours >=0 }; bool operator== (const TimeStamp& left, const TimeStamp& right); bool operator!= (const TimeStamp& left, const TimeStamp& right); bool operator< (const TimeStamp& left, const TimeStamp& right); bool operator<= (const TimeStamp& left, const TimeStamp& right); bool operator> (const TimeStamp& left, const TimeStamp& right); bool operator>= (const TimeStamp& left, const TimeStamp& right); TimeStamp operator+ (const TimeStamp& stamp, double hours); TimeStamp operator+ (double hours, const TimeStamp& stamp); double operator- (const TimeStamp& left, const TimeStamp& right); ///< Returns the difference between \a left and \a right in in-game hours. } #endif openmw-openmw-0.38.0/apps/openmw/mwworld/weather.cpp000066400000000000000000001324711264522266000225350ustar00rootroot00000000000000#include "weather.hpp" #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwsound/sound.hpp" #include "../mwrender/renderingmanager.hpp" #include "../mwrender/sky.hpp" #include "player.hpp" #include "esmstore.hpp" #include "fallback.hpp" #include "cellstore.hpp" #include using namespace MWWorld; namespace { static const int invalidWeatherID = -1; // linear interpolate between x and y based on factor. float lerp (float x, float y, float factor) { return x * (1-factor) + y * factor; } // linear interpolate between x and y based on factor. osg::Vec4f lerp (const osg::Vec4f& x, const osg::Vec4f& y, float factor) { return x * (1-factor) + y * factor; } } template T TimeOfDayInterpolator::getValue(const float gameHour, const TimeOfDaySettings& timeSettings) const { // TODO: use pre/post sunset/sunrise time values in [Weather] section // night if (gameHour <= timeSettings.mNightEnd || gameHour >= timeSettings.mNightStart + 1) return mNightValue; // sunrise else if (gameHour >= timeSettings.mNightEnd && gameHour <= timeSettings.mDayStart + 1) { if (gameHour <= timeSettings.mSunriseTime) { // fade in float advance = timeSettings.mSunriseTime - gameHour; float factor = advance / 0.5f; return lerp(mSunriseValue, mNightValue, factor); } else { // fade out float advance = gameHour - timeSettings.mSunriseTime; float factor = advance / 3.f; return lerp(mSunriseValue, mDayValue, factor); } } // day else if (gameHour >= timeSettings.mDayStart + 1 && gameHour <= timeSettings.mDayEnd - 1) return mDayValue; // sunset else if (gameHour >= timeSettings.mDayEnd - 1 && gameHour <= timeSettings.mNightStart + 1) { if (gameHour <= timeSettings.mDayEnd + 1) { // fade in float advance = (timeSettings.mDayEnd + 1) - gameHour; float factor = (advance / 2); return lerp(mSunsetValue, mDayValue, factor); } else { // fade out float advance = gameHour - (timeSettings.mDayEnd + 1); float factor = advance / 2.f; return lerp(mSunsetValue, mNightValue, factor); } } // shut up compiler return T(); } template class TimeOfDayInterpolator; template class TimeOfDayInterpolator; Weather::Weather(const std::string& name, const MWWorld::Fallback& fallback, float stormWindSpeed, float rainSpeed, const std::string& particleEffect) : mCloudTexture(fallback.getFallbackString("Weather_" + name + "_Cloud_Texture")) , mSkyColor(fallback.getFallbackColour("Weather_" + name +"_Sky_Sunrise_Color"), fallback.getFallbackColour("Weather_" + name + "_Sky_Day_Color"), fallback.getFallbackColour("Weather_" + name + "_Sky_Sunset_Color"), fallback.getFallbackColour("Weather_" + name + "_Sky_Night_Color")) , mFogColor(fallback.getFallbackColour("Weather_" + name + "_Fog_Sunrise_Color"), fallback.getFallbackColour("Weather_" + name + "_Fog_Day_Color"), fallback.getFallbackColour("Weather_" + name + "_Fog_Sunset_Color"), fallback.getFallbackColour("Weather_" + name + "_Fog_Night_Color")) , mAmbientColor(fallback.getFallbackColour("Weather_" + name + "_Ambient_Sunrise_Color"), fallback.getFallbackColour("Weather_" + name + "_Ambient_Day_Color"), fallback.getFallbackColour("Weather_" + name + "_Ambient_Sunset_Color"), fallback.getFallbackColour("Weather_" + name + "_Ambient_Night_Color")) , mSunColor(fallback.getFallbackColour("Weather_" + name + "_Sun_Sunrise_Color"), fallback.getFallbackColour("Weather_" + name + "_Sun_Day_Color"), fallback.getFallbackColour("Weather_" + name + "_Sun_Sunset_Color"), fallback.getFallbackColour("Weather_" + name + "_Sun_Night_Color")) , mLandFogDepth(fallback.getFallbackFloat("Weather_" + name + "_Land_Fog_Day_Depth"), fallback.getFallbackFloat("Weather_" + name + "_Land_Fog_Day_Depth"), fallback.getFallbackFloat("Weather_" + name + "_Land_Fog_Day_Depth"), fallback.getFallbackFloat("Weather_" + name + "_Land_Fog_Night_Depth")) , mSunDiscSunsetColor(fallback.getFallbackColour("Weather_" + name + "_Sun_Disc_Sunset_Color")) , mWindSpeed(fallback.getFallbackFloat("Weather_" + name + "_Wind_Speed")) , mCloudSpeed(fallback.getFallbackFloat("Weather_" + name + "_Cloud_Speed")) , mGlareView(fallback.getFallbackFloat("Weather_" + name + "_Glare_View")) , mIsStorm(mWindSpeed > stormWindSpeed) , mRainSpeed(rainSpeed) , mRainFrequency(fallback.getFallbackFloat("Weather_" + name + "_Rain_Entrance_Speed")) , mParticleEffect(particleEffect) , mRainEffect(fallback.getFallbackBool("Weather_" + name + "_Using_Precip") ? "meshes\\raindrop.nif" : "") , mTransitionDelta(fallback.getFallbackFloat("Weather_" + name + "_Transition_Delta")) , mCloudsMaximumPercent(fallback.getFallbackFloat("Weather_" + name + "_Clouds_Maximum_Percent")) , mThunderFrequency(fallback.getFallbackFloat("Weather_" + name + "_Thunder_Frequency")) , mThunderThreshold(fallback.getFallbackFloat("Weather_" + name + "_Thunder_Threshold")) , mThunderSoundID() , mFlashDecrement(fallback.getFallbackFloat("Weather_" + name + "_Flash_Decrement")) , mFlashBrightness(0.0f) { mThunderSoundID[0] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_0"); mThunderSoundID[1] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_1"); mThunderSoundID[2] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_2"); mThunderSoundID[3] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_3"); // TODO: support weathers that have both "Ambient Loop Sound ID" and "Rain Loop Sound ID", need to play both sounds at the same time. if (!mRainEffect.empty()) // NOTE: in vanilla, the weathers with rain seem to be hardcoded; changing Using_Precip has no effect { mAmbientLoopSoundID = fallback.getFallbackString("Weather_" + name + "_Rain_Loop_Sound_ID"); if (mAmbientLoopSoundID.empty()) // default to "rain" if not set mAmbientLoopSoundID = "rain"; } else mAmbientLoopSoundID = fallback.getFallbackString("Weather_" + name + "_Ambient_Loop_Sound_ID"); if (Misc::StringUtils::ciEqual(mAmbientLoopSoundID, "None")) mAmbientLoopSoundID.clear(); /* Unhandled: Rain Diameter=600 ? Rain Height Min=200 ? Rain Height Max=700 ? Rain Threshold=0.6 ? Max Raindrops=650 ? */ } float Weather::transitionDelta() const { // Transition Delta describes how quickly transitioning to the weather in question will take, in Hz. Note that the // measurement is in real time, not in-game time. return mTransitionDelta; } float Weather::cloudBlendFactor(const float transitionRatio) const { // Clouds Maximum Percent affects how quickly the sky transitions from one sky texture to the next. return transitionRatio / mCloudsMaximumPercent; } float Weather::calculateThunder(const float transitionRatio, const float elapsedSeconds, const bool isPaused) { // When paused, the flash brightness remains the same and no new strikes can occur. if(!isPaused) { // Morrowind doesn't appear to do any calculations unless the transition ratio is higher than the Thunder Threshold. if(transitionRatio >= mThunderThreshold && mThunderFrequency > 0.0f) { flashDecrement(elapsedSeconds); if(Misc::Rng::rollProbability() <= thunderChance(transitionRatio, elapsedSeconds)) { lightningAndThunder(); } } else { mFlashBrightness = 0.0f; } } return mFlashBrightness; } inline void Weather::flashDecrement(const float elapsedSeconds) { // The Flash Decrement is measured in whole units per second. This means that if the flash brightness was // currently 1.0, then it should take approximately 0.25 seconds to decay to 0.0 (the minimum). float decrement = mFlashDecrement * elapsedSeconds; mFlashBrightness = decrement > mFlashBrightness ? 0.0f : mFlashBrightness - decrement; } inline float Weather::thunderChance(const float transitionRatio, const float elapsedSeconds) const { // This formula is reversed from the observation that with Thunder Frequency set to 1, there are roughly 10 strikes // per minute. It doesn't appear to be tied to in game time as Timescale doesn't affect it. Various values of // Thunder Frequency seem to change the average number of strikes in a linear fashion.. During a transition, it appears to // scaled based on how far past it is past the Thunder Threshold. float scaleFactor = (transitionRatio - mThunderThreshold) / (1.0f - mThunderThreshold); return ((mThunderFrequency * 10.0f) / 60.0f) * elapsedSeconds * scaleFactor; } inline void Weather::lightningAndThunder(void) { // Morrowind seems to vary the intensity of the brightness based on which of the four sound IDs it selects. // They appear to go from 0 (brightest, closest) to 3 (faintest, farthest). The value of 0.25 per distance // was derived by setting the Flash Decrement to 0.1 and measuring how long each value took to decay to 0. // TODO: Determine the distribution of each distance to see if it's evenly weighted. unsigned int distance = Misc::Rng::rollDice(4); // Flash brightness appears additive, since if multiple strikes occur, it takes longer for it to decay to 0. mFlashBrightness += 1 - (distance * 0.25f); MWBase::Environment::get().getSoundManager()->playSound(mThunderSoundID[distance], 1.0, 1.0); } RegionWeather::RegionWeather(const ESM::Region& region) : mWeather(invalidWeatherID) , mChances() { mChances.reserve(10); mChances.push_back(region.mData.mClear); mChances.push_back(region.mData.mCloudy); mChances.push_back(region.mData.mFoggy); mChances.push_back(region.mData.mOvercast); mChances.push_back(region.mData.mRain); mChances.push_back(region.mData.mThunder); mChances.push_back(region.mData.mAsh); mChances.push_back(region.mData.mBlight); mChances.push_back(region.mData.mA); mChances.push_back(region.mData.mB); } RegionWeather::RegionWeather(const ESM::RegionWeatherState& state) : mWeather(state.mWeather) , mChances(state.mChances) { } RegionWeather::operator ESM::RegionWeatherState() const { ESM::RegionWeatherState state = { mWeather, mChances }; return state; } void RegionWeather::setChances(const std::vector& chances) { if(mChances.size() < chances.size()) { mChances.reserve(chances.size()); } std::vector::const_iterator it = chances.begin(); for(size_t i = 0; it != chances.end(); ++it, ++i) { mChances[i] = *it; } // Regional weather no longer supports the current type, select a new weather pattern. if((static_cast(mWeather) >= mChances.size()) || (mChances[mWeather] == 0)) { chooseNewWeather(); } } void RegionWeather::setWeather(int weatherID) { mWeather = weatherID; } int RegionWeather::getWeather() { // If the region weather was already set (by ChangeWeather, or by a previous call) then just return that value. // Note that the region weather will be expired periodically when the weather update timer expires. if(mWeather == invalidWeatherID) { chooseNewWeather(); } return mWeather; } void RegionWeather::chooseNewWeather() { // All probabilities must add to 100 (responsibility of the user). // If chances A and B has values 30 and 70 then by generating 100 numbers 1..100, 30% will be lesser or equal 30 // and 70% will be greater than 30 (in theory). int chance = Misc::Rng::rollDice(100) + 1; // 1..100 int sum = 0; int i = 0; for(; static_cast(i) < mChances.size(); ++i) { sum += mChances[i]; if(chance <= sum) break; } mWeather = i; } MoonModel::MoonModel(const std::string& name, const MWWorld::Fallback& fallback) : mFadeInStart(fallback.getFallbackFloat("Moons_" + name + "_Fade_In_Start")) , mFadeInFinish(fallback.getFallbackFloat("Moons_" + name + "_Fade_In_Finish")) , mFadeOutStart(fallback.getFallbackFloat("Moons_" + name + "_Fade_Out_Start")) , mFadeOutFinish(fallback.getFallbackFloat("Moons_" + name + "_Fade_Out_Finish")) , mAxisOffset(fallback.getFallbackFloat("Moons_" + name + "_Axis_Offset")) , mSpeed(fallback.getFallbackFloat("Moons_" + name + "_Speed")) , mDailyIncrement(fallback.getFallbackFloat("Moons_" + name + "_Daily_Increment")) , mFadeStartAngle(fallback.getFallbackFloat("Moons_" + name + "_Fade_Start_Angle")) , mFadeEndAngle(fallback.getFallbackFloat("Moons_" + name + "_Fade_End_Angle")) , mMoonShadowEarlyFadeAngle(fallback.getFallbackFloat("Moons_" + name + "_Moon_Shadow_Early_Fade_Angle")) { // Morrowind appears to have a minimum speed in order to avoid situations where the moon couldn't conceivably // complete a rotation in a single 24 hour period. The value of 180/23 was deduced from reverse engineering. mSpeed = std::min(mSpeed, 180.0f / 23.0f); } MWRender::MoonState MoonModel::calculateState(const TimeStamp& gameTime) const { float rotationFromHorizon = angle(gameTime); MWRender::MoonState state = { rotationFromHorizon, mAxisOffset, // Reverse engineered from Morrowind's scene graph rotation matrices. static_cast(phase(gameTime)), shadowBlend(rotationFromHorizon), earlyMoonShadowAlpha(rotationFromHorizon) * hourlyAlpha(gameTime.getHour()) }; return state; } inline float MoonModel::angle(const TimeStamp& gameTime) const { // Morrowind's moons start travel on one side of the horizon (let's call it H-rise) and travel 180 degrees to the // opposite horizon (let's call it H-set). Upon reaching H-set, they reset to H-rise until the next moon rise. // When calculating the angle of the moon, several cases have to be taken into account: // 1. Moon rises and then sets in one day. // 2. Moon sets and doesn't rise in one day (occurs when the moon rise hour is >= 24). // 3. Moon sets and then rises in one day. float moonRiseHourToday = moonRiseHour(gameTime.getDay()); float moonRiseAngleToday = 0; if(gameTime.getHour() < moonRiseHourToday) { float moonRiseHourYesterday = moonRiseHour(gameTime.getDay() - 1); if(moonRiseHourYesterday < 24) { float moonRiseAngleYesterday = rotation(24 - moonRiseHourYesterday); if(moonRiseAngleYesterday < 180) { // The moon rose but did not set yesterday, so accumulate yesterday's angle with how much we've travelled today. moonRiseAngleToday = rotation(gameTime.getHour()) + moonRiseAngleYesterday; } } } else { moonRiseAngleToday = rotation(gameTime.getHour() - moonRiseHourToday); } if(moonRiseAngleToday >= 180) { // The moon set today, reset the angle to the horizon. moonRiseAngleToday = 0; } return moonRiseAngleToday; } inline float MoonModel::moonRiseHour(unsigned int daysPassed) const { // This arises from the start date of 16 Last Seed, 427 // TODO: Find an alternate formula that doesn't rely on this day being fixed. static const unsigned int startDay = 16; // This odd formula arises from the fact that on 16 Last Seed, 17 increments have occurred, meaning // that upon starting a new game, it must only calculate the moon phase as far back as 1 Last Seed. // Note that we don't modulo after adding the latest daily increment because other calculations need to // know if doing so would cause the moon rise to be postponed until the next day (which happens when // the moon rise hour is >= 24 in Morrowind). return mDailyIncrement + std::fmod((daysPassed - 1 + startDay) * mDailyIncrement, 24.0f); } inline float MoonModel::rotation(float hours) const { // 15 degrees per hour was reverse engineered from the rotation matrices of the Morrowind scene graph. // Note that this correlates to 360 / 24, which is a full rotation every 24 hours, so speed is a measure // of whole rotations that could be completed in a day. return 15.0f * mSpeed * hours; } inline unsigned int MoonModel::phase(const TimeStamp& gameTime) const { // Morrowind starts with a full moon on 16 Last Seed and then begins to wane 17 Last Seed, working on 3 day phase cycle. // Note: this is an internal helper, and as such we don't want to return MWRender::MoonState::Phase since we can't // forward declare it (C++11 strongly typed enums solve this). // If the moon didn't rise yet today, use yesterday's moon phase. if(gameTime.getHour() < moonRiseHour(gameTime.getDay())) return (gameTime.getDay() / 3) % 8; else return ((gameTime.getDay() + 1) / 3) % 8; } inline float MoonModel::shadowBlend(float angle) const { // The Fade End Angle and Fade Start Angle describe a region where the moon transitions from a solid disk // that is roughly the color of the sky, to a textured surface. // Depending on the current angle, the following values describe the ratio between the textured moon // and the solid disk: // 1. From Fade End Angle 1 to Fade Start Angle 1 (during moon rise): 0..1 // 2. From Fade Start Angle 1 to Fade Start Angle 2 (between moon rise and moon set): 1 (textured) // 3. From Fade Start Angle 2 to Fade End Angle 2 (during moon set): 1..0 // 4. From Fade End Angle 2 to Fade End Angle 1 (between moon set and moon rise): 0 (solid disk) float fadeAngle = mFadeStartAngle - mFadeEndAngle; float fadeEndAngle2 = 180.0f - mFadeEndAngle; float fadeStartAngle2 = 180.0f - mFadeStartAngle; if((angle >= mFadeEndAngle) && (angle < mFadeStartAngle)) return (angle - mFadeEndAngle) / fadeAngle; else if((angle >= mFadeStartAngle) && (angle < fadeStartAngle2)) return 1.0f; else if((angle >= fadeStartAngle2) && (angle < fadeEndAngle2)) return (fadeEndAngle2 - angle) / fadeAngle; else return 0.0f; } inline float MoonModel::hourlyAlpha(float gameHour) const { // The Fade Out Start / Finish and Fade In Start / Finish describe the hours at which the moon // appears and disappears. // Depending on the current hour, the following values describe how transparent the moon is. // 1. From Fade Out Start to Fade Out Finish: 1..0 // 2. From Fade Out Finish to Fade In Start: 0 (transparent) // 3. From Fade In Start to Fade In Finish: 0..1 // 4. From Fade In Finish to Fade Out Start: 1 (solid) if((gameHour >= mFadeOutStart) && (gameHour < mFadeOutFinish)) return (mFadeOutFinish - gameHour) / (mFadeOutFinish - mFadeOutStart); else if((gameHour >= mFadeOutFinish) && (gameHour < mFadeInStart)) return 0.0f; else if((gameHour >= mFadeInStart) && (gameHour < mFadeInFinish)) return (gameHour - mFadeInStart) / (mFadeInFinish - mFadeInStart); else return 1.0f; } inline float MoonModel::earlyMoonShadowAlpha(float angle) const { // The Moon Shadow Early Fade Angle describes an arc relative to Fade End Angle. // Depending on the current angle, the following values describe how transparent the moon is. // 1. From Moon Shadow Early Fade Angle 1 to Fade End Angle 1 (during moon rise): 0..1 // 2. From Fade End Angle 1 to Fade End Angle 2 (between moon rise and moon set): 1 (solid) // 3. From Fade End Angle 2 to Moon Shadow Early Fade Angle 2 (during moon set): 1..0 // 4. From Moon Shadow Early Fade Angle 2 to Moon Shadow Early Fade Angle 1: 0 (transparent) float moonShadowEarlyFadeAngle1 = mFadeEndAngle - mMoonShadowEarlyFadeAngle; float fadeEndAngle2 = 180.0f - mFadeEndAngle; float moonShadowEarlyFadeAngle2 = fadeEndAngle2 + mMoonShadowEarlyFadeAngle; if((angle >= moonShadowEarlyFadeAngle1) && (angle < mFadeEndAngle)) return (angle - moonShadowEarlyFadeAngle1) / mMoonShadowEarlyFadeAngle; else if((angle >= mFadeEndAngle) && (angle < fadeEndAngle2)) return 1.0f; else if((angle >= fadeEndAngle2) && (angle < moonShadowEarlyFadeAngle2)) return (moonShadowEarlyFadeAngle2 - angle) / mMoonShadowEarlyFadeAngle; else return 0.0f; } WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const MWWorld::Fallback& fallback, MWWorld::ESMStore& store) : mStore(store) , mRendering(rendering) , mSunriseTime(fallback.getFallbackFloat("Weather_Sunrise_Time")) , mSunsetTime(fallback.getFallbackFloat("Weather_Sunset_Time")) , mSunriseDuration(fallback.getFallbackFloat("Weather_Sunrise_Duration")) , mSunsetDuration(fallback.getFallbackFloat("Weather_Sunset_Duration")) , mSunPreSunsetTime(fallback.getFallbackFloat("Weather_Sun_Pre-Sunset_Time")) , mNightFade(0, 0, 0, 1) , mHoursBetweenWeatherChanges(fallback.getFallbackFloat("Weather_Hours_Between_Weather_Changes")) , mRainSpeed(fallback.getFallbackFloat("Weather_Precip_Gravity")) , mUnderwaterFog(fallback.getFallbackFloat("Water_UnderwaterSunriseFog"), fallback.getFallbackFloat("Water_UnderwaterDayFog"), fallback.getFallbackFloat("Water_UnderwaterSunsetFog"), fallback.getFallbackFloat("Water_UnderwaterNightFog")) , mWeatherSettings() , mMasser("Masser", fallback) , mSecunda("Secunda", fallback) , mWindSpeed(0.f) , mIsStorm(false) , mStormDirection(0,1,0) , mCurrentRegion() , mTimePassed(0) , mFastForward(false) , mWeatherUpdateTime(mHoursBetweenWeatherChanges) , mTransitionFactor(0) , mCurrentWeather(0) , mNextWeather(0) , mQueuedWeather(0) , mRegions() , mResult() , mAmbientSound() , mPlayingSoundID() { mTimeSettings.mNightStart = mSunsetTime + mSunsetDuration; mTimeSettings.mNightEnd = mSunriseTime - 0.5f; mTimeSettings.mDayStart = mSunriseTime + mSunriseDuration; mTimeSettings.mDayEnd = mSunsetTime; mTimeSettings.mSunriseTime = mSunriseTime; mWeatherSettings.reserve(10); addWeather("Clear", fallback); // 0 addWeather("Cloudy", fallback); // 1 addWeather("Foggy", fallback); // 2 addWeather("Overcast", fallback); // 3 addWeather("Rain", fallback); // 4 addWeather("Thunderstorm", fallback); // 5 addWeather("Ashstorm", fallback, "meshes\\ashcloud.nif"); // 6 addWeather("Blight", fallback, "meshes\\blightcloud.nif"); // 7 addWeather("Snow", fallback, "meshes\\snow.nif"); // 8 addWeather("Blizzard", fallback, "meshes\\blizzard.nif"); // 9 Store::iterator it = store.get().begin(); for(; it != store.get().end(); ++it) { std::string regionID = Misc::StringUtils::lowerCase(it->mId); mRegions.insert(std::make_pair(regionID, RegionWeather(*it))); } forceWeather(0); } WeatherManager::~WeatherManager() { stopSounds(); } void WeatherManager::changeWeather(const std::string& regionID, const unsigned int weatherID) { // In Morrowind, this seems to have the following behavior, when applied to the current region: // - When there is no transition in progress, start transitioning to the new weather. // - If there is a transition in progress, queue up the transition and process it when the current one completes. // - If there is a transition in progress, and a queued transition, overwrite the queued transition. // - If multiple calls to ChangeWeather are made while paused (console up), only the last call will be used, // meaning that if there was no transition in progress, only the last ChangeWeather will be processed. // If the region isn't current, Morrowind will store the new weather for the region in question. if(weatherID < mWeatherSettings.size()) { std::string lowerCaseRegionID = Misc::StringUtils::lowerCase(regionID); std::map::iterator it = mRegions.find(lowerCaseRegionID); if(it != mRegions.end()) { it->second.setWeather(weatherID); regionalWeatherChanged(it->first, it->second); } } } void WeatherManager::modRegion(const std::string& regionID, const std::vector& chances) { // Sets the region's probability for various weather patterns. Note that this appears to be saved permanently. // In Morrowind, this seems to have the following behavior when applied to the current region: // - If the region supports the current weather, no change in current weather occurs. // - If the region no longer supports the current weather, and there is no transition in progress, begin to // transition to a new supported weather type. // - If the region no longer supports the current weather, and there is a transition in progress, queue a // transition to a new supported weather type. std::string lowerCaseRegionID = Misc::StringUtils::lowerCase(regionID); std::map::iterator it = mRegions.find(lowerCaseRegionID); if(it != mRegions.end()) { it->second.setChances(chances); regionalWeatherChanged(it->first, it->second); } } void WeatherManager::playerTeleported() { // If the player teleports to an outdoors cell in a new region (for instance, by travelling), the weather needs to // be changed immediately, and any transitions for the previous region discarded. MWBase::World* world = MWBase::Environment::get().getWorld(); if(world->isCellExterior() || world->isCellQuasiExterior()) { std::string playerRegion = Misc::StringUtils::lowerCase(world->getPlayerPtr().getCell()->getCell()->mRegion); std::map::iterator it = mRegions.find(playerRegion); if(it != mRegions.end() && playerRegion != mCurrentRegion) { mCurrentRegion = playerRegion; forceWeather(it->second.getWeather()); } } } void WeatherManager::update(float duration, bool paused) { MWWorld::ConstPtr player = MWMechanics::getPlayer(); MWBase::World& world = *MWBase::Environment::get().getWorld(); TimeStamp time = world.getTimeStamp(); if(!paused || mFastForward) { // Add new transitions when either the player's current external region changes. std::string playerRegion = Misc::StringUtils::lowerCase(player.getCell()->getCell()->mRegion); if(updateWeatherTime() || updateWeatherRegion(playerRegion)) { std::map::iterator it = mRegions.find(mCurrentRegion); if(it != mRegions.end()) { addWeatherTransition(it->second.getWeather()); } } updateWeatherTransitions(duration); } const bool exterior = (world.isCellExterior() || world.isCellQuasiExterior()); if(!exterior) { mRendering.setSkyEnabled(false); stopSounds(); return; } calculateWeatherResult(time.getHour(), duration, paused); mWindSpeed = mResult.mWindSpeed; mIsStorm = mResult.mIsStorm; if (mIsStorm) { osg::Vec3f playerPos (player.getRefData().getPosition().asVec3()); osg::Vec3f redMountainPos (19950, 72032, 27831); mStormDirection = (playerPos - redMountainPos); mStormDirection.z() = 0; mStormDirection.normalize(); mRendering.getSkyManager()->setStormDirection(mStormDirection); } // disable sun during night if (time.getHour() >= mTimeSettings.mNightStart || time.getHour() <= mSunriseTime) mRendering.getSkyManager()->sunDisable(); else mRendering.getSkyManager()->sunEnable(); // Update the sun direction. Run it east to west at a fixed angle from overhead. // The sun's speed at day and night may differ, since mSunriseTime and mNightStart // mark when the sun is level with the horizon. { // Shift times into a 24-hour window beginning at mSunriseTime... float adjustedHour = time.getHour(); float adjustedNightStart = mTimeSettings.mNightStart; if ( time.getHour() < mSunriseTime ) adjustedHour += 24.f; if ( mTimeSettings.mNightStart < mSunriseTime ) adjustedNightStart += 24.f; const bool is_night = adjustedHour >= adjustedNightStart; const float dayDuration = adjustedNightStart - mSunriseTime; const float nightDuration = 24.f - dayDuration; double theta; if ( !is_night ) { theta = M_PI * (adjustedHour - mSunriseTime) / dayDuration; } else { theta = M_PI * (1.f - (adjustedHour - adjustedNightStart) / nightDuration); } osg::Vec3f final( static_cast(cos(theta)), -0.268f, // approx tan( -15 degrees ) static_cast(sin(theta))); mRendering.setSunDirection( final * -1 ); } float underwaterFog = mUnderwaterFog.getValue(time.getHour(), mTimeSettings); float peakHour = mSunriseTime + (mSunsetTime - mSunriseTime) / 2; if (time.getHour() < mSunriseTime || time.getHour() > mSunsetTime) mRendering.getSkyManager()->setGlareTimeOfDayFade(0); else if (time.getHour() < peakHour) mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (peakHour - time.getHour()) / (peakHour - mSunriseTime)); else mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (time.getHour() - peakHour) / (mSunsetTime - peakHour)); mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time)); mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time)); mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mFogColor); mRendering.setAmbientColour(mResult.mAmbientColor); mRendering.setSunColour(mResult.mSunColor); mRendering.getSkyManager()->setWeather(mResult); // Play sounds if (mPlayingSoundID != mResult.mAmbientLoopSoundID) { stopSounds(); if (!mResult.mAmbientLoopSoundID.empty()) mAmbientSound = MWBase::Environment::get().getSoundManager()->playSound(mResult.mAmbientLoopSoundID, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); mPlayingSoundID = mResult.mAmbientLoopSoundID; } if (mAmbientSound.get()) mAmbientSound->setVolume(mResult.mAmbientSoundVolume); } void WeatherManager::stopSounds() { if (mAmbientSound.get()) MWBase::Environment::get().getSoundManager()->stopSound(mAmbientSound); mAmbientSound.reset(); mPlayingSoundID.clear(); } float WeatherManager::getWindSpeed() const { return mWindSpeed; } bool WeatherManager::isInStorm() const { return mIsStorm; } osg::Vec3f WeatherManager::getStormDirection() const { return mStormDirection; } void WeatherManager::advanceTime(double hours, bool incremental) { // In Morrowind, when the player sleeps/waits, serves jail time, travels, or trains, all weather transitions are // immediately applied, regardless of whatever transition time might have been remaining. mTimePassed += hours; mFastForward = !incremental ? true : mFastForward; } unsigned int WeatherManager::getWeatherID() const { return mCurrentWeather; } bool WeatherManager::isDark() const { TimeStamp time = MWBase::Environment::get().getWorld()->getTimeStamp(); bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); return exterior && (time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart - 1); } void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress) { ESM::WeatherState state; state.mCurrentRegion = mCurrentRegion; state.mTimePassed = mTimePassed; state.mFastForward = mFastForward; state.mWeatherUpdateTime = mWeatherUpdateTime; state.mTransitionFactor = mTransitionFactor; state.mCurrentWeather = mCurrentWeather; state.mNextWeather = mNextWeather; state.mQueuedWeather = mQueuedWeather; std::map::iterator it = mRegions.begin(); for(; it != mRegions.end(); ++it) { state.mRegions.insert(std::make_pair(it->first, it->second)); } writer.startRecord(ESM::REC_WTHR); state.save(writer); writer.endRecord(ESM::REC_WTHR); } bool WeatherManager::readRecord(ESM::ESMReader& reader, uint32_t type) { if(ESM::REC_WTHR == type) { static const int oldestCompatibleSaveFormat = 2; if(reader.getFormat() < oldestCompatibleSaveFormat) { // Weather state isn't really all that important, so to preserve older save games, we'll just discard the // older weather records, rather than fail to handle the record. reader.skipRecord(); } else { ESM::WeatherState state; state.load(reader); mCurrentRegion.swap(state.mCurrentRegion); mTimePassed = state.mTimePassed; mFastForward = state.mFastForward; mWeatherUpdateTime = state.mWeatherUpdateTime; mTransitionFactor = state.mTransitionFactor; mCurrentWeather = state.mCurrentWeather; mNextWeather = state.mNextWeather; mQueuedWeather = state.mQueuedWeather; mRegions.clear(); std::map::iterator it = state.mRegions.begin(); if(it == state.mRegions.end()) { // When loading an imported save, the region modifiers aren't currently being set, so just reset them. importRegions(); } else { for(; it != state.mRegions.end(); ++it) { mRegions.insert(std::make_pair(it->first, RegionWeather(it->second))); } } } return true; } return false; } void WeatherManager::clear() { stopSounds(); mCurrentRegion = ""; mTimePassed = 0.0f; mWeatherUpdateTime = 0.0f; forceWeather(0); mRegions.clear(); importRegions(); } inline void WeatherManager::addWeather(const std::string& name, const MWWorld::Fallback& fallback, const std::string& particleEffect) { static const float fStromWindSpeed = mStore.get().find("fStromWindSpeed")->getFloat(); Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, particleEffect); mWeatherSettings.push_back(weather); } inline void WeatherManager::importRegions() { Store::iterator it = mStore.get().begin(); for(; it != mStore.get().end(); ++it) { std::string regionID = Misc::StringUtils::lowerCase(it->mId); mRegions.insert(std::make_pair(regionID, RegionWeather(*it))); } } inline void WeatherManager::regionalWeatherChanged(const std::string& regionID, RegionWeather& region) { // If the region is current, then add a weather transition for it. MWWorld::ConstPtr player = MWMechanics::getPlayer(); if(player.isInCell()) { std::string playerRegion = Misc::StringUtils::lowerCase(player.getCell()->getCell()->mRegion); if(!playerRegion.empty() && (playerRegion == regionID)) { addWeatherTransition(region.getWeather()); } } } inline bool WeatherManager::updateWeatherTime() { mWeatherUpdateTime -= mTimePassed; mTimePassed = 0.0f; if(mWeatherUpdateTime <= 0.0f) { // Expire all regional weather, so that any call to getWeather() will return a new weather ID. std::map::iterator it = mRegions.begin(); for(; it != mRegions.end(); ++it) { it->second.setWeather(invalidWeatherID); } mWeatherUpdateTime += mHoursBetweenWeatherChanges; return true; } return false; } inline bool WeatherManager::updateWeatherRegion(const std::string& playerRegion) { if(!playerRegion.empty() && playerRegion != mCurrentRegion) { mCurrentRegion = playerRegion; return true; } return false; } inline void WeatherManager::updateWeatherTransitions(const float elapsedRealSeconds) { // When a player chooses to train, wait, or serves jail time, any transitions will be fast forwarded to the last // weather type set, regardless of the remaining transition time. if(!mFastForward && inTransition()) { const float delta = mWeatherSettings[mNextWeather].transitionDelta(); mTransitionFactor -= elapsedRealSeconds * delta; if(mTransitionFactor <= 0.0f) { mCurrentWeather = mNextWeather; mNextWeather = mQueuedWeather; mQueuedWeather = invalidWeatherID; // We may have begun processing the queued transition, so we need to apply the remaining time towards it. if(inTransition()) { const float newDelta = mWeatherSettings[mNextWeather].transitionDelta(); const float remainingSeconds = -(mTransitionFactor / delta); mTransitionFactor = 1.0f - (remainingSeconds * newDelta); } else { mTransitionFactor = 0.0f; } } } else { if(mQueuedWeather != invalidWeatherID) { mCurrentWeather = mQueuedWeather; } else if(mNextWeather != invalidWeatherID) { mCurrentWeather = mNextWeather; } mNextWeather = invalidWeatherID; mQueuedWeather = invalidWeatherID; mFastForward = false; } } inline void WeatherManager::forceWeather(const int weatherID) { mTransitionFactor = 0.0f; mCurrentWeather = weatherID; mNextWeather = invalidWeatherID; mQueuedWeather = invalidWeatherID; } inline bool WeatherManager::inTransition() { return mNextWeather != invalidWeatherID; } inline void WeatherManager::addWeatherTransition(const int weatherID) { // In order to work like ChangeWeather expects, this method begins transitioning to the new weather immediately if // no transition is in progress, otherwise it queues it to be transitioned. assert(weatherID >= 0 && static_cast(weatherID) < mWeatherSettings.size()); if(!inTransition() && (weatherID != mCurrentWeather)) { mNextWeather = weatherID; mTransitionFactor = 1.0f; } else if(inTransition() && (weatherID != mNextWeather)) { mQueuedWeather = weatherID; } } inline void WeatherManager::calculateWeatherResult(const float gameHour, const float elapsedSeconds, const bool isPaused) { float flash = 0.0f; if(!inTransition()) { calculateResult(mCurrentWeather, gameHour); flash = mWeatherSettings[mCurrentWeather].calculateThunder(1.0f, elapsedSeconds, isPaused); } else { calculateTransitionResult(1 - mTransitionFactor, gameHour); float currentFlash = mWeatherSettings[mCurrentWeather].calculateThunder(mTransitionFactor, elapsedSeconds, isPaused); float nextFlash = mWeatherSettings[mNextWeather].calculateThunder(1 - mTransitionFactor, elapsedSeconds, isPaused); flash = currentFlash + nextFlash; } osg::Vec4f flashColor(flash, flash, flash, 0.0f); mResult.mFogColor += flashColor; mResult.mAmbientColor += flashColor; mResult.mSunColor += flashColor; } inline void WeatherManager::calculateResult(const int weatherID, const float gameHour) { const Weather& current = mWeatherSettings[weatherID]; mResult.mCloudTexture = current.mCloudTexture; mResult.mCloudBlendFactor = 0; mResult.mWindSpeed = current.mWindSpeed; mResult.mCloudSpeed = current.mCloudSpeed; mResult.mGlareView = current.mGlareView; mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; mResult.mAmbientSoundVolume = 1.f; mResult.mEffectFade = 1.f; mResult.mIsStorm = current.mIsStorm; mResult.mRainSpeed = current.mRainSpeed; mResult.mRainFrequency = current.mRainFrequency; mResult.mParticleEffect = current.mParticleEffect; mResult.mRainEffect = current.mRainEffect; mResult.mNight = (gameHour < mSunriseTime || gameHour > mTimeSettings.mNightStart - 1); mResult.mFogDepth = current.mLandFogDepth.getValue(gameHour, mTimeSettings); mResult.mFogColor = current.mFogColor.getValue(gameHour, mTimeSettings); mResult.mAmbientColor = current.mAmbientColor.getValue(gameHour, mTimeSettings); mResult.mSunColor = current.mSunColor.getValue(gameHour, mTimeSettings); mResult.mSkyColor = current.mSkyColor.getValue(gameHour, mTimeSettings); mResult.mNightFade = mNightFade.getValue(gameHour, mTimeSettings); if (gameHour >= mSunsetTime - mSunPreSunsetTime) { float factor = (gameHour - (mSunsetTime - mSunPreSunsetTime)) / mSunPreSunsetTime; factor = std::min(1.f, factor); mResult.mSunDiscColor = lerp(osg::Vec4f(1,1,1,1), current.mSunDiscSunsetColor, factor); // The SunDiscSunsetColor in the INI isn't exactly the resulting color on screen, most likely because // MW applied the color to the ambient term as well. After the ambient and emissive terms are added together, the fixed pipeline // would then clamp the total lighting to (1,1,1). A noticable change in color tone can be observed when only one of the color components gets clamped. // Unfortunately that means we can't use the INI color as is, have to replicate the above nonsense. mResult.mSunDiscColor = mResult.mSunDiscColor + osg::componentMultiply(mResult.mSunDiscColor, mResult.mAmbientColor); for (int i=0; i<3; ++i) mResult.mSunDiscColor[i] = std::min(1.f, mResult.mSunDiscColor[i]); } else mResult.mSunDiscColor = osg::Vec4f(1,1,1,1); if (gameHour >= mSunsetTime) { float fade = std::min(1.f, (gameHour - mSunsetTime) / 2.f); fade = fade*fade; mResult.mSunDiscColor.a() = 1.f - fade; } else if (gameHour >= mSunriseTime && gameHour <= mSunriseTime + 1) { mResult.mSunDiscColor.a() = gameHour - mSunriseTime; } else mResult.mSunDiscColor.a() = 1; } inline void WeatherManager::calculateTransitionResult(const float factor, const float gameHour) { calculateResult(mCurrentWeather, gameHour); const MWRender::WeatherResult current = mResult; calculateResult(mNextWeather, gameHour); const MWRender::WeatherResult other = mResult; mResult.mCloudTexture = current.mCloudTexture; mResult.mNextCloudTexture = other.mCloudTexture; mResult.mCloudBlendFactor = mWeatherSettings[mNextWeather].cloudBlendFactor(factor); mResult.mFogColor = lerp(current.mFogColor, other.mFogColor, factor); mResult.mSunColor = lerp(current.mSunColor, other.mSunColor, factor); mResult.mSkyColor = lerp(current.mSkyColor, other.mSkyColor, factor); mResult.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor, factor); mResult.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor, factor); mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor); mResult.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor); mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor); mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor); mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor); mResult.mNight = current.mNight; if(factor < 0.5) { mResult.mIsStorm = current.mIsStorm; mResult.mParticleEffect = current.mParticleEffect; mResult.mRainEffect = current.mRainEffect; mResult.mParticleEffect = current.mParticleEffect; mResult.mRainSpeed = current.mRainSpeed; mResult.mRainFrequency = current.mRainFrequency; mResult.mAmbientSoundVolume = 1-(factor*2); mResult.mEffectFade = mResult.mAmbientSoundVolume; mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; } else { mResult.mIsStorm = other.mIsStorm; mResult.mParticleEffect = other.mParticleEffect; mResult.mRainEffect = other.mRainEffect; mResult.mParticleEffect = other.mParticleEffect; mResult.mRainSpeed = other.mRainSpeed; mResult.mRainFrequency = other.mRainFrequency; mResult.mAmbientSoundVolume = 2*(factor-0.5f); mResult.mEffectFade = mResult.mAmbientSoundVolume; mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID; } } openmw-openmw-0.38.0/apps/openmw/mwworld/weather.hpp000066400000000000000000000225211264522266000225340ustar00rootroot00000000000000#ifndef GAME_MWWORLD_WEATHER_H #define GAME_MWWORLD_WEATHER_H #include #include #include #include #include "../mwbase/soundmanager.hpp" #include "../mwrender/sky.hpp" namespace ESM { struct Region; struct RegionWeatherState; class ESMWriter; class ESMReader; } namespace MWRender { class RenderingManager; } namespace Loading { class Listener; } namespace MWWorld { class Fallback; class TimeStamp; struct TimeOfDaySettings { float mNightStart; float mNightEnd; float mDayStart; float mDayEnd; float mSunriseTime; }; /// Interpolates between 4 data points (sunrise, day, sunset, night) based on the time of day. /// The template value could be a floating point number, or a color. template class TimeOfDayInterpolator { public: TimeOfDayInterpolator(const T& sunrise, const T& day, const T& sunset, const T& night) : mSunriseValue(sunrise), mDayValue(day), mSunsetValue(sunset), mNightValue(night) { } T getValue (const float gameHour, const TimeOfDaySettings& timeSettings) const; private: T mSunriseValue, mDayValue, mSunsetValue, mNightValue; }; /// Defines a single weather setting (according to INI) class Weather { public: Weather(const std::string& name, const MWWorld::Fallback& fallback, float stormWindSpeed, float rainSpeed, const std::string& particleEffect); std::string mCloudTexture; // Sky (atmosphere) color TimeOfDayInterpolator mSkyColor; // Fog color TimeOfDayInterpolator mFogColor; // Ambient lighting color TimeOfDayInterpolator mAmbientColor; // Sun (directional) lighting color TimeOfDayInterpolator mSunColor; // Fog depth/density TimeOfDayInterpolator mLandFogDepth; // Color modulation for the sun itself during sunset osg::Vec4f mSunDiscSunsetColor; // Used by scripts to animate signs, etc based on the wind (GetWindSpeed) float mWindSpeed; // Cloud animation speed multiplier float mCloudSpeed; // Value between 0 and 1, defines the strength of the sun glare effect. // Also appears to modify how visible the sun, moons, and stars are for various weather effects. float mGlareView; // Sound effect // This is used for Blight, Ashstorm and Blizzard (Bloodmoon) std::string mAmbientLoopSoundID; // Is this an ash storm / blight storm? If so, the following will happen: // - The particles and clouds will be oriented so they appear to come from the Red Mountain. // - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation) // - Slower movement when walking against the storm (fStromWalkMult) bool mIsStorm; // How fast does rain travel down? // In Morrowind.ini this is set globally, but we may want to change it per weather later. float mRainSpeed; // How often does a new rain mesh spawn? float mRainFrequency; std::string mParticleEffect; std::string mRainEffect; // Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature // is broken in the vanilla game and was disabled. float transitionDelta() const; float cloudBlendFactor(const float transitionRatio) const; float calculateThunder(const float transitionRatio, const float elapsedSeconds, const bool isPaused); private: float mTransitionDelta; float mCloudsMaximumPercent; // Note: In MW, only thunderstorms support these attributes, but in the interest of making weather more // flexible, these settings are imported for all weather types. Only thunderstorms will normally have any // non-zero values. float mThunderFrequency; float mThunderThreshold; std::string mThunderSoundID[4]; float mFlashDecrement; float mFlashBrightness; void flashDecrement(const float elapsedSeconds); float thunderChance(const float transitionRatio, const float elapsedSeconds) const; void lightningAndThunder(void); }; /// A class for storing a region's weather. class RegionWeather { public: explicit RegionWeather(const ESM::Region& region); explicit RegionWeather(const ESM::RegionWeatherState& state); operator ESM::RegionWeatherState() const; void setChances(const std::vector& chances); void setWeather(int weatherID); int getWeather(); private: int mWeather; std::vector mChances; void chooseNewWeather(); }; /// A class that acts as a model for the moons. class MoonModel { public: MoonModel(const std::string& name, const MWWorld::Fallback& fallback); MWRender::MoonState calculateState(const TimeStamp& gameTime) const; private: float mFadeInStart; float mFadeInFinish; float mFadeOutStart; float mFadeOutFinish; float mAxisOffset; float mSpeed; float mDailyIncrement; float mFadeStartAngle; float mFadeEndAngle; float mMoonShadowEarlyFadeAngle; float angle(const TimeStamp& gameTime) const; float moonRiseHour(unsigned int daysPassed) const; float rotation(float hours) const; unsigned int phase(const TimeStamp& gameTime) const; float shadowBlend(float angle) const; float hourlyAlpha(float gameHour) const; float earlyMoonShadowAlpha(float angle) const; }; /// Interface for weather settings class WeatherManager { public: // Have to pass fallback and Store, can't use singleton since World isn't fully constructed yet at the time WeatherManager(MWRender::RenderingManager& rendering, const MWWorld::Fallback& fallback, MWWorld::ESMStore& store); ~WeatherManager(); /** * Change the weather in the specified region * @param region that should be changed * @param ID of the weather setting to shift to */ void changeWeather(const std::string& regionID, const unsigned int weatherID); void modRegion(const std::string& regionID, const std::vector& chances); void playerTeleported(); /** * Per-frame update * @param duration * @param paused */ void update(float duration, bool paused = false); void stopSounds(); float getWindSpeed() const; /// Are we in an ash or blight storm? bool isInStorm() const; osg::Vec3f getStormDirection() const; void advanceTime(double hours, bool incremental); unsigned int getWeatherID() const; /// @see World::isDark bool isDark() const; void write(ESM::ESMWriter& writer, Loading::Listener& progress); bool readRecord(ESM::ESMReader& reader, uint32_t type); void clear(); private: MWWorld::ESMStore& mStore; MWRender::RenderingManager& mRendering; float mSunriseTime; float mSunsetTime; float mSunriseDuration; float mSunsetDuration; float mSunPreSunsetTime; TimeOfDaySettings mTimeSettings; // fading of night skydome TimeOfDayInterpolator mNightFade; float mHoursBetweenWeatherChanges; float mRainSpeed; // underwater fog not really related to weather, but we handle it here because it's convenient TimeOfDayInterpolator mUnderwaterFog; std::vector mWeatherSettings; MoonModel mMasser; MoonModel mSecunda; float mWindSpeed; bool mIsStorm; osg::Vec3f mStormDirection; std::string mCurrentRegion; float mTimePassed; bool mFastForward; float mWeatherUpdateTime; float mTransitionFactor; int mCurrentWeather; int mNextWeather; int mQueuedWeather; std::map mRegions; MWRender::WeatherResult mResult; MWBase::SoundPtr mAmbientSound; std::string mPlayingSoundID; void addWeather(const std::string& name, const MWWorld::Fallback& fallback, const std::string& particleEffect = ""); void importRegions(); void regionalWeatherChanged(const std::string& regionID, RegionWeather& region); bool updateWeatherTime(); bool updateWeatherRegion(const std::string& playerRegion); void updateWeatherTransitions(const float elapsedRealSeconds); void forceWeather(const int weatherID); bool inTransition(); void addWeatherTransition(const int weatherID); void calculateWeatherResult(const float gameHour, const float elapsedSeconds, const bool isPaused); void calculateResult(const int weatherID, const float gameHour); void calculateTransitionResult(const float factor, const float gameHour); }; } #endif // GAME_MWWORLD_WEATHER_H openmw-openmw-0.38.0/apps/openmw/mwworld/worldimp.cpp000066400000000000000000003262611264522266000227350ustar00rootroot00000000000000#include "worldimp.hpp" #if defined(_WIN32) && !defined(__MINGW32__) #include #elif defined HAVE_UNORDERED_MAP #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/levelledlist.hpp" #include "../mwmechanics/combat.hpp" #include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors #include "../mwrender/animation.hpp" #include "../mwrender/renderingmanager.hpp" #include "../mwrender/camera.hpp" #include "../mwrender/vismask.hpp" #include "../mwscript/interpretercontext.hpp" #include "../mwscript/globalscripts.hpp" #include "../mwclass/door.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwphysics/actor.hpp" #include "../mwphysics/collisiontype.hpp" #include "player.hpp" #include "manualref.hpp" #include "cellstore.hpp" #include "containerstore.hpp" #include "inventorystore.hpp" #include "actionteleport.hpp" #include "projectilemanager.hpp" #include "weather.hpp" #include "contentloader.hpp" #include "esmloader.hpp" namespace { // Wraps a value to (-PI, PI] void wrap(float& rad) { const float pi = static_cast(osg::PI); if (rad>0) rad = std::fmod(rad+pi, 2.0f*pi)-pi; else rad = std::fmod(rad-pi, 2.0f*pi)+pi; } } namespace MWWorld { struct GameContentLoader : public ContentLoader { GameContentLoader(Loading::Listener& listener) : ContentLoader(listener) { } bool addLoader(const std::string& extension, ContentLoader* loader) { return mLoaders.insert(std::make_pair(extension, loader)).second; } void load(const boost::filesystem::path& filepath, int& index) { LoadersContainer::iterator it(mLoaders.find(Misc::StringUtils::lowerCase(filepath.extension().string()))); if (it != mLoaders.end()) { it->second->load(filepath, index); } else { std::string msg("Cannot load file: "); msg += filepath.string(); throw std::runtime_error(msg.c_str()); } } private: typedef std::tr1::unordered_map LoadersContainer; LoadersContainer mLoaders; }; int World::getDaysPerMonth (int month) const { switch (month) { case 0: return 31; case 1: return 28; case 2: return 31; case 3: return 30; case 4: return 31; case 5: return 30; case 6: return 31; case 7: return 31; case 8: return 30; case 9: return 31; case 10: return 30; case 11: return 31; } throw std::runtime_error ("month out of range"); } void World::adjustSky() { if (mSky && (isCellExterior() || isCellQuasiExterior())) { mRendering->skySetDate (mDay->getInteger(), mMonth->getInteger()); mRendering->setSkyEnabled(true); } else mRendering->setSkyEnabled(false); } World::World ( osgViewer::Viewer* viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem, const Files::Collections& fileCollections, const std::vector& contentFiles, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int activationDistanceOverride, const std::string& startCell, const std::string& startupScript, const std::string& resourcePath) : mResourceSystem(resourceSystem), mFallback(fallbackMap), mPlayer (0), mLocalScripts (mStore), mSky (true), mCells (mStore, mEsm), mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), mStartCell (startCell), mTeleportEnabled(true), mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0) { mPhysics = new MWPhysics::PhysicsSystem(resourceSystem, rootNode); mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem, &mFallback, resourcePath); mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering, mPhysics)); mEsm.resize(contentFiles.size()); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); listener->loadingOn(); GameContentLoader gameContentLoader(*listener); EsmLoader esmLoader(mStore, mEsm, encoder, *listener); gameContentLoader.addLoader(".esm", &esmLoader); gameContentLoader.addLoader(".esp", &esmLoader); gameContentLoader.addLoader(".omwgame", &esmLoader); gameContentLoader.addLoader(".omwaddon", &esmLoader); gameContentLoader.addLoader(".project", &esmLoader); loadContentFiles(fileCollections, contentFiles, gameContentLoader); listener->loadingOff(); // insert records that may not be present in all versions of MW if (mEsm[0].getFormat() == 0) ensureNeededRecords(); fillGlobalVariables(); mStore.setUp(); mStore.movePlayerRecord(); mSwimHeightScale = mStore.get().find("fSwimHeightScale")->getFloat(); mWeatherManager = new MWWorld::WeatherManager(*mRendering, mFallback, mStore); mWorldScene = new Scene(*mRendering, mPhysics); } void World::fillGlobalVariables() { mGlobalVariables.fill (mStore); mGameHour = &mGlobalVariables["gamehour"]; mDaysPassed = &mGlobalVariables["dayspassed"]; mDay = &mGlobalVariables["day"]; mMonth = &mGlobalVariables["month"]; mYear = &mGlobalVariables["year"]; mTimeScale = &mGlobalVariables["timescale"]; } void World::startNewGame (bool bypass) { mGoToJail = false; mLevitationEnabled = true; mTeleportEnabled = true; mGodMode = false; mScriptsEnabled = true; mSky = true; // Rebuild player setupPlayer(); renderPlayer(); mRendering->resetCamera(); MWBase::Environment::get().getWindowManager()->updatePlayer(); // we don't want old weather to persist on a new game // Note that if reset later, the initial ChangeWeather that the chargen script calls will be lost. delete mWeatherManager; mWeatherManager = new MWWorld::WeatherManager(*mRendering, mFallback, mStore); if (!bypass) { // set new game mark mGlobalVariables["chargenstate"].setInteger (1); } else mGlobalVariables["chargenstate"].setInteger (-1); if (bypass && !mStartCell.empty()) { ESM::Position pos; if (findExteriorPosition (mStartCell, pos)) { changeToExteriorCell (pos); fixPosition(getPlayerPtr()); } else { findInteriorPosition (mStartCell, pos); changeToInteriorCell (mStartCell, pos); } } else { for (int i=0; i<5; ++i) MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); if (!getPlayerPtr().isInCell()) { ESM::Position pos; const int cellSize = 8192; pos.pos[0] = cellSize/2; pos.pos[1] = cellSize/2; pos.pos[2] = 0; pos.rot[0] = 0; pos.rot[1] = 0; pos.rot[2] = 0; mWorldScene->changeToExteriorCell(pos, true); } } if (!bypass) { std::string video = mFallback.getFallbackString("Movies_New_Game"); if (!video.empty()) MWBase::Environment::get().getWindowManager()->playVideo(video, true); } // enable collision if (!mPhysics->toggleCollisionMode()) mPhysics->toggleCollisionMode(); if (!mStartupScript.empty()) MWBase::Environment::get().getWindowManager()->executeInConsole(mStartupScript); } void World::clear() { mWeatherManager->clear(); mRendering->clear(); mProjectileManager->clear(); mLocalScripts.clear(); mWorldScene->changeToVoid(); mStore.clearDynamic(); mStore.setUp(); if (mPlayer) { mPlayer->clear(); mPlayer->setCell(0); mPlayer->getPlayer().getRefData() = RefData(); mPlayer->set(mStore.get().find ("player")); } mCells.clear(); mDoorStates.clear(); mTeleportEnabled = true; mLevitationEnabled = true; fillGlobalVariables(); } int World::countSavedGameRecords() const { return mCells.countSavedGameRecords() +mStore.countSavedGameRecords() +mGlobalVariables.countSavedGameRecords() +mProjectileManager->countSavedGameRecords() +1 // player record +1 // weather record +1 // actorId counter +1 // levitation/teleport enabled state +1; // camera } int World::countSavedGameCells() const { return mCells.countSavedGameRecords(); } void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { // Active cells could have a dirty fog of war, sync it to the CellStore first for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { CellStore* cellstore = *iter; MWBase::Environment::get().getWindowManager()->writeFog(cellstore); } MWMechanics::CreatureStats::writeActorIdCounter(writer); mStore.write (writer, progress); // dynamic Store must be written (and read) before Cells, so that // references to custom made records will be recognized mCells.write (writer, progress); mGlobalVariables.write (writer, progress); mPlayer->write (writer, progress); mWeatherManager->write (writer, progress); mProjectileManager->write (writer, progress); writer.startRecord(ESM::REC_ENAB); writer.writeHNT("TELE", mTeleportEnabled); writer.writeHNT("LEVT", mLevitationEnabled); writer.endRecord(ESM::REC_ENAB); writer.startRecord(ESM::REC_CAM_); writer.writeHNT("FIRS", isFirstPerson()); writer.endRecord(ESM::REC_CAM_); } void World::readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap) { switch (type) { case ESM::REC_ACTC: MWMechanics::CreatureStats::readActorIdCounter(reader); return; case ESM::REC_ENAB: reader.getHNT(mTeleportEnabled, "TELE"); reader.getHNT(mLevitationEnabled, "LEVT"); return; default: if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && !mPlayer->readRecord (reader, type) && !mWeatherManager->readRecord (reader, type) && !mCells.readRecord (reader, type, contentFileMap) && !mProjectileManager->readRecord (reader, type) ) { throw std::runtime_error ("unknown record in saved game"); } break; } } void World::ensureNeededRecords() { std::map gmst; // Companion (tribunal) gmst["sCompanionShare"] = ESM::Variant("Companion Share"); gmst["sCompanionWarningMessage"] = ESM::Variant("Warning message"); gmst["sCompanionWarningButtonOne"] = ESM::Variant("Button 1"); gmst["sCompanionWarningButtonTwo"] = ESM::Variant("Button 2"); gmst["sCompanionShare"] = ESM::Variant("Companion Share"); gmst["sProfitValue"] = ESM::Variant("Profit Value"); gmst["sTeleportDisabled"] = ESM::Variant("Teleport disabled"); gmst["sLevitateDisabled"] = ESM::Variant("Levitate disabled"); // Missing in unpatched MW 1.0 gmst["sDifficulty"] = ESM::Variant("Difficulty"); gmst["fDifficultyMult"] = ESM::Variant(5.f); gmst["sAuto_Run"] = ESM::Variant("Auto Run"); gmst["sServiceRefusal"] = ESM::Variant("Service Refusal"); gmst["sNeedOneSkill"] = ESM::Variant("Need one skill"); gmst["sNeedTwoSkills"] = ESM::Variant("Need two skills"); gmst["sEasy"] = ESM::Variant("Easy"); gmst["sHard"] = ESM::Variant("Hard"); gmst["sDeleteNote"] = ESM::Variant("Delete Note"); gmst["sEditNote"] = ESM::Variant("Edit Note"); gmst["sAdmireSuccess"] = ESM::Variant("Admire Success"); gmst["sAdmireFail"] = ESM::Variant("Admire Fail"); gmst["sIntimidateSuccess"] = ESM::Variant("Intimidate Success"); gmst["sIntimidateFail"] = ESM::Variant("Intimidate Fail"); gmst["sTauntSuccess"] = ESM::Variant("Taunt Success"); gmst["sTauntFail"] = ESM::Variant("Taunt Fail"); gmst["sBribeSuccess"] = ESM::Variant("Bribe Success"); gmst["sBribeFail"] = ESM::Variant("Bribe Fail"); gmst["fNPCHealthBarTime"] = ESM::Variant(5.f); gmst["fNPCHealthBarFade"] = ESM::Variant(1.f); // Werewolf (BM) gmst["fWereWolfRunMult"] = ESM::Variant(1.f); gmst["fWereWolfSilverWeaponDamageMult"] = ESM::Variant(1.f); std::map globals; // vanilla Morrowind does not define dayspassed. globals["dayspassed"] = ESM::Variant(1); // but the addons start counting at 1 :( globals["werewolfclawmult"] = ESM::Variant(25.f); globals["pcknownwerewolf"] = ESM::Variant(0); // following should exist in all versions of MW, but not necessarily in TCs globals["gamehour"] = ESM::Variant(0.f); globals["timescale"] = ESM::Variant(30.f); globals["day"] = ESM::Variant(1); globals["month"] = ESM::Variant(1); globals["year"] = ESM::Variant(1); globals["pcrace"] = ESM::Variant(0); globals["pchascrimegold"] = ESM::Variant(0); globals["pchasgolddiscount"] = ESM::Variant(0); globals["crimegolddiscount"] = ESM::Variant(0); globals["crimegoldturnin"] = ESM::Variant(0); globals["pchasturnin"] = ESM::Variant(0); for (std::map::iterator it = gmst.begin(); it != gmst.end(); ++it) { if (!mStore.get().search(it->first)) { ESM::GameSetting setting; setting.mId = it->first; setting.mValue = it->second; mStore.insertStatic(setting); } } for (std::map::iterator it = globals.begin(); it != globals.end(); ++it) { if (!mStore.get().search(it->first)) { ESM::Global setting; setting.mId = it->first; setting.mValue = it->second; mStore.insertStatic(setting); } } } World::~World() { // Must be cleared before mRendering is destroyed mProjectileManager->clear(); delete mWeatherManager; delete mWorldScene; delete mRendering; delete mPhysics; delete mPlayer; } const ESM::Cell *World::getExterior (const std::string& cellName) const { // first try named cells const ESM::Cell *cell = mStore.get().searchExtByName (cellName); if (cell != 0) { return cell; } // didn't work -> now check for regions const MWWorld::Store ®ions = mStore.get(); MWWorld::Store::iterator it = regions.begin(); for (; it != regions.end(); ++it) { if (Misc::StringUtils::ciEqual(cellName, it->mName)) { return mStore.get().searchExtByRegion(it->mId); } } return 0; } const MWWorld::Fallback *World::getFallback() const { return &mFallback; } CellStore *World::getExterior (int x, int y) { return mCells.getExterior (x, y); } CellStore *World::getInterior (const std::string& name) { return mCells.getInterior (name); } CellStore *World::getCell (const ESM::CellId& id) { if (id.mPaged) return getExterior (id.mIndex.mX, id.mIndex.mY); else return getInterior (id.mWorldspace); } void World::useDeathCamera() { if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() ) { mRendering->getCamera()->togglePreviewMode(false); mRendering->getCamera()->toggleVanityMode(false); } if(mRendering->getCamera()->isFirstPerson()) mRendering->getCamera()->toggleViewMode(true); } MWWorld::Player& World::getPlayer() { return *mPlayer; } const MWWorld::ESMStore& World::getStore() const { return mStore; } std::vector& World::getEsmReader() { return mEsm; } LocalScripts& World::getLocalScripts() { return mLocalScripts; } bool World::hasCellChanged() const { return mWorldScene->hasCellChanged(); } void World::setGlobalInt (const std::string& name, int value) { if (name=="gamehour") setHour (value); else if (name=="day") setDay (value); else if (name=="month") setMonth (value); else mGlobalVariables[name].setInteger (value); } void World::setGlobalFloat (const std::string& name, float value) { if (name=="gamehour") setHour (value); else if (name=="day") setDay(static_cast(value)); else if (name=="month") setMonth(static_cast(value)); else mGlobalVariables[name].setFloat (value); } int World::getGlobalInt (const std::string& name) const { return mGlobalVariables[name].getInteger(); } float World::getGlobalFloat (const std::string& name) const { return mGlobalVariables[name].getFloat(); } char World::getGlobalVariableType (const std::string& name) const { return mGlobalVariables.getType (name); } std::string World::getCellName (const MWWorld::CellStore *cell) const { if (!cell) cell = mWorldScene->getCurrentCell(); if (!cell->getCell()->isExterior() || !cell->getCell()->mName.empty()) return cell->getCell()->mName; if (const ESM::Region* region = getStore().get().search (cell->getCell()->mRegion)) return region->mName; return getStore().get().find ("sDefaultCellname")->mValue.getString(); } void World::removeRefScript (MWWorld::RefData *ref) { mLocalScripts.remove (ref); } Ptr World::searchPtr (const std::string& name, bool activeOnly) { Ptr ret; // the player is always in an active cell. if (name=="player") { return mPlayer->getPlayer(); } std::string lowerCaseName = Misc::StringUtils::lowerCase(name); for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { // TODO: caching still doesn't work efficiently here (only works for the one CellStore that the reference is in) CellStore* cellstore = *iter; Ptr ptr = mCells.getPtr (lowerCaseName, *cellstore, false); if (!ptr.isEmpty()) return ptr; } if (!activeOnly) { ret = mCells.getPtr (lowerCaseName); if (!ret.isEmpty()) return ret; } for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { CellStore* cellstore = *iter; Ptr ptr = cellstore->searchInContainer(lowerCaseName); if (!ptr.isEmpty()) return ptr; } Ptr ptr = mPlayer->getPlayer().getClass() .getContainerStore(mPlayer->getPlayer()).search(lowerCaseName); return ptr; } Ptr World::getPtr (const std::string& name, bool activeOnly) { Ptr ret = searchPtr(name, activeOnly); if (!ret.isEmpty()) return ret; throw std::runtime_error ("unknown ID: " + name); } Ptr World::searchPtrViaActorId (int actorId) { // The player is not registered in any CellStore so must be checked manually if (actorId == getPlayerPtr().getClass().getCreatureStats(getPlayerPtr()).getActorId()) return getPlayerPtr(); // Now search cells return mWorldScene->searchPtrViaActorId (actorId); } struct FindContainerVisitor { ConstPtr mContainedPtr; Ptr mResult; FindContainerVisitor(const ConstPtr& containedPtr) : mContainedPtr(containedPtr) {} bool operator() (Ptr ptr) { if (mContainedPtr.getContainerStore() == &ptr.getClass().getContainerStore(ptr)) { mResult = ptr; return false; } return true; } }; Ptr World::findContainer(const ConstPtr& ptr) { if (ptr.isInCell()) return Ptr(); Ptr player = getPlayerPtr(); if (ptr.getContainerStore() == &player.getClass().getContainerStore(player)) return player; const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { FindContainerVisitor visitor(ptr); (*cellIt)->forEachType(visitor); if (visitor.mResult.isEmpty()) (*cellIt)->forEachType(visitor); if (visitor.mResult.isEmpty()) (*cellIt)->forEachType(visitor); if (!visitor.mResult.isEmpty()) return visitor.mResult; } return Ptr(); } void World::addContainerScripts(const Ptr& reference, CellStore * cell) { if( reference.getTypeName()==typeid (ESM::Container).name() || reference.getTypeName()==typeid (ESM::NPC).name() || reference.getTypeName()==typeid (ESM::Creature).name()) { MWWorld::ContainerStore& container = reference.getClass().getContainerStore(reference); for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) { std::string script = it->getClass().getScript(*it); if(script != "") { MWWorld::Ptr item = *it; item.mCell = cell; mLocalScripts.add (script, item); } } } } void World::enable (const Ptr& reference) { // enable is a no-op for items in containers if (!reference.isInCell()) return; if (!reference.getRefData().isEnabled()) { reference.getRefData().enable(); if(mWorldScene->getActiveCells().find (reference.getCell()) != mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->addObjectToScene (reference); } } void World::removeContainerScripts(const Ptr& reference) { if( reference.getTypeName()==typeid (ESM::Container).name() || reference.getTypeName()==typeid (ESM::NPC).name() || reference.getTypeName()==typeid (ESM::Creature).name()) { MWWorld::ContainerStore& container = reference.getClass().getContainerStore(reference); for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) { std::string script = it->getClass().getScript(*it); if(script != "") { MWWorld::Ptr item = *it; mLocalScripts.remove (item); } } } } void World::disable (const Ptr& reference) { // disable is a no-op for items in containers if (!reference.isInCell()) return; if (reference.getRefData().isEnabled()) { if (reference == getPlayerPtr()) throw std::runtime_error("can not disable player object"); reference.getRefData().disable(); if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->removeObjectFromScene (reference); } } void World::advanceTime (double hours, bool incremental) { MWBase::Environment::get().getMechanicsManager()->advanceTime(static_cast(hours * 3600)); mWeatherManager->advanceTime (hours, incremental); hours += mGameHour->getFloat(); setHour (hours); int days = static_cast(hours / 24); if (days>0) mDaysPassed->setInteger ( days + mDaysPassed->getInteger()); } void World::setHour (double hour) { if (hour<0) hour = 0; int days = static_cast(hour / 24); hour = std::fmod (hour, 24); mGameHour->setFloat(static_cast(hour)); if (days>0) setDay (days + mDay->getInteger()); } void World::setDay (int day) { if (day<1) day = 1; int month = mMonth->getInteger(); while (true) { int days = getDaysPerMonth (month); if (day<=days) break; if (month<11) { ++month; } else { month = 0; mYear->setInteger(mYear->getInteger()+1); } day -= days; } mDay->setInteger(day); mMonth->setInteger(month); mRendering->skySetDate(day, month); } void World::setMonth (int month) { if (month<0) month = 0; int years = month / 12; month = month % 12; int days = getDaysPerMonth (month); if (mDay->getInteger()>days) mDay->setInteger (days); mMonth->setInteger (month); if (years>0) mYear->setInteger (years+mYear->getInteger()); mRendering->skySetDate (mDay->getInteger(), month); } int World::getDay() const { return mDay->getInteger(); } int World::getMonth() const { return mMonth->getInteger(); } int World::getYear() const { return mYear->getInteger(); } std::string World::getMonthName (int month) const { if (month==-1) month = getMonth(); const int months = 12; if (month<0 || month>=months) return ""; static const char *monthNames[months] = { "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" }; return getStore().get().find (monthNames[month])->mValue.getString(); } TimeStamp World::getTimeStamp() const { return TimeStamp (mGameHour->getFloat(), mDaysPassed->getInteger()); } bool World::toggleSky() { mSky = !mSky; mRendering->setSkyEnabled(mSky); return mSky; } int World::getMasserPhase() const { return mRendering->skyGetMasserPhase(); } int World::getSecundaPhase() const { return mRendering->skyGetSecundaPhase(); } void World::setMoonColour (bool red) { mRendering->skySetMoonColour (red); } float World::getTimeScaleFactor() const { return mTimeScale->getFloat(); } void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { mPhysics->clearQueuedMovement(); if (mCurrentWorldSpace != cellName) { // changed worldspace mProjectileManager->clear(); mRendering->notifyWorldSpaceChanged(); mCurrentWorldSpace = cellName; } removeContainerScripts(getPlayerPtr()); mWorldScene->changeToInteriorCell(cellName, position); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); } void World::changeToExteriorCell (const ESM::Position& position) { mPhysics->clearQueuedMovement(); if (mCurrentWorldSpace != "sys::default") // FIXME { // changed worldspace mProjectileManager->clear(); mRendering->notifyWorldSpaceChanged(); } removeContainerScripts(getPlayerPtr()); mWorldScene->changeToExteriorCell(position, true); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); } void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange) { if (!detectWorldSpaceChange) mCurrentWorldSpace = cellId.mWorldspace; if (cellId.mPaged) changeToExteriorCell (position); else changeToInteriorCell (cellId.mWorldspace, position); } void World::markCellAsUnchanged() { return mWorldScene->markCellAsUnchanged(); } float World::getMaxActivationDistance () { if (mActivationDistanceOverride >= 0) return static_cast(mActivationDistanceOverride); static const int iMaxActivateDist = getStore().get().find("iMaxActivateDist")->getInt(); return iMaxActivateDist * 5.f / 4.f; } MWWorld::Ptr World::getFacedObject() { MWWorld::Ptr facedObject; if (MWBase::Environment::get().getWindowManager()->isGuiMode() && MWBase::Environment::get().getWindowManager()->isConsoleMode()) facedObject = getFacedObject(getMaxActivationDistance() * 50, false); else { float telekinesisRangeBonus = mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() .get(ESM::MagicEffect::Telekinesis).getMagnitude(); telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; facedObject = getFacedObject(activationDistance); } return facedObject; } osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const { const MWRender::Animation *anim = mRendering->getAnimation(actor); if(anim) { const osg::Node *node = anim->getNode("Head"); if(!node) node = anim->getNode("Bip01 Head"); if(node) { osg::MatrixList mats = node->getWorldMatrices(); if(!mats.empty()) return mats[0]; } } return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3()); } std::pair World::getHitContact(const MWWorld::ConstPtr &ptr, float distance) { const ESM::Position &posdata = ptr.getRefData().getPosition(); osg::Quat rot = osg::Quat(posdata.rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0,0,-1)); osg::Vec3f pos = getActorHeadTransform(ptr).getTrans(); std::pair result = mPhysics->getHitContact(ptr, pos, rot, distance); if(result.first.isEmpty()) return std::make_pair(MWWorld::Ptr(), osg::Vec3f()); return std::make_pair(result.first, result.second); } void World::deleteObject (const Ptr& ptr) { if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == NULL) { if (ptr == getPlayerPtr()) throw std::runtime_error("can not delete player object"); ptr.getRefData().setCount(0); if (ptr.isInCell() && mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end() && ptr.getRefData().isEnabled()) { mWorldScene->removeObjectFromScene (ptr); mLocalScripts.remove (ptr); removeContainerScripts (ptr); } } } void World::undeleteObject(const Ptr& ptr) { if (!ptr.getCellRef().hasContentFile()) return; if (ptr.getRefData().isDeleted()) { ptr.getRefData().setCount(1); if (mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end() && ptr.getRefData().isEnabled()) { mWorldScene->addObjectToScene(ptr); std::string script = ptr.getClass().getScript(ptr); if (!script.empty()) mLocalScripts.add(script, ptr); addContainerScripts(ptr, ptr.getCell()); } } } MWWorld::Ptr World::moveObject(const Ptr &ptr, CellStore* newCell, float x, float y, float z) { ESM::Position pos = ptr.getRefData().getPosition(); pos.pos[0] = x; pos.pos[1] = y; pos.pos[2] = z; ptr.getRefData().setPosition(pos); osg::Vec3f vec(x, y, z); CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = isPlayer || (currCell && mWorldScene->isCellActive(*currCell)); MWWorld::Ptr newPtr = ptr; if (currCell != newCell) { removeContainerScripts(ptr); if (isPlayer) { if (!newCell->isExterior()) changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos); else { if (mWorldScene->isCellActive(*newCell)) mWorldScene->changePlayerCell(newCell, pos, false); else mWorldScene->changeToExteriorCell(pos, false); } addContainerScripts (getPlayerPtr(), newCell); newPtr = getPlayerPtr(); } else { bool currCellActive = mWorldScene->isCellActive(*currCell); bool newCellActive = mWorldScene->isCellActive(*newCell); if (!currCellActive && newCellActive) { newPtr = currCell->moveTo(ptr, newCell); mWorldScene->addObjectToScene(newPtr); std::string script = newPtr.getClass().getScript(newPtr); if (!script.empty()) { mLocalScripts.add(script, newPtr); } addContainerScripts(newPtr, newCell); } else if (!newCellActive && currCellActive) { mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); removeContainerScripts (ptr); haveToMove = false; newPtr = currCell->moveTo(ptr, newCell); newPtr.getRefData().setBaseNode(0); } else if (!currCellActive && !newCellActive) newPtr = currCell->moveTo(ptr, newCell); else // both cells active { newPtr = currCell->moveTo(ptr, newCell); mRendering->updatePtr(ptr, newPtr); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr); mPhysics->updatePtr(ptr, newPtr); MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->updateCell(ptr, newPtr); std::string script = ptr.getClass().getScript(ptr); if (!script.empty()) { mLocalScripts.remove(ptr); removeContainerScripts (ptr); mLocalScripts.add(script, newPtr); addContainerScripts (newPtr, newCell); } } } } if (haveToMove && newPtr.getRefData().getBaseNode()) { mRendering->moveObject(newPtr, vec); mPhysics->updatePosition(newPtr); } if (isPlayer) { mWorldScene->playerMoved(vec); } return newPtr; } MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z) { CellStore *cell = ptr.getCell(); if (cell->isExterior()) { int cellX, cellY; positionToIndex(x, y, cellX, cellY); cell = getExterior(cellX, cellY); } return moveObject(ptr, cell, x, y, z); } MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z) { return moveObjectImp(ptr, x, y, z); } void World::scaleObject (const Ptr& ptr, float scale) { ptr.getCellRef().setScale(scale); mWorldScene->updateObjectScale(ptr); } void World::rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, bool adjust) { const float pi = static_cast(osg::PI); ESM::Position pos = ptr.getRefData().getPosition(); float *objRot = pos.rot; if(adjust) { objRot[0] += rot.x(); objRot[1] += rot.y(); objRot[2] += rot.z(); } else { objRot[0] = rot.x(); objRot[1] = rot.y(); objRot[2] = rot.z(); } if(ptr.getClass().isActor()) { /* HACK? Actors shouldn't really be rotating around X (or Y), but * currently it's done so for rotating the camera, which needs * clamping. */ const float half_pi = pi/2.f; if(objRot[0] < -half_pi) objRot[0] = -half_pi; else if(objRot[0] > half_pi) objRot[0] = half_pi; wrap(objRot[1]); wrap(objRot[2]); } ptr.getRefData().setPosition(pos); if(ptr.getRefData().getBaseNode() != 0) mWorldScene->updateObjectRotation(ptr, true); } void World::adjustPosition(const Ptr &ptr, bool force) { ESM::Position pos (ptr.getRefData().getPosition()); if(!ptr.getRefData().getBaseNode()) { // will be adjusted when Ptr's cell becomes active return; } float terrainHeight = -std::numeric_limits::max(); if (ptr.getCell()->isExterior()) terrainHeight = mRendering->getTerrainHeightAt(pos.asVec3()); if (pos.pos[2] < terrainHeight) pos.pos[2] = terrainHeight; pos.pos[2] += 20; // place slightly above. will snap down to ground with code below ptr.getRefData().setPosition(pos); if (force || !isFlying(ptr)) { osg::Vec3f traced = mPhysics->traceDown(ptr, 500); if (traced.z() < pos.pos[2]) pos.pos[2] = traced.z(); } moveObject(ptr, ptr.getCell(), pos.pos[0], pos.pos[1], pos.pos[2]); } void World::fixPosition(const Ptr &actor) { const float dist = 8000; ESM::Position pos (actor.getRefData().getPosition()); pos.pos[2] += dist; actor.getRefData().setPosition(pos); osg::Vec3f traced = mPhysics->traceDown(actor, dist*1.1f); moveObject(actor, actor.getCell(), traced.x(), traced.y(), traced.z()); } void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) { rotateObjectImp(ptr, osg::Vec3f(x, y, z), adjust); } MWWorld::Ptr World::safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) { return copyObjectToCell(ptr,cell,pos,ptr.getRefData().getCount(),false); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const { const int cellSize = 8192; x = static_cast(cellSize * cellX); y = static_cast(cellSize * cellY); if (centre) { x += cellSize/2; y += cellSize/2; } } void World::positionToIndex (float x, float y, int &cellX, int &cellY) const { const int cellSize = 8192; cellX = static_cast(std::floor(x / cellSize)); cellY = static_cast(std::floor(y / cellSize)); } void World::queueMovement(const Ptr &ptr, const osg::Vec3f &velocity) { mPhysics->queueObjectMovement(ptr, velocity); } void World::doPhysics(float duration) { mPhysics->stepSimulation(duration); processDoors(duration); mProjectileManager->update(duration); const MWPhysics::PtrVelocityList &results = mPhysics->applyQueuedMovement(duration); MWPhysics::PtrVelocityList::const_iterator player(results.end()); for(MWPhysics::PtrVelocityList::const_iterator iter(results.begin());iter != results.end();++iter) { if(iter->first == getPlayerPtr()) { // Handle player last, in case a cell transition occurs player = iter; continue; } moveObjectImp(iter->first, iter->second.x(), iter->second.y(), iter->second.z()); } if(player != results.end()) moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z()); mPhysics->debugDraw(); } bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) { osg::Vec3f a(x1,y1,z1); osg::Vec3f b(x2,y2,z2); MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door); return result.mHit; } void World::processDoors(float duration) { std::map::iterator it = mDoorStates.begin(); while (it != mDoorStates.end()) { if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode()) { // The door is no longer in an active cell, or it was disabled. // Erase from mDoorStates, since we no longer need to move it. // Once we load the door's cell again (or re-enable the door), Door::insertObject will reinsert to mDoorStates. mDoorStates.erase(it++); } else { const ESM::Position& objPos = it->first.getRefData().getPosition(); float oldRot = objPos.rot[2]; float minRot = it->first.getCellRef().getPosition().rot[2]; float maxRot = minRot + osg::DegreesToRadians(90.f); float diff = duration * osg::DegreesToRadians(90.f); float targetRot = std::min(std::max(minRot, oldRot + diff * (it->second == 1 ? 1 : -1)), maxRot); rotateObject(it->first, objPos.rot[0], objPos.rot[1], targetRot); // the rotation order we want to use mWorldScene->updateObjectRotation(it->first, false); bool reached = (targetRot == maxRot && it->second) || targetRot == minRot; /// \todo should use convexSweepTest here std::vector collisions = mPhysics->getCollisions(it->first, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor); for (std::vector::iterator cit = collisions.begin(); cit != collisions.end(); ++cit) { MWWorld::Ptr ptr = *cit; if (ptr.getClass().isActor()) { // Collided with actor, ask actor to try to avoid door if(ptr != getPlayerPtr() ) { MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr); } // we need to undo the rotation rotateObject(it->first, objPos.rot[0], objPos.rot[1], oldRot); reached = false; } } if (reached) { // Mark as non-moving it->first.getClass().setDoorState(it->first, 0); mDoorStates.erase(it++); } else ++it; } } } bool World::toggleCollisionMode() { return mPhysics->toggleCollisionMode(); } bool World::toggleRenderMode (MWRender::RenderMode mode) { switch (mode) { case MWRender::Render_CollisionDebug: return mPhysics->toggleDebugRendering(); default: return mRendering->toggleRenderMode(mode); } } const ESM::Potion *World::createRecord (const ESM::Potion& record) { return mStore.insert(record); } const ESM::Class *World::createRecord (const ESM::Class& record) { return mStore.insert(record); } const ESM::Spell *World::createRecord (const ESM::Spell& record) { return mStore.insert(record); } const ESM::Cell *World::createRecord (const ESM::Cell& record) { return mStore.insert(record); } const ESM::CreatureLevList *World::createOverrideRecord(const ESM::CreatureLevList &record) { return mStore.overrideRecord(record); } const ESM::ItemLevList *World::createOverrideRecord(const ESM::ItemLevList &record) { return mStore.overrideRecord(record); } const ESM::NPC *World::createRecord(const ESM::NPC &record) { bool update = false; if (Misc::StringUtils::ciEqual(record.mId, "player")) { const ESM::NPC *player = mPlayer->getPlayer().get()->mBase; update = record.isMale() != player->isMale() || !Misc::StringUtils::ciEqual(record.mRace, player->mRace) || !Misc::StringUtils::ciEqual(record.mHead, player->mHead) || !Misc::StringUtils::ciEqual(record.mHair, player->mHair); } const ESM::NPC *ret = mStore.insert(record); if (update) { renderPlayer(); } return ret; } const ESM::Armor *World::createRecord (const ESM::Armor& record) { return mStore.insert(record); } const ESM::Weapon *World::createRecord (const ESM::Weapon& record) { return mStore.insert(record); } const ESM::Clothing *World::createRecord (const ESM::Clothing& record) { return mStore.insert(record); } const ESM::Enchantment *World::createRecord (const ESM::Enchantment& record) { return mStore.insert(record); } const ESM::Book *World::createRecord (const ESM::Book& record) { return mStore.insert(record); } void World::update (float duration, bool paused) { if (mGoToJail && !paused) goToJail(); updateWeather(duration, paused); if (!paused) doPhysics (duration); mWorldScene->update (duration, paused); updateWindowManager (); updateSoundListener(); updatePlayer(paused); } void World::updatePlayer(bool paused) { MWWorld::Ptr player = getPlayerPtr(); // TODO: move to MWWorld::Player if (player.getCell()->isExterior()) { ESM::Position pos = player.getRefData().getPosition(); mPlayer->setLastKnownExteriorPosition(pos.asVec3()); } bool isWerewolf = player.getClass().getNpcStats(player).isWerewolf(); bool isFirstPerson = mRendering->getCamera()->isFirstPerson(); if (isWerewolf && isFirstPerson) { float werewolfFov = mFallback.getFallbackFloat("General_Werewolf_FOV"); if (werewolfFov != 0) mRendering->overrideFieldOfView(werewolfFov); MWBase::Environment::get().getWindowManager()->setWerewolfOverlay(true); } else { mRendering->resetFieldOfView(); MWBase::Environment::get().getWindowManager()->setWerewolfOverlay(false); } // Sink the camera while sneaking bool sneaking = player.getClass().getCreatureStats(getPlayerPtr()).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool inair = !isOnGround(player); bool swimming = isSwimming(player); static const float i1stPersonSneakDelta = getStore().get().find("i1stPersonSneakDelta")->getFloat(); if(!paused && sneaking && !(swimming || inair)) mRendering->getCamera()->setSneakOffset(i1stPersonSneakDelta); else mRendering->getCamera()->setSneakOffset(0.f); int blind = static_cast(player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude()); MWBase::Environment::get().getWindowManager()->setBlindness(std::max(0, std::min(100, blind))); int nightEye = static_cast(player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::NightEye).getMagnitude()); mRendering->setNightEyeFactor(std::min(1.f, (nightEye/100.f))); mRendering->getCamera()->setCameraDistance(); if(!mRendering->getCamera()->isFirstPerson()) { osg::Vec3f focal, camera; mRendering->getCamera()->getPosition(focal, camera); float radius = mRendering->getNearClipDistance()*2.5f; MWPhysics::PhysicsSystem::RayResult result = mPhysics->castSphere(focal, camera, radius); if (result.mHit) mRendering->getCamera()->setCameraDistance((result.mHitPos - focal).length() - radius, false, false); } } void World::updateSoundListener() { const ESM::Position& refpos = getPlayerPtr().getRefData().getPosition(); osg::Vec3f listenerPos; if (isFirstPerson()) listenerPos = mRendering->getCameraPosition(); else listenerPos = refpos.asVec3() + osg::Vec3f(0, 0, 1.85f * mPhysics->getHalfExtents(getPlayerPtr()).z()); osg::Quat listenerOrient = osg::Quat(refpos.rot[1], osg::Vec3f(0,-1,0)) * osg::Quat(refpos.rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0,0,-1)); osg::Vec3f forward = listenerOrient * osg::Vec3f(0,1,0); osg::Vec3f up = listenerOrient * osg::Vec3f(0,0,1); bool underwater = isUnderwater(getPlayerPtr().getCell(), listenerPos); MWBase::Environment::get().getSoundManager()->setListenerPosDir(listenerPos, forward, up, underwater); } void World::updateWindowManager () { // inform the GUI about focused object MWWorld::Ptr object = getFacedObject (); MWBase::Environment::get().getWindowManager()->setFocusObject(object); // retrieve object dimensions so we know where to place the floating label if (!object.isEmpty ()) { osg::Vec4f screenBounds = mRendering->getScreenBounds(object); MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords( screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w()); } } MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer) { maxDistance += mRendering->getCameraDistance(); if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); return mRendering->castCameraToViewportRay(x, y, maxDistance, ignorePlayer).mHitObject; } else { return mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer).mHitObject; } } bool World::isCellExterior() const { const CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { return currentCell->getCell()->isExterior(); } return false; } bool World::isCellQuasiExterior() const { const CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { if (!(currentCell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) return false; else return true; } return false; } int World::getCurrentWeather() const { return mWeatherManager->getWeatherID(); } void World::changeWeather(const std::string& region, const unsigned int id) { mWeatherManager->changeWeather(region, id); } void World::modRegion(const std::string ®ionid, const std::vector &chances) { mWeatherManager->modRegion(regionid, chances); } osg::Vec2f World::getNorthVector (CellStore* cell) { MWWorld::Ptr northmarker = cell->search("northmarker"); if (northmarker.isEmpty()) return osg::Vec2f(0, 1); osg::Quat orient (-northmarker.getRefData().getPosition().rot[2], osg::Vec3f(0,0,1)); osg::Vec3f dir = orient * osg::Vec3f(0,1,0); osg::Vec2f d (dir.x(), dir.y()); return d; } struct GetDoorMarkerVisitor { GetDoorMarkerVisitor(std::vector& out) : mOut(out) { } std::vector& mOut; bool operator()(const MWWorld::Ptr& ptr) { MWWorld::LiveCellRef& ref = *static_cast* >(ptr.getBase()); if (!ref.mData.isEnabled() || ref.mData.isDeleted()) return true; if (ref.mRef.getTeleport()) { World::DoorMarker newMarker; newMarker.name = MWClass::Door::getDestination(ref); ESM::CellId cellid; if (!ref.mRef.getDestCell().empty()) { cellid.mWorldspace = ref.mRef.getDestCell(); cellid.mPaged = false; } else { cellid.mPaged = true; MWBase::Environment::get().getWorld()->positionToIndex( ref.mRef.getDoorDest().pos[0], ref.mRef.getDoorDest().pos[1], cellid.mIndex.mX, cellid.mIndex.mY); } newMarker.dest = cellid; ESM::Position pos = ref.mData.getPosition (); newMarker.x = pos.pos[0]; newMarker.y = pos.pos[1]; mOut.push_back(newMarker); } return true; } }; void World::getDoorMarkers (CellStore* cell, std::vector& out) { GetDoorMarkerVisitor visitor(out); cell->forEachType(visitor); } void World::setWaterHeight(const float height) { mPhysics->setWaterHeight(height); mRendering->setWaterHeight(height); } bool World::toggleWater() { return mRendering->toggleRenderMode(MWRender::Render_Water); } bool World::toggleWorld() { return mRendering->toggleRenderMode(MWRender::Render_Scene); } void World::PCDropped (const Ptr& item) { std::string script = item.getClass().getScript(item); // Set OnPCDrop Variable on item's script, if it has a script with that variable declared if(script != "") item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1); } MWWorld::Ptr World::placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) { const float maxDist = 200.f; MWRender::RenderingManager::RayResult result = mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true); CellStore* cell = getPlayerPtr().getCell(); ESM::Position pos = getPlayerPtr().getRefData().getPosition(); if (result.mHit) { pos.pos[0] = result.mHitPointWorld.x(); pos.pos[1] = result.mHitPointWorld.y(); pos.pos[2] = result.mHitPointWorld.z(); } // We want only the Z part of the player's rotation pos.rot[0] = 0; pos.rot[1] = 0; // copy the object and set its count Ptr dropped = copyObjectToCell(object, cell, pos, amount, true); // only the player place items in the world, so no need to check actor PCDropped(dropped); return dropped; } bool World::canPlaceObject(float cursorX, float cursorY) { const float maxDist = 200.f; MWRender::RenderingManager::RayResult result = mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true); if (result.mHit) { // check if the wanted position is on a flat surface, and not e.g. against a vertical wall if (std::acos((result.mHitNormalWorld/result.mHitNormalWorld.length()) * osg::Vec3f(0,0,1)) >= osg::DegreesToRadians(30.f)) return false; return true; } else return false; } Ptr World::copyObjectToCell(const ConstPtr &object, CellStore* cell, ESM::Position pos, int count, bool adjustPos) { if (cell->isExterior()) { int cellX, cellY; positionToIndex(pos.pos[0], pos.pos[1], cellX, cellY); cell = mCells.getExterior(cellX, cellY); } MWWorld::Ptr dropped = object.getClass().copyToCell(object, *cell, pos, count); // Reset some position values that could be uninitialized if this item came from a container dropped.getCellRef().setPosition(pos); dropped.getCellRef().unsetRefNum(); if (mWorldScene->isCellActive(*cell)) { if (dropped.getRefData().isEnabled()) { mWorldScene->addObjectToScene(dropped); } std::string script = dropped.getClass().getScript(dropped); if (!script.empty()) { mLocalScripts.add(script, dropped); } addContainerScripts(dropped, cell); } if (!object.getClass().isActor() && adjustPos && dropped.getRefData().getBaseNode()) { // Adjust position so the location we wanted ends up in the middle of the object bounding box osg::ComputeBoundsVisitor computeBounds; computeBounds.setTraversalMask(~MWRender::Mask_ParticleSystem); dropped.getRefData().getBaseNode()->accept(computeBounds); osg::BoundingBox bounds = computeBounds.getBoundingBox(); if (bounds.valid()) { bounds.set(bounds._min - pos.asVec3(), bounds._max - pos.asVec3()); osg::Vec3f adjust ( (bounds.xMin() + bounds.xMax()) / 2, (bounds.yMin() + bounds.yMax()) / 2, bounds.zMin() ); pos.pos[0] -= adjust.x(); pos.pos[1] -= adjust.y(); pos.pos[2] -= adjust.z(); moveObject(dropped, pos.pos[0], pos.pos[1], pos.pos[2]); } } return dropped; } MWWorld::Ptr World::dropObjectOnGround (const Ptr& actor, const ConstPtr& object, int amount) { MWWorld::CellStore* cell = actor.getCell(); ESM::Position pos = actor.getRefData().getPosition(); // We want only the Z part of the actor's rotation pos.rot[0] = 0; pos.rot[1] = 0; osg::Vec3f orig = pos.asVec3(); orig.z() += 20; osg::Vec3f dir (0, 0, -1); float len = 1000000.0; MWRender::RenderingManager::RayResult result = mRendering->castRay(orig, orig+dir*len, true, true); if (result.mHit) pos.pos[2] = result.mHitPointWorld.z(); // copy the object and set its count Ptr dropped = copyObjectToCell(object, cell, pos, amount, true); if(actor == mPlayer->getPlayer()) // Only call if dropped by player PCDropped(dropped); return dropped; } void World::processChangedSettings(const Settings::CategorySettingVector& settings) { mRendering->processChangedSettings(settings); } bool World::isFlying(const MWWorld::Ptr &ptr) const { const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); if(!ptr.getClass().isActor()) return false; if (stats.isDead()) return false; if (ptr.getClass().canFly(ptr)) return !stats.isParalyzed(); if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0 && isLevitationEnabled()) return true; const MWPhysics::Actor* actor = mPhysics->getActor(ptr); if(!actor || !actor->getCollisionMode()) return true; return false; } bool World::isSlowFalling(const MWWorld::Ptr &ptr) const { if(!ptr.getClass().isActor()) return false; const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); if(stats.getMagicEffects().get(ESM::MagicEffect::SlowFall).getMagnitude() > 0) return true; return false; } bool World::isSubmerged(const MWWorld::ConstPtr &object) const { return isUnderwater(object, 1.0f/mSwimHeightScale); } bool World::isSwimming(const MWWorld::ConstPtr &object) const { return isUnderwater(object, mSwimHeightScale); } bool World::isWading(const MWWorld::ConstPtr &object) const { const float kneeDeep = 0.25f; return isUnderwater(object, kneeDeep); } bool World::isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const { osg::Vec3f pos (object.getRefData().getPosition().asVec3()); pos.z() += heightRatio*2*mPhysics->getRenderingHalfExtents(object).z(); return isUnderwater(object.getCell(), pos); } bool World::isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const { if (!(cell->getCell()->mData.mFlags & ESM::Cell::HasWater)) { return false; } return pos.z() < cell->getWaterLevel(); } bool World::isOnGround(const MWWorld::Ptr &ptr) const { return mPhysics->isOnGround(ptr); } void World::togglePOV() { mRendering->togglePOV(); } bool World::isFirstPerson() const { return mRendering->getCamera()->isFirstPerson(); } void World::togglePreviewMode(bool enable) { mRendering->togglePreviewMode(enable); } bool World::toggleVanityMode(bool enable) { return mRendering->toggleVanityMode(enable); } void World::allowVanityMode(bool allow) { mRendering->allowVanityMode(allow); } void World::togglePlayerLooking(bool enable) { mRendering->togglePlayerLooking(enable); } void World::changeVanityModeScale(float factor) { mRendering->changeVanityModeScale(factor); } bool World::vanityRotateCamera(float * rot) { return mRendering->vanityRotateCamera(rot); } void World::setCameraDistance(float dist, bool adjust, bool override_) { mRendering->setCameraDistance(dist, adjust, override_); } void World::setupPlayer() { const ESM::NPC *player = mStore.get().find("player"); if (!mPlayer) mPlayer = new MWWorld::Player(player); else { // Remove the old CharacterController MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr()); mPhysics->remove(getPlayerPtr()); mRendering->removePlayer(getPlayerPtr()); mPlayer->set(player); } Ptr ptr = mPlayer->getPlayer(); mRendering->setupPlayer(ptr); } void World::renderPlayer() { MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr()); mRendering->renderPlayer(getPlayerPtr()); scaleObject(getPlayerPtr(), 1.f); // apply race height MWBase::Environment::get().getMechanicsManager()->add(getPlayerPtr()); MWBase::Environment::get().getMechanicsManager()->watchActor(getPlayerPtr()); std::string model = getPlayerPtr().getClass().getModel(getPlayerPtr()); model = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS()); mPhysics->remove(getPlayerPtr()); mPhysics->addActor(getPlayerPtr(), model); } int World::canRest () { CellStore *currentCell = mWorldScene->getCurrentCell(); Ptr player = mPlayer->getPlayer(); RefData &refdata = player.getRefData(); osg::Vec3f playerPos(refdata.getPosition().asVec3()); const MWPhysics::Actor* actor = mPhysics->getActor(player); if (!actor) throw std::runtime_error("can't find player"); if ((actor->getCollisionMode() && !mPhysics->isOnSolidGround(player)) || isUnderwater(currentCell, playerPos)) return 2; if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || player.getClass().getNpcStats(player).isWerewolf()) return 1; return 0; } MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr) { return mRendering->getAnimation(ptr); } const MWRender::Animation* World::getAnimation(const MWWorld::ConstPtr &ptr) const { return mRendering->getAnimation(ptr); } void World::screenshot(osg::Image* image, int w, int h) { mRendering->screenshot(image, w, h); } void World::activateDoor(const MWWorld::Ptr& door) { int state = door.getClass().getDoorState(door); switch (state) { case 0: if (door.getRefData().getPosition().rot[2] == door.getCellRef().getPosition().rot[2]) state = 1; // if closed, then open else state = 2; // if open, then close break; case 2: state = 1; // if closing, then open break; case 1: default: state = 2; // if opening, then close break; } door.getClass().setDoorState(door, state); mDoorStates[door] = state; } void World::activateDoor(const Ptr &door, int state) { door.getClass().setDoorState(door, state); mDoorStates[door] = state; if (state == 0) mDoorStates.erase(door); } bool World::getPlayerStandingOn (const MWWorld::ConstPtr& object) { MWWorld::Ptr player = getPlayerPtr(); return mPhysics->isActorStandingOn(player, object); } bool World::getActorStandingOn (const MWWorld::ConstPtr& object) { std::vector actors; mPhysics->getActorsStandingOn(object, actors); return !actors.empty(); } bool World::getPlayerCollidingWith (const MWWorld::ConstPtr& object) { MWWorld::Ptr player = getPlayerPtr(); return mPhysics->isActorCollidingWith(player, object); } bool World::getActorCollidingWith (const MWWorld::ConstPtr& object) { std::vector actors; mPhysics->getActorsCollidingWith(object, actors); return !actors.empty(); } void World::hurtStandingActors(const ConstPtr &object, float healthPerSecond) { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; std::vector actors; mPhysics->getActorsStandingOn(object, actors); for (std::vector::iterator it = actors.begin(); it != actors.end(); ++it) { MWWorld::Ptr actor = *it; MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); if (stats.isDead()) continue; MWMechanics::DynamicStat health = stats.getHealth(); health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration()); stats.setHealth(health); mPhysics->markAsNonSolid (object); if (healthPerSecond > 0.0f) { if (actor == getPlayerPtr()) MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); if (!MWBase::Environment::get().getSoundManager()->getSoundPlaying(actor, "Health Damage")) MWBase::Environment::get().getSoundManager()->playSound3D(actor, "Health Damage", 1.0f, 1.0f); } } } void World::hurtCollidingActors(const ConstPtr &object, float healthPerSecond) { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; std::vector actors; mPhysics->getActorsCollidingWith(object, actors); for (std::vector::iterator it = actors.begin(); it != actors.end(); ++it) { MWWorld::Ptr actor = *it; MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); if (stats.isDead()) continue; MWMechanics::DynamicStat health = stats.getHealth(); health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration()); stats.setHealth(health); mPhysics->markAsNonSolid (object); if (healthPerSecond > 0.0f) { if (actor == getPlayerPtr()) MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); if (!MWBase::Environment::get().getSoundManager()->getSoundPlaying(actor, "Health Damage")) MWBase::Environment::get().getSoundManager()->playSound3D(actor, "Health Damage", 1.0f, 1.0f); } } } float World::getWindSpeed() { if (isCellExterior() || isCellQuasiExterior()) return mWeatherManager->getWindSpeed(); else return 0.f; } bool World::isInStorm() const { if (isCellExterior() || isCellQuasiExterior()) return mWeatherManager->isInStorm(); else return false; } osg::Vec3f World::getStormDirection() const { if (isCellExterior() || isCellQuasiExterior()) return mWeatherManager->getStormDirection(); else return osg::Vec3f(0,1,0); } struct GetContainersOwnedByVisitor { GetContainersOwnedByVisitor(const MWWorld::ConstPtr& owner, std::vector& out) : mOwner(owner) , mOut(out) { } MWWorld::ConstPtr mOwner; std::vector& mOut; bool operator()(const MWWorld::Ptr& ptr) { if (ptr.getRefData().isDeleted()) return true; if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), mOwner.getCellRef().getRefId())) mOut.push_back(ptr); return true; } }; void World::getContainersOwnedBy (const MWWorld::ConstPtr& owner, std::vector& out) { const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { GetContainersOwnedByVisitor visitor (owner, out); (*cellIt)->forEachType(visitor); } } struct ListObjectsVisitor { std::vector mObjects; bool operator() (Ptr ptr) { if (ptr.getRefData().getBaseNode()) mObjects.push_back(ptr); return true; } }; void World::getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out) { const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { ListObjectsVisitor visitor; (*cellIt)->forEach(visitor); for (std::vector::iterator it = visitor.mObjects.begin(); it != visitor.mObjects.end(); ++it) if (Misc::StringUtils::ciEqual(it->getCellRef().getOwner(), npc.getCellRef().getRefId())) out.push_back(*it); } } bool World::getLOS(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& targetActor) { if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled()) return false; // cannot get LOS unless both NPC's are enabled if (!targetActor.getRefData().getBaseNode() || !actor.getRefData().getBaseNode()) return false; // not in active cell return mPhysics->getLineOfSight(actor, targetActor); } float World::getDistToNearestRayHit(const osg::Vec3f& from, const osg::Vec3f& dir, float maxDist) { osg::Vec3f to (dir); to.normalize(); to = from + (to * maxDist); MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(from, to, MWWorld::Ptr(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_HeightMap|MWPhysics::CollisionType_Door); if (!result.mHit) return maxDist; else return (result.mHitPos - from).length(); } void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable) { MWPhysics::Actor *physicActor = mPhysics->getActor(actor); if (physicActor) physicActor->enableCollisionBody(enable); } bool World::findInteriorPosition(const std::string &name, ESM::Position &pos) { typedef MWWorld::CellRefList::List DoorList; typedef MWWorld::CellRefList::List StaticList; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; MWWorld::CellStore *cellStore = getInterior(name); if (0 == cellStore) { return false; } const DoorList &doors = cellStore->getReadOnlyDoors().mList; for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) { if (!it->mRef.getTeleport()) { continue; } MWWorld::CellStore *source = 0; // door to exterior if (it->mRef.getDestCell().empty()) { int x, y; ESM::Position doorDest = it->mRef.getDoorDest(); positionToIndex(doorDest.pos[0], doorDest.pos[1], x, y); source = getExterior(x, y); } // door to interior else { source = getInterior(it->mRef.getDestCell()); } if (0 != source) { // Find door leading to our current teleport door // and use it destination to position inside cell. const DoorList &doors = source->getReadOnlyDoors().mList; for (DoorList::const_iterator jt = doors.begin(); jt != doors.end(); ++jt) { if (it->mRef.getTeleport() && Misc::StringUtils::ciEqual(name, jt->mRef.getDestCell())) { /// \note Using _any_ door pointed to the interior, /// not the one pointed to current door. pos = jt->mRef.getDoorDest(); return true; } } } } // Fall back to the first static location. const StaticList &statics = cellStore->getReadOnlyStatics().mList; if ( statics.begin() != statics.end() ) { pos = statics.begin()->mRef.getPosition(); return true; } return false; } bool World::findExteriorPosition(const std::string &name, ESM::Position &pos) { pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; if (const ESM::Cell *ext = getExterior(name)) { int x = ext->getGridX(); int y = ext->getGridY(); indexToPosition(x, y, pos.pos[0], pos.pos[1], true); // Note: Z pos will be adjusted by adjustPosition later pos.pos[2] = 0; return true; } return false; } void World::enableTeleporting(bool enable) { mTeleportEnabled = enable; } bool World::isTeleportingEnabled() const { return mTeleportEnabled; } void World::enableLevitation(bool enable) { mLevitationEnabled = enable; } bool World::isLevitationEnabled() const { return mLevitationEnabled; } void World::reattachPlayerCamera() { mRendering->rebuildPtr(getPlayerPtr()); } bool World::getGodModeState() { return mGodMode; } bool World::toggleGodMode() { mGodMode = !mGodMode; return mGodMode; } bool World::toggleScripts() { mScriptsEnabled = !mScriptsEnabled; return mScriptsEnabled; } bool World::getScriptsEnabled() const { return mScriptsEnabled; } void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, ContentLoader& contentLoader) { std::vector::const_iterator it(content.begin()); std::vector::const_iterator end(content.end()); for (int idx = 0; it != end; ++it, ++idx) { boost::filesystem::path filename(*it); const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string()); if (col.doesExist(*it)) { contentLoader.load(col.getPath(*it), idx); } else { std::stringstream msg; msg << "Failed loading " << *it << ": the content file does not exist"; throw std::runtime_error(msg.str()); } } } bool World::startSpellCast(const Ptr &actor) { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); std::string message; bool fail = false; bool isPlayer = (actor == getPlayerPtr()); std::string selectedSpell = stats.getSpells().getSelectedSpell(); if (!selectedSpell.empty()) { const ESM::Spell* spell = getStore().get().find(selectedSpell); // Check mana MWMechanics::DynamicStat magicka = stats.getMagicka(); if (magicka.getCurrent() < spell->mData.mCost && !(isPlayer && getGodModeState())) { message = "#{sMagicInsufficientSP}"; fail = true; } // If this is a power, check if it was already used in the last 24h if (!fail && spell->mData.mType == ESM::Spell::ST_Power && !stats.getSpells().canUsePower(spell)) { message = "#{sPowerAlreadyUsed}"; fail = true; } // Reduce mana if (!fail) { magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); stats.setMagicka(magicka); } } if (isPlayer && fail) MWBase::Environment::get().getWindowManager()->messageBox(message); return !fail; } void World::castSpell(const Ptr &actor) { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); // Get the target to use for "on touch" effects, using the facing direction from Head node MWWorld::Ptr target; float distance = 192.f; // ?? osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3(); osg::Vec3f origin = getActorHeadTransform(actor).getTrans(); osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0)) * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1)); osg::Vec3f direction = orient * osg::Vec3f(0,1,0); osg::Vec3f dest = origin + direction * distance; // For actor targets, we want to use bounding boxes (physics raycast). // This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise. // For object targets, we want the detailed shapes (rendering raycast). // If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf. MWPhysics::PhysicsSystem::RayResult result1 = mPhysics->castRay(origin, dest, actor, MWPhysics::CollisionType_Actor); MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true); float dist1 = std::numeric_limits::max(); float dist2 = std::numeric_limits::max(); if (result1.mHit) dist1 = (origin - result1.mHitPos).length(); if (result2.mHit) dist2 = (origin - result2.mHitPointWorld).length(); if (dist1 <= dist2 && result1.mHit) { target = result1.mHitObject; hitPosition = result1.mHitPos; } else if (result2.mHit) { target = result2.mHitObject; hitPosition = result2.mHitPointWorld; } std::string selectedSpell = stats.getSpells().getSelectedSpell(); MWMechanics::CastSpell cast(actor, target); cast.mHitPosition = hitPosition; if (!selectedSpell.empty()) { const ESM::Spell* spell = getStore().get().find(selectedSpell); cast.cast(spell); } else if (actor.getClass().hasInventoryStore(actor)) { MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); if (inv.getSelectedEnchantItem() != inv.end()) cast.cast(*inv.getSelectedEnchantItem()); } } void World::launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) { mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength); } void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection) { mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, caster, sourceName, fallbackDirection); } const std::vector& World::getContentFiles() const { return mContentFiles; } void World::breakInvisibility(const Ptr &actor) { actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); if (actor.getClass().hasInventoryStore(actor)) actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); // Normally updated once per frame, but here it is kinda important to do it right away. MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor); } bool World::isDark() const { MWWorld::CellStore* cell = mPlayer->getPlayer().getCell(); if (cell->isExterior()) return mWeatherManager->isDark(); else { uint32_t ambient = cell->getCell()->mAmbi.mAmbient; int ambientTotal = (ambient & 0xff) + ((ambient>>8) & 0xff) + ((ambient>>16) & 0xff); return !(cell->getCell()->mData.mFlags & ESM::Cell::NoSleep) && ambientTotal <= 201; } } bool World::findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, osg::Vec3f& result) { if (cell->isExterior()) return false; // Search for a 'nearest' exterior, counting each cell between the starting // cell and the exterior as a distance of 1. Will fail for isolated interiors. std::set< std::string >checkedCells; std::set< std::string >currentCells; std::set< std::string >nextCells; nextCells.insert( cell->getCell()->mName ); while ( !nextCells.empty() ) { currentCells = nextCells; nextCells.clear(); for( std::set< std::string >::const_iterator i = currentCells.begin(); i != currentCells.end(); ++i ) { MWWorld::CellStore *next = getInterior( *i ); if ( !next ) continue; const MWWorld::CellRefList& doors = next->getReadOnlyDoors(); const CellRefList::List& refList = doors.mList; // Check if any door in the cell leads to an exterior directly for (CellRefList::List::const_iterator it = refList.begin(); it != refList.end(); ++it) { const MWWorld::LiveCellRef& ref = *it; if (!ref.mRef.getTeleport()) continue; if (ref.mRef.getDestCell().empty()) { ESM::Position pos = ref.mRef.getDoorDest(); result = pos.asVec3(); return true; } else { std::string dest = ref.mRef.getDestCell(); if ( !checkedCells.count(dest) && !currentCells.count(dest) ) nextCells.insert(dest); } } checkedCells.insert( *i ); } } // No luck :( return false; } MWWorld::ConstPtr World::getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id ) { if ( ptr.getCell()->isExterior() ) { return getClosestMarkerFromExteriorPosition(mPlayer->getLastKnownExteriorPosition(), id); } // Search for a 'nearest' marker, counting each cell between the starting // cell and the exterior as a distance of 1. If an exterior is found, jump // to the nearest exterior marker, without further interior searching. std::set< std::string >checkedCells; std::set< std::string >currentCells; std::set< std::string >nextCells; MWWorld::ConstPtr closestMarker; nextCells.insert( ptr.getCell()->getCell()->mName ); while ( !nextCells.empty() ) { currentCells = nextCells; nextCells.clear(); for( std::set< std::string >::const_iterator i = currentCells.begin(); i != currentCells.end(); ++i ) { MWWorld::CellStore *next = getInterior( *i ); checkedCells.insert( *i ); if ( !next ) continue; closestMarker = next->searchConst( id ); if ( !closestMarker.isEmpty() ) { return closestMarker; } const MWWorld::CellRefList& doors = next->getReadOnlyDoors(); const CellRefList::List& doorList = doors.mList; // Check if any door in the cell leads to an exterior directly for (CellRefList::List::const_iterator it = doorList.begin(); it != doorList.end(); ++it) { const MWWorld::LiveCellRef& ref = *it; if (!ref.mRef.getTeleport()) continue; if (ref.mRef.getDestCell().empty()) { osg::Vec3f worldPos = ref.mRef.getDoorDest().asVec3(); return getClosestMarkerFromExteriorPosition(worldPos, id); } else { std::string dest = ref.mRef.getDestCell(); if ( !checkedCells.count(dest) && !currentCells.count(dest) ) nextCells.insert(dest); } } } } return MWWorld::Ptr(); } MWWorld::ConstPtr World::getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ) { MWWorld::ConstPtr closestMarker; float closestDistance = std::numeric_limits::max(); std::vector markers; mCells.getExteriorPtrs(id, markers); for (std::vector::iterator it2 = markers.begin(); it2 != markers.end(); ++it2) { ESM::Position pos = it2->getRefData().getPosition(); osg::Vec3f markerPos = pos.asVec3(); float distance = (worldPos - markerPos).length2(); if (distance < closestDistance) { closestDistance = distance; closestMarker = *it2; } } return closestMarker; } void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id) { MWWorld::ConstPtr closestMarker = getClosestMarker( ptr, id ); if ( closestMarker.isEmpty() ) { std::cerr << "Failed to teleport: no closest marker found" << std::endl; return; } std::string cellName; if ( !closestMarker.mCell->isExterior() ) cellName = closestMarker.mCell->getCell()->mName; MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false); action.execute(ptr); } void World::updateWeather(float duration, bool paused) { if (mPlayer->wasTeleported()) { mPlayer->setTeleported(false); mWeatherManager->playerTeleported(); } mWeatherManager->update(duration, paused); } struct AddDetectedReferenceVisitor { AddDetectedReferenceVisitor(std::vector& out, Ptr detector, World::DetectionType type, float squaredDist) : mOut(out), mDetector(detector), mSquaredDist(squaredDist), mType(type) { } std::vector& mOut; Ptr mDetector; float mSquaredDist; World::DetectionType mType; bool operator() (const MWWorld::Ptr& ptr) { if ((ptr.getRefData().getPosition().asVec3() - mDetector.getRefData().getPosition().asVec3()).length2() >= mSquaredDist) return true; if (!ptr.getRefData().isEnabled() || ptr.getRefData().isDeleted()) return true; // Consider references inside containers as well (except if we are looking for a Creature, they cannot be in containers) if (mType != World::Detect_Creature && (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name())) { MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); { for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { if (needToAdd(*it, mDetector)) { mOut.push_back(ptr); return true; } } } } if (needToAdd(ptr, mDetector)) mOut.push_back(ptr); return true; } bool needToAdd (const MWWorld::Ptr& ptr, const MWWorld::Ptr& detector) { if (mType == World::Detect_Creature) { // If in werewolf form, this detects only NPCs, otherwise only creatures if (detector.getClass().isNpc() && detector.getClass().getNpcStats(detector).isWerewolf()) { if (ptr.getClass().getTypeName() != typeid(ESM::NPC).name()) return false; } else if (ptr.getClass().getTypeName() != typeid(ESM::Creature).name()) return false; if (ptr.getClass().getCreatureStats(ptr).isDead()) return false; } if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr)) return false; if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty()) return false; return true; } }; void World::listDetectedReferences(const Ptr &ptr, std::vector &out, DetectionType type) { const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); float dist=0; if (type == World::Detect_Creature) dist = effects.get(ESM::MagicEffect::DetectAnimal).getMagnitude(); else if (type == World::Detect_Key) dist = effects.get(ESM::MagicEffect::DetectKey).getMagnitude(); else if (type == World::Detect_Enchantment) dist = effects.get(ESM::MagicEffect::DetectEnchantment).getMagnitude(); if (!dist) return; dist = feetToGameUnits(dist); AddDetectedReferenceVisitor visitor (out, ptr, type, dist*dist); const Scene::CellStoreCollection& active = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator it = active.begin(); it != active.end(); ++it) { MWWorld::CellStore* cellStore = *it; cellStore->forEach(visitor); } } float World::feetToGameUnits(float feet) { // Looks like there is no GMST for this. This factor was determined in experiments // with the Telekinesis effect. return feet * 22; } MWWorld::Ptr World::getPlayerPtr() { return mPlayer->getPlayer(); } void World::updateDialogueGlobals() { MWWorld::Ptr player = getPlayerPtr(); int bounty = player.getClass().getNpcStats(player).getBounty(); int playerGold = player.getClass().getContainerStore(player).count(ContainerStore::sGoldId); float fCrimeGoldDiscountMult = getStore().get().find("fCrimeGoldDiscountMult")->getFloat(); float fCrimeGoldTurnInMult = getStore().get().find("fCrimeGoldTurnInMult")->getFloat(); int discount = static_cast(bounty * fCrimeGoldDiscountMult); int turnIn = static_cast(bounty * fCrimeGoldTurnInMult); if (bounty > 0) { discount = std::max(1, discount); turnIn = std::max(1, turnIn); } mGlobalVariables["pchascrimegold"].setInteger((bounty <= playerGold) ? 1 : 0); mGlobalVariables["pchasgolddiscount"].setInteger((discount <= playerGold) ? 1 : 0); mGlobalVariables["crimegolddiscount"].setInteger(discount); mGlobalVariables["crimegoldturnin"].setInteger(turnIn); mGlobalVariables["pchasturnin"].setInteger((turnIn <= playerGold) ? 1 : 0); } void World::confiscateStolenItems(const Ptr &ptr) { MWWorld::ConstPtr prisonMarker = getClosestMarker( ptr, "prisonmarker" ); if ( prisonMarker.isEmpty() ) { std::cerr << "Failed to confiscate items: no closest prison marker found." << std::endl; return; } std::string prisonName = prisonMarker.getCellRef().getDestCell(); if ( prisonName.empty() ) { std::cerr << "Failed to confiscate items: prison marker not linked to prison interior" << std::endl; return; } MWWorld::CellStore *prison = getInterior( prisonName ); if ( !prison ) { std::cerr << "Failed to confiscate items: failed to load cell " << prisonName << std::endl; return; } MWWorld::Ptr closestChest = prison->search( "stolen_goods" ); if (!closestChest.isEmpty()) //Found a close chest { MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest); } else std::cerr << "Failed to confiscate items: no stolen_goods container found" << std::endl; } void World::goToJail() { if (!mGoToJail) { // Reset bounty and forget the crime now, but don't change cell yet (the player should be able to read the dialog text first) mGoToJail = true; MWWorld::Ptr player = getPlayerPtr(); int bounty = player.getClass().getNpcStats(player).getBounty(); player.getClass().getNpcStats(player).setBounty(0); mPlayer->recordCrimeId(); confiscateStolenItems(player); int iDaysinPrisonMod = getStore().get().find("iDaysinPrisonMod")->getInt(); mDaysInPrison = std::max(1, bounty / iDaysinPrisonMod); return; } else { mGoToJail = false; MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); MWBase::Environment::get().getWindowManager()->goToJail(mDaysInPrison); } } void World::spawnRandomCreature(const std::string &creatureList) { const ESM::CreatureLevList* list = getStore().get().find(creatureList); int iNumberCreatures = getStore().get().find("iNumberCreatures")->getInt(); int numCreatures = 1 + Misc::Rng::rollDice(iNumberCreatures); // [1, iNumberCreatures] for (int i=0; igetPlayer().getRefData().getPosition(); osg::Vec3f pos(ipos.asVec3()); osg::Quat rot(-ipos.rot[2], osg::Vec3f(0,0,1)); const float distance = 50; pos = pos + (rot * osg::Vec3f(0,1,0)) * distance; ipos.pos[0] = pos.x(); ipos.pos[1] = pos.y(); ipos.pos[2] = pos.z(); ipos.rot[0] = 0; ipos.rot[1] = 0; ipos.rot[2] = 0; MWWorld::CellStore* cell = mPlayer->getPlayer().getCell(); MWWorld::ManualRef ref(getStore(), selectedCreature, 1); ref.getPtr().getCellRef().setPosition(ipos); safePlaceObject(ref.getPtr(), cell, ipos); } } void World::spawnBloodEffect(const Ptr &ptr, const osg::Vec3f &worldPosition) { if (ptr == getPlayerPtr() && Settings::Manager::getBool("hit fader", "GUI")) return; int type = ptr.getClass().getBloodTexture(ptr); std::string texture; switch (type) { case 2: texture = getFallback()->getFallbackString("Blood_Texture_2"); break; case 1: texture = getFallback()->getFallbackString("Blood_Texture_1"); break; case 0: default: texture = getFallback()->getFallbackString("Blood_Texture_0"); break; } std::stringstream modelName; modelName << "Blood_Model_"; int roll = Misc::Rng::rollDice(3); // [0, 2] modelName << roll; std::string model = "meshes\\" + getFallback()->getFallbackString(modelName.str()); mRendering->spawnEffect(model, texture, worldPosition); } void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos) { mRendering->spawnEffect(model, textureOverride, worldPos); } void World::explodeSpell(const osg::Vec3f &origin, const ESM::EffectList &effects, const Ptr &caster, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName) { std::map > toApply; for (std::vector::const_iterator effectIt = effects.mList.begin(); effectIt != effects.mList.end(); ++effectIt) { const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); if (effectIt->mArea <= 0) continue; // Not an area effect // Spawn the explosion orb effect const ESM::Static* areaStatic; if (!effect->mArea.empty()) areaStatic = getStore().get().find (effect->mArea); else areaStatic = getStore().get().find ("VFX_DefaultArea"); mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", origin, static_cast(effectIt->mArea)); // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(!effect->mAreaSound.empty()) sndMgr->playSound3D(origin, effect->mAreaSound, 1.0f, 1.0f); else sndMgr->playSound3D(origin, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f); } // Get the actors in range of the effect std::vector objects; MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( origin, feetToGameUnits(static_cast(effectIt->mArea)), objects); for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) toApply[*affected].push_back(*effectIt); } // Now apply the appropriate effects to each actor in range for (std::map >::iterator apply = toApply.begin(); apply != toApply.end(); ++apply) { MWWorld::Ptr source = caster; // Vanilla-compatible behaviour of never applying the spell to the caster // (could be changed by mods later) if (apply->first == caster) continue; if (source.isEmpty()) source = apply->first; MWMechanics::CastSpell cast(source, apply->first); cast.mHitPosition = origin; cast.mId = id; cast.mSourceName = sourceName; cast.mStack = false; ESM::EffectList effects; effects.mList = apply->second; cast.inflict(apply->first, caster, effects, rangeType, false, true); } } void World::activate(const Ptr &object, const Ptr &actor) { MWScript::InterpreterContext interpreterContext (&object.getRefData().getLocals(), object); interpreterContext.activate (object); std::string script = object.getClass().getScript (object); breakInvisibility(actor); if (mScriptsEnabled) { if (!script.empty()) { getLocalScripts().setIgnore (object); MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); } if (!interpreterContext.hasActivationBeenHandled()) interpreterContext.executeActivation(object, actor); } else interpreterContext.executeActivation(object, actor); } struct ResetActorsVisitor { bool operator() (Ptr ptr) { // Can't reset actors that were moved to a different cell, because we don't know what cell they came from. // This could be fixed once we properly track actor cell changes, but may not be desirable behaviour anyhow. if (ptr.getClass().isActor() && ptr.getCellRef().hasContentFile()) { const ESM::Position& origPos = ptr.getCellRef().getPosition(); MWBase::Environment::get().getWorld()->moveObject(ptr, origPos.pos[0], origPos.pos[1], origPos.pos[2]); MWBase::Environment::get().getWorld()->rotateObject(ptr, origPos.rot[0], origPos.rot[1], origPos.rot[2]); ptr.getClass().adjustPosition(ptr, false); } return true; } }; void World::resetActors() { for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { CellStore* cellstore = *iter; ResetActorsVisitor visitor; cellstore->forEach(visitor); } } bool World::isWalkingOnWater(const ConstPtr &actor) { const MWPhysics::Actor* physicActor = mPhysics->getActor(actor); if (physicActor && physicActor->isWalkingOnWater()) return true; return false; } osg::Vec3f World::aimToTarget(const ConstPtr &actor, const MWWorld::ConstPtr& target) { osg::Vec3f weaponPos = getActorHeadTransform(actor).getTrans(); osg::Vec3f targetPos = mPhysics->getPosition(target); return (targetPos - weaponPos); } float World::getHitDistance(const ConstPtr &actor, const ConstPtr &target) { osg::Vec3f weaponPos = getActorHeadTransform(actor).getTrans(); return mPhysics->getHitDistance(weaponPos, target); } } openmw-openmw-0.38.0/apps/openmw/mwworld/worldimp.hpp000066400000000000000000000665551264522266000227510ustar00rootroot00000000000000#ifndef GAME_MWWORLD_WORLDIMP_H #define GAME_MWWORLD_WORLDIMP_H #include #include #include "ptr.hpp" #include "scene.hpp" #include "esmstore.hpp" #include "cells.hpp" #include "localscripts.hpp" #include "timestamp.hpp" #include "fallback.hpp" #include "globals.hpp" #include "../mwbase/world.hpp" #include "contentloader.hpp" #include namespace osg { class Group; } namespace osgViewer { class Viewer; } namespace Resource { class ResourceSystem; } namespace ESM { struct Position; } namespace Files { class Collections; } namespace MWRender { class SkyManager; class Animation; class Camera; } namespace ToUTF8 { class Utf8Encoder; } struct ContentLoader; namespace MWWorld { class WeatherManager; class Player; class ProjectileManager; /// \brief The game world and its visual representation class World : public MWBase::World { Resource::ResourceSystem* mResourceSystem; MWWorld::Fallback mFallback; MWRender::RenderingManager* mRendering; MWWorld::WeatherManager* mWeatherManager; MWWorld::Scene *mWorldScene; MWWorld::Player *mPlayer; std::vector mEsm; MWWorld::ESMStore mStore; LocalScripts mLocalScripts; MWWorld::Globals mGlobalVariables; MWPhysics::PhysicsSystem *mPhysics; bool mSky; ESM::Variant* mGameHour; ESM::Variant* mDaysPassed; ESM::Variant* mDay; ESM::Variant* mMonth; ESM::Variant* mYear; ESM::Variant* mTimeScale; Cells mCells; std::string mCurrentWorldSpace; boost::shared_ptr mProjectileManager; bool mGodMode; bool mScriptsEnabled; std::vector mContentFiles; // not implemented World (const World&); World& operator= (const World&); int mActivationDistanceOverride; std::string mStartupScript; std::map mDoorStates; ///< only holds doors that are currently moving. 1 = opening, 2 = closing std::string mStartCell; void updateWeather(float duration, bool paused = false); int getDaysPerMonth (int month) const; void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, bool adjust); Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return an updated Ptr in case the Ptr's cell changes Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); void updateSoundListener(); void updateWindowManager (); void updatePlayer(bool paused); MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true); public: // FIXME void removeContainerScripts(const Ptr& reference); private: void addContainerScripts(const Ptr& reference, CellStore* cell); void PCDropped (const Ptr& item); void processDoors(float duration); ///< Run physics simulation and modify \a world accordingly. void doPhysics(float duration); ///< Run physics simulation and modify \a world accordingly. void ensureNeededRecords(); void fillGlobalVariables(); /** * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) * @param fileCollections- Container which holds content file names and their paths * @param content - Container which holds content file names * @param contentLoader - */ void loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, ContentLoader& contentLoader); float mSwimHeightScale; bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const; ///< helper function for implementing isSwimming(), isSubmerged(), isWading() bool mTeleportEnabled; bool mLevitationEnabled; bool mGoToJail; int mDaysInPrison; float feetToGameUnits(float feet); MWWorld::ConstPtr getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id ); MWWorld::ConstPtr getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ); public: World ( osgViewer::Viewer* viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem, const Files::Collections& fileCollections, const std::vector& contentFiles, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int activationDistanceOverride, const std::string& startCell, const std::string& startupScript, const std::string& resourcePath); virtual ~World(); virtual void startNewGame (bool bypass); ///< \param bypass Bypass regular game start. virtual void clear(); virtual int countSavedGameRecords() const; virtual int countSavedGameCells() const; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; virtual void readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap); virtual CellStore *getExterior (int x, int y); virtual CellStore *getInterior (const std::string& name); virtual CellStore *getCell (const ESM::CellId& id); //switch to POV before showing player's death animation virtual void useDeathCamera(); virtual void setWaterHeight(const float height); virtual bool toggleWater(); virtual bool toggleWorld(); virtual void adjustSky(); virtual const Fallback *getFallback() const; virtual Player& getPlayer(); virtual MWWorld::Ptr getPlayerPtr(); virtual const MWWorld::ESMStore& getStore() const; virtual std::vector& getEsmReader(); virtual LocalScripts& getLocalScripts(); virtual bool hasCellChanged() const; ///< Has the set of active cells changed, since the last frame? virtual bool isCellExterior() const; virtual bool isCellQuasiExterior() const; virtual osg::Vec2f getNorthVector (CellStore* cell); ///< get north vector for given interior cell virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out); ///< get a list of teleport door markers for a given cell, to be displayed on the local map virtual void setGlobalInt (const std::string& name, int value); ///< Set value independently from real type. virtual void setGlobalFloat (const std::string& name, float value); ///< Set value independently from real type. virtual int getGlobalInt (const std::string& name) const; ///< Get value independently from real type. virtual float getGlobalFloat (const std::string& name) const; ///< Get value independently from real type. virtual char getGlobalVariableType (const std::string& name) const; ///< Return ' ', if there is no global variable with this name. virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const; ///< Return name of the cell. /// /// \note If cell==0, the cell the player is currently in will be used instead to /// generate a name. virtual void removeRefScript (MWWorld::RefData *ref); //< Remove the script attached to ref from mLocalScripts virtual Ptr getPtr (const std::string& name, bool activeOnly); ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. virtual Ptr searchPtr (const std::string& name, bool activeOnly); ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. virtual Ptr searchPtrViaActorId (int actorId); ///< Search is limited to the active cells. virtual MWWorld::Ptr findContainer (const MWWorld::ConstPtr& ptr); ///< Return a pointer to a liveCellRef which contains \a ptr. /// \note Search is limited to the active cells. virtual void adjustPosition (const Ptr& ptr, bool force); ///< Adjust position after load to be on ground. Must be called after model load. /// @param force do this even if the ptr is flying virtual void fixPosition (const Ptr& actor); ///< Attempt to fix position so that the Ptr is no longer inside collision geometry. virtual void enable (const Ptr& ptr); virtual void disable (const Ptr& ptr); virtual void advanceTime (double hours, bool incremental = false); ///< Advance in-game time. virtual void setHour (double hour); ///< Set in-game time hour. virtual void setMonth (int month); ///< Set in-game time month. virtual void setDay (int day); ///< Set in-game time day. virtual int getDay() const; virtual int getMonth() const; virtual int getYear() const; virtual std::string getMonthName (int month = -1) const; ///< Return name of month (-1: current month) virtual TimeStamp getTimeStamp() const; ///< Return current in-game time stamp. virtual bool toggleSky(); ///< \return Resulting mode virtual void changeWeather (const std::string& region, unsigned int id); virtual int getCurrentWeather() const; virtual int getMasserPhase() const; virtual int getSecundaPhase() const; virtual void setMoonColour (bool red); virtual void modRegion(const std::string ®ionid, const std::vector &chances); virtual float getTimeScaleFactor() const; virtual void changeToInteriorCell (const std::string& cellName, const ESM::Position& position); ///< Move to interior cell. virtual void changeToExteriorCell (const ESM::Position& position); ///< Move to exterior cell. virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true); ///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes virtual const ESM::Cell *getExterior (const std::string& cellName) const; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. virtual void markCellAsUnchanged(); virtual MWWorld::Ptr getFacedObject(); ///< Return pointer to the object the player is looking at, if it is within activation range /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to /// use the "Head" node as a basis. virtual std::pair getHitContact(const MWWorld::ConstPtr &ptr, float distance); /// @note No-op for items in containers. Use ContainerStore::removeItem instead. virtual void deleteObject (const Ptr& ptr); virtual void undeleteObject (const Ptr& ptr); virtual MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z); ///< @return an updated Ptr in case the Ptr's cell changes virtual MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z); ///< @return an updated Ptr virtual void scaleObject (const Ptr& ptr, float scale); /// World rotates object, uses radians /// @note Rotations via this method use a different rotation order than the initial rotations in the CS. This /// could be considered a bug, but is needed for MW compatibility. /// \param adjust indicates rotation should be set or adjusted virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); virtual MWWorld::Ptr safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos); ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. virtual float getMaxActivationDistance(); virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; ///< Convert cell numbers to position. virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const; ///< Convert position to cell numbers virtual void queueMovement(const Ptr &ptr, const osg::Vec3f &velocity); ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2); ///< cast a Ray and return true if there is an object in the ray path. virtual bool toggleCollisionMode(); ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. ///< \return Resulting mode virtual bool toggleRenderMode (MWRender::RenderMode mode); ///< Toggle a render mode. ///< \return Resulting mode virtual const ESM::Potion *createRecord (const ESM::Potion& record); ///< Create a new record (of type potion) in the ESM store. /// \return pointer to created record virtual const ESM::Spell *createRecord (const ESM::Spell& record); ///< Create a new record (of type spell) in the ESM store. /// \return pointer to created record virtual const ESM::Class *createRecord (const ESM::Class& record); ///< Create a new record (of type class) in the ESM store. /// \return pointer to created record virtual const ESM::Cell *createRecord (const ESM::Cell& record); ///< Create a new record (of type cell) in the ESM store. /// \return pointer to created record virtual const ESM::NPC *createRecord(const ESM::NPC &record); ///< Create a new record (of type npc) in the ESM store. /// \return pointer to created record virtual const ESM::Armor *createRecord (const ESM::Armor& record); ///< Create a new record (of type armor) in the ESM store. /// \return pointer to created record virtual const ESM::Weapon *createRecord (const ESM::Weapon& record); ///< Create a new record (of type weapon) in the ESM store. /// \return pointer to created record virtual const ESM::Clothing *createRecord (const ESM::Clothing& record); ///< Create a new record (of type clothing) in the ESM store. /// \return pointer to created record virtual const ESM::Enchantment *createRecord (const ESM::Enchantment& record); ///< Create a new record (of type enchantment) in the ESM store. /// \return pointer to created record virtual const ESM::Book *createRecord (const ESM::Book& record); ///< Create a new record (of type book) in the ESM store. /// \return pointer to created record virtual const ESM::CreatureLevList *createOverrideRecord (const ESM::CreatureLevList& record); ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record virtual const ESM::ItemLevList *createOverrideRecord (const ESM::ItemLevList& record); ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record virtual void update (float duration, bool paused); virtual MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount); ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @param number of objects to place virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount); ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position /// @param object /// @param number of objects to place virtual bool canPlaceObject(float cursorX, float cursorY); ///< @return true if it is possible to place on object at specified cursor location virtual void processChangedSettings(const Settings::CategorySettingVector& settings); virtual bool isFlying(const MWWorld::Ptr &ptr) const; virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const; ///Is the head of the creature underwater? virtual bool isSubmerged(const MWWorld::ConstPtr &object) const; virtual bool isSwimming(const MWWorld::ConstPtr &object) const; virtual bool isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const; virtual bool isWading(const MWWorld::ConstPtr &object) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const; virtual void togglePOV(); virtual bool isFirstPerson() const; virtual void togglePreviewMode(bool enable); virtual bool toggleVanityMode(bool enable); virtual void allowVanityMode(bool allow); virtual void togglePlayerLooking(bool enable); virtual void changeVanityModeScale(float factor); virtual bool vanityRotateCamera(float * rot); virtual void setCameraDistance(float dist, bool adjust = false, bool override = true); virtual void setupPlayer(); virtual void renderPlayer(); /// open or close a non-teleport door (depending on current state) virtual void activateDoor(const MWWorld::Ptr& door); /// update movement state of a non-teleport door as specified /// @param state see MWClass::setDoorState /// @note throws an exception when invoked on a teleport door virtual void activateDoor(const MWWorld::Ptr& door, int state); virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object); ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::ConstPtr& object); ///< @return true if any actor is standing on \a object virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object); ///< @return true if the player is colliding with \a object virtual bool getActorCollidingWith (const MWWorld::ConstPtr& object); ///< @return true if any actor is colliding with \a object virtual void hurtStandingActors (const MWWorld::ConstPtr& object, float dmgPerSecond); ///< Apply a health difference to any actors standing on \a object. /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. virtual void hurtCollidingActors (const MWWorld::ConstPtr& object, float dmgPerSecond); ///< Apply a health difference to any actors colliding with \a object. /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. virtual float getWindSpeed(); virtual void getContainersOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out); ///< get all containers in active cells owned by this Npc virtual void getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out); ///< get all items in active cells owned by this Npc virtual bool getLOS(const MWWorld::ConstPtr& actor,const MWWorld::ConstPtr& targetActor); ///< get Line of Sight (morrowind stupid implementation) virtual float getDistToNearestRayHit(const osg::Vec3f& from, const osg::Vec3f& dir, float maxDist); virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable); virtual int canRest(); ///< check if the player is allowed to rest \n /// 0 - yes \n /// 1 - only waiting \n /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr); virtual const MWRender::Animation* getAnimation(const MWWorld::ConstPtr &ptr) const; virtual void reattachPlayerCamera(); /// \todo this does not belong here virtual void screenshot (osg::Image* image, int w, int h); /// Find center of exterior cell above land surface /// \return false if exterior with given name not exists, true otherwise virtual bool findExteriorPosition(const std::string &name, ESM::Position &pos); /// Find position in interior cell near door entrance /// \return false if interior with given name not exists, true otherwise virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos); /// Enables or disables use of teleport spell effects (recall, intervention, etc). virtual void enableTeleporting(bool enable); /// Returns true if teleport spell effects are allowed. virtual bool isTeleportingEnabled() const; /// Enables or disables use of levitation spell effect. virtual void enableLevitation(bool enable); /// Returns true if levitation spell effect is allowed. virtual bool isLevitationEnabled() const; virtual bool getGodModeState(); virtual bool toggleGodMode(); virtual bool toggleScripts(); virtual bool getScriptsEnabled() const; /** * @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met. * @param actor * @return true if the spell can be casted (i.e. the animation should start) */ virtual bool startSpellCast (const MWWorld::Ptr& actor); /** * @brief Cast the actual spell, should be called mid-animation * @param actor */ virtual void castSpell (const MWWorld::Ptr& actor); virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, float speed, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection); virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength); virtual const std::vector& getContentFiles() const; virtual void breakInvisibility (const MWWorld::Ptr& actor); // Are we in an exterior or pseudo-exterior cell and it's night? virtual bool isDark() const; virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, osg::Vec3f& result); /// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id); /// List all references (filtered by \a type) detected by \a ptr. The range /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. /// @note This also works for references in containers. virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, DetectionType type); /// Update the value of some globals according to the world state, which may be used by dialogue entries. /// This should be called when initiating a dialogue. virtual void updateDialogueGlobals(); /// Moves all stolen items from \a ptr to the closest evidence chest. virtual void confiscateStolenItems(const MWWorld::Ptr& ptr); virtual void goToJail (); /// Spawn a random creature from a levelled list next to the player virtual void spawnRandomCreature(const std::string& creatureList); /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition); virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos); virtual void explodeSpell (const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName); virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor); /// @see MWWorld::WeatherManager::isInStorm virtual bool isInStorm() const; /// @see MWWorld::WeatherManager::getStormDirection virtual osg::Vec3f getStormDirection() const; /// Resets all actors in the current active cells to their original location within that cell. virtual void resetActors(); virtual bool isWalkingOnWater (const MWWorld::ConstPtr& actor); /// Return a vector aiming the actor's weapon towards a target. /// @note The length of the vector is the distance between actor and target. virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target); /// Return the distance between actor's weapon and target's collision box. virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target); }; } #endif openmw-openmw-0.38.0/apps/openmw_test_suite/000077500000000000000000000000001264522266000211375ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw_test_suite/CMakeLists.txt000066400000000000000000000013411264522266000236760ustar00rootroot00000000000000find_package(GTest REQUIRED) if (GTEST_FOUND) include_directories(${GTEST_INCLUDE_DIRS}) file(GLOB UNITTEST_SRC_FILES ../openmw/mwworld/store.cpp ../openmw/mwworld/esmstore.cpp mwworld/test_store.cpp mwdialogue/test_keywordsearch.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) target_link_libraries(openmw_test_suite ${GTEST_BOTH_LIBRARIES} components) # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw_test_suite ${CMAKE_THREAD_LIBS_INIT}) endif() endif() openmw-openmw-0.38.0/apps/openmw_test_suite/mwdialogue/000077500000000000000000000000001264522266000232745ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp000066400000000000000000000042431264522266000277140ustar00rootroot00000000000000#include #include "apps/openmw/mwdialogue/keywordsearch.hpp" struct KeywordSearchTest : public ::testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(KeywordSearchTest, keyword_test_conflict_resolution) { // test to make sure the longest keyword in a chain of conflicting keywords gets chosen MWDialogue::KeywordSearch search; search.seed("foo bar", 0); search.seed("bar lock", 0); search.seed("lock switch", 0); std::string text = "foo bar lock switch"; std::vector::Match> matches; search.highlightKeywords(text.begin(), text.end(), matches); // Should contain: "foo bar", "lock switch" ASSERT_TRUE (matches.size() == 2); ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == "foo bar"); ASSERT_TRUE (std::string(matches.rbegin()->mBeg, matches.rbegin()->mEnd) == "lock switch"); } TEST_F(KeywordSearchTest, keyword_test_conflict_resolution2) { MWDialogue::KeywordSearch search; search.seed("the dwemer", 0); search.seed("dwemer language", 0); std::string text = "the dwemer language"; std::vector::Match> matches; search.highlightKeywords(text.begin(), text.end(), matches); ASSERT_TRUE (matches.size() == 1); ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == "dwemer language"); } TEST_F(KeywordSearchTest, keyword_test_conflict_resolution3) { // testing that the longest keyword is chosen, rather than maximizing the // amount of highlighted characters by highlighting the first and last keyword MWDialogue::KeywordSearch search; search.seed("foo bar", 0); search.seed("bar lock", 0); search.seed("lock so", 0); std::string text = "foo bar lock so"; std::vector::Match> matches; search.highlightKeywords(text.begin(), text.end(), matches); ASSERT_TRUE (matches.size() == 1); ASSERT_TRUE (std::string(matches.front().mBeg, matches.front().mEnd) == "bar lock"); } openmw-openmw-0.38.0/apps/openmw_test_suite/mwworld/000077500000000000000000000000001264522266000226325ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/openmw_test_suite/mwworld/test_store.cpp000066400000000000000000000240341264522266000255340ustar00rootroot00000000000000#include #include #include #include #include #include #include "apps/openmw/mwworld/esmstore.hpp" static Loading::Listener dummyListener; /// Base class for tests of ESMStore that rely on external content files to produce the test results struct ContentFileTest : public ::testing::Test { protected: virtual void SetUp() { readContentFiles(); // load the content files std::vector readerList; readerList.resize(mContentFiles.size()); int index=0; for (std::vector::const_iterator it = mContentFiles.begin(); it != mContentFiles.end(); ++it) { ESM::ESMReader lEsm; lEsm.setEncoder(NULL); lEsm.setIndex(index); lEsm.setGlobalReaderList(&readerList); lEsm.open(it->string()); readerList[index] = lEsm; mEsmStore.load(readerList[index], &dummyListener); ++index; } mEsmStore.setUp(); } virtual void TearDown() { } // read absolute path to content files from openmw.cfg void readContentFiles() { boost::program_options::variables_map variables; boost::program_options::options_description desc("Allowed options"); desc.add_options() ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()->composing()) ("content", boost::program_options::value >()->default_value(std::vector(), "") ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") ("data-local", boost::program_options::value()->default_value("")); boost::program_options::notify(variables); mConfigurationManager.readConfiguration(variables, desc, true); Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { dataDirs = Files::PathContainer(variables["data"].as()); } std::string local = variables["data-local"].as(); if (!local.empty()) { dataLocal.push_back(Files::PathContainer::value_type(local)); } mConfigurationManager.processPaths (dataDirs); mConfigurationManager.processPaths (dataLocal, true); if (!dataLocal.empty()) dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); Files::Collections collections (dataDirs, true); std::vector contentFiles = variables["content"].as >(); for (std::vector::iterator it = contentFiles.begin(); it != contentFiles.end(); ++it) mContentFiles.push_back(collections.getPath(*it)); } protected: Files::ConfigurationManager mConfigurationManager; MWWorld::ESMStore mEsmStore; std::vector mContentFiles; }; /// Print results of the dialogue merging process, i.e. the resulting linked list. TEST_F(ContentFileTest, dialogue_merging_test) { if (mContentFiles.empty()) { std::cout << "No content files found, skipping test" << std::endl; return; } const std::string file = "test_dialogue_merging.txt"; boost::filesystem::ofstream stream; stream.open(file); const MWWorld::Store& dialStore = mEsmStore.get(); for (MWWorld::Store::iterator it = dialStore.begin(); it != dialStore.end(); ++it) { const ESM::Dialogue& dial = *it; stream << "Dialogue: " << dial.mId << std::endl; for (ESM::Dialogue::InfoContainer::const_iterator infoIt = dial.mInfo.begin(); infoIt != dial.mInfo.end(); ++infoIt) { const ESM::DialInfo& info = *infoIt; stream << info.mId << std::endl; } stream << std::endl; } std::cout << "dialogue_merging_test successful, results printed to " << file << std::endl; } // Note: here we don't test records that don't use string names (e.g. Land, Pathgrid, Cell) #define RUN_TEST_FOR_TYPES(func, arg1, arg2) \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); \ func(arg1, arg2); template void printRecords(MWWorld::ESMStore& esmStore, std::ostream& outStream) { const MWWorld::Store& store = esmStore.get(); outStream << store.getSize() << " " << T::getRecordType() << " records" << std::endl; for (typename MWWorld::Store::iterator it = store.begin(); it != store.end(); ++it) { const T& record = *it; outStream << record.mId << std::endl; } outStream << std::endl; } /// Print some basic diagnostics about the loaded content files, e.g. number of records and names of those records /// Also used to test the iteration order of records TEST_F(ContentFileTest, content_diagnostics_test) { if (mContentFiles.empty()) { std::cout << "No content files found, skipping test" << std::endl; return; } const std::string file = "test_content_diagnostics.txt"; boost::filesystem::ofstream stream; stream.open(file); RUN_TEST_FOR_TYPES(printRecords, mEsmStore, stream); std::cout << "diagnostics_test successful, results printed to " << file << std::endl; } // TODO: /// Print results of autocalculated NPC spell lists. Also serves as test for attribute/skill autocalculation which the spell autocalculation heavily relies on /// - even incorrect rounding modes can completely change the resulting spell lists. /* TEST_F(ContentFileTest, autocalc_test) { if (mContentFiles.empty()) { std::cout << "No content files found, skipping test" << std::endl; return; } } */ /// Base class for tests of ESMStore that do not rely on external content files struct StoreTest : public ::testing::Test { protected: MWWorld::ESMStore mEsmStore; }; /// Create an ESM file in-memory containing the specified record. /// @param deleted Write record with deleted flag? template Files::IStreamPtr getEsmFile(T record, bool deleted) { ESM::ESMWriter writer; std::stringstream* stream = new std::stringstream; writer.setFormat(0); writer.save(*stream); writer.startRecord(T::sRecordId); record.save(writer, deleted); writer.endRecord(T::sRecordId); return Files::IStreamPtr(stream); } /// Tests deletion of records. TEST_F(StoreTest, delete_test) { const std::string recordId = "foobar"; typedef ESM::Apparatus RecordType; RecordType record; record.blank(); record.mId = recordId; ESM::ESMReader reader; std::vector readerList; readerList.push_back(reader); reader.setGlobalReaderList(&readerList); // master file inserts a record Files::IStreamPtr file = getEsmFile(record, false); reader.open(file, "filename"); mEsmStore.load(reader, &dummyListener); mEsmStore.setUp(); ASSERT_TRUE (mEsmStore.get().getSize() == 1); // now a plugin deletes it file = getEsmFile(record, true); reader.open(file, "filename"); mEsmStore.load(reader, &dummyListener); mEsmStore.setUp(); ASSERT_TRUE (mEsmStore.get().getSize() == 0); // now another plugin inserts it again // expected behaviour is the record to reappear rather than staying deleted file = getEsmFile(record, false); reader.open(file, "filename"); mEsmStore.load(reader, &dummyListener); mEsmStore.setUp(); ASSERT_TRUE (mEsmStore.get().getSize() == 1); } /// Tests overwriting of records. TEST_F(StoreTest, overwrite_test) { const std::string recordId = "foobar"; const std::string recordIdUpper = "Foobar"; typedef ESM::Apparatus RecordType; RecordType record; record.blank(); record.mId = recordId; ESM::ESMReader reader; std::vector readerList; readerList.push_back(reader); reader.setGlobalReaderList(&readerList); // master file inserts a record Files::IStreamPtr file = getEsmFile(record, false); reader.open(file, "filename"); mEsmStore.load(reader, &dummyListener); mEsmStore.setUp(); // now a plugin overwrites it with changed data record.mId = recordIdUpper; // change id to uppercase, to test case smashing while we're at it record.mModel = "the_new_model"; file = getEsmFile(record, false); reader.open(file, "filename"); mEsmStore.load(reader, &dummyListener); mEsmStore.setUp(); // verify that changes were actually applied const RecordType* overwrittenRec = mEsmStore.get().search(recordId); ASSERT_TRUE (overwrittenRec != NULL); ASSERT_TRUE (overwrittenRec && overwrittenRec->mModel == "the_new_model"); } openmw-openmw-0.38.0/apps/openmw_test_suite/openmw_test_suite.cpp000066400000000000000000000002171264522266000254200ustar00rootroot00000000000000#include GTEST_API_ int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } openmw-openmw-0.38.0/apps/wizard/000077500000000000000000000000001264522266000166625ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/wizard/CMakeLists.txt000066400000000000000000000077551264522266000214400ustar00rootroot00000000000000if (WIN32) # windows users can just run the morrowind installer set(OPENMW_USE_UNSHIELD FALSE) else() set(OPENMW_USE_UNSHIELD TRUE) find_package(LIBUNSHIELD REQUIRED) if(NOT LIBUNSHIELD_FOUND) message(FATAL_ERROR "Failed to find Unshield library") endif(NOT LIBUNSHIELD_FOUND) endif() set(WIZARD componentselectionpage.cpp conclusionpage.cpp existinginstallationpage.cpp importpage.cpp inisettings.cpp installationtargetpage.cpp intropage.cpp languageselectionpage.cpp main.cpp mainwizard.cpp methodselectionpage.cpp utils/componentlistwidget.cpp ) if(WIN32) list(APPEND WIZARD ${CMAKE_SOURCE_DIR}/files/windows/openmw-wizard.rc) endif() set(WIZARD_HEADER componentselectionpage.hpp conclusionpage.hpp existinginstallationpage.hpp importpage.hpp inisettings.hpp installationtargetpage.hpp intropage.hpp languageselectionpage.hpp mainwizard.hpp methodselectionpage.hpp utils/componentlistwidget.hpp ) # Headers that must be pre-processed set(WIZARD_HEADER_MOC componentselectionpage.hpp conclusionpage.hpp existinginstallationpage.hpp importpage.hpp installationtargetpage.hpp intropage.hpp languageselectionpage.hpp mainwizard.hpp methodselectionpage.hpp utils/componentlistwidget.hpp ) set(WIZARD_UI ${CMAKE_SOURCE_DIR}/files/ui/wizard/componentselectionpage.ui ${CMAKE_SOURCE_DIR}/files/ui/wizard/conclusionpage.ui ${CMAKE_SOURCE_DIR}/files/ui/wizard/existinginstallationpage.ui ${CMAKE_SOURCE_DIR}/files/ui/wizard/importpage.ui ${CMAKE_SOURCE_DIR}/files/ui/wizard/installationtargetpage.ui ${CMAKE_SOURCE_DIR}/files/ui/wizard/intropage.ui ${CMAKE_SOURCE_DIR}/files/ui/wizard/languageselectionpage.ui ${CMAKE_SOURCE_DIR}/files/ui/wizard/methodselectionpage.ui ) if (OPENMW_USE_UNSHIELD) set (WIZARD ${WIZARD} installationpage.cpp unshield/unshieldworker.cpp) set (WIZARD_HEADER ${WIZARD_HEADER} installationpage.hpp unshield/unshieldworker.hpp) set (WIZARD_HEADER_MOC ${WIZARD_HEADER_MOC} installationpage.hpp unshield/unshieldworker.hpp) set (WIZARD_UI ${WIZARD_UI} ${CMAKE_SOURCE_DIR}/files/ui/wizard/installationpage.ui) add_definitions(-DOPENMW_USE_UNSHIELD) endif (OPENMW_USE_UNSHIELD) source_group(wizard FILES ${WIZARD} ${WIZARD_HEADER}) set(QT_USE_QTGUI 1) # Set some platform specific settings if(WIN32) set(GUI_TYPE WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) if (DESIRED_QT_VERSION MATCHES 4) include(${QT_USE_FILE}) QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc) QT4_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC}) QT4_WRAP_UI(UI_HDRS ${WIZARD_UI}) else() QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc) QT5_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC}) QT5_WRAP_UI(UI_HDRS ${WIZARD_UI}) endif() include_directories(${CMAKE_CURRENT_BINARY_DIR}) if (OPENMW_USE_UNSHIELD) include_directories(${LIBUNSHIELD_INCLUDE_DIR}) endif() add_executable(openmw-wizard ${GUI_TYPE} ${WIZARD} ${WIZARD_HEADER} ${RCC_SRCS} ${MOC_SRCS} ${UI_HDRS} ) target_link_libraries(openmw-wizard components ) if (DESIRED_QT_VERSION MATCHES 4) target_link_libraries(openmw-wizard ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY}) if (WIN32) target_link_libraries(openmw-wizard ${QT_QTMAIN_LIBRARY}) endif() else() qt5_use_modules(openmw-wizard Widgets Core) if (WIN32) target_link_libraries(Qt5::WinMain) endif() endif() if (OPENMW_USE_UNSHIELD) target_link_libraries(openmw-wizard ${LIBUNSHIELD_LIBRARY}) endif() if(DPKG_PROGRAM) INSTALL(TARGETS openmw-wizard RUNTIME DESTINATION games COMPONENT openmw-wizard) endif() if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw-wizard gcov) endif() # Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream if (UNIX AND NOT APPLE) target_link_libraries(openmw-wizard dl Xt) endif() openmw-openmw-0.38.0/apps/wizard/componentselectionpage.cpp000066400000000000000000000137661264522266000241500ustar00rootroot00000000000000#include "componentselectionpage.hpp" #include #include #include #include #include "mainwizard.hpp" Wizard::ComponentSelectionPage::ComponentSelectionPage(QWidget *parent) : QWizardPage(parent) { mWizard = qobject_cast(parent); setupUi(this); setCommitPage(true); setButtonText(QWizard::CommitButton, tr("&Install")); registerField(QLatin1String("installation.components"), componentsList); connect(componentsList, SIGNAL(itemChanged(QListWidgetItem *)), this, SLOT(updateButton(QListWidgetItem *))); } void Wizard::ComponentSelectionPage::updateButton(QListWidgetItem *item) { if (field(QLatin1String("installation.new")).toBool() == true) return; // Morrowind is always checked here bool unchecked = true; for (int i =0; i < componentsList->count(); ++i) { QListWidgetItem *item = componentsList->item(i); if (!item) continue; if (item->checkState() == Qt::Checked) { unchecked = false; } } if (unchecked) { setCommitPage(false); setButtonText(QWizard::NextButton, tr("&Skip")); } else { setCommitPage(true); } } void Wizard::ComponentSelectionPage::initializePage() { componentsList->clear(); QString path(field(QLatin1String("installation.path")).toString()); QListWidgetItem *morrowindItem = new QListWidgetItem(QLatin1String("Morrowind")); QListWidgetItem *tribunalItem = new QListWidgetItem(QLatin1String("Tribunal")); QListWidgetItem *bloodmoonItem = new QListWidgetItem(QLatin1String("Bloodmoon")); if (field(QLatin1String("installation.new")).toBool() == true) { morrowindItem->setFlags((morrowindItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable); morrowindItem->setData(Qt::CheckStateRole, Qt::Checked); componentsList->addItem(morrowindItem); tribunalItem->setFlags(tribunalItem->flags() | Qt::ItemIsUserCheckable); tribunalItem->setData(Qt::CheckStateRole, Qt::Checked); componentsList->addItem(tribunalItem); bloodmoonItem->setFlags(bloodmoonItem->flags() | Qt::ItemIsUserCheckable); bloodmoonItem->setData(Qt::CheckStateRole, Qt::Checked); componentsList->addItem(bloodmoonItem); } else { if (mWizard->mInstallations[path].hasMorrowind) { morrowindItem->setText(tr("Morrowind\t\t(installed)")); morrowindItem->setFlags((morrowindItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable); morrowindItem->setData(Qt::CheckStateRole, Qt::Unchecked); } else { morrowindItem->setText(tr("Morrowind")); morrowindItem->setData(Qt::CheckStateRole, Qt::Checked); } componentsList->addItem(morrowindItem); if (mWizard->mInstallations[path].hasTribunal) { tribunalItem->setText(tr("Tribunal\t\t(installed)")); tribunalItem->setFlags((tribunalItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable); tribunalItem->setData(Qt::CheckStateRole, Qt::Unchecked); } else { tribunalItem->setText(tr("Tribunal")); tribunalItem->setData(Qt::CheckStateRole, Qt::Checked); } componentsList->addItem(tribunalItem); if (mWizard->mInstallations[path].hasBloodmoon) { bloodmoonItem->setText(tr("Bloodmoon\t\t(installed)")); bloodmoonItem->setFlags((bloodmoonItem->flags() & ~Qt::ItemIsEnabled) | Qt::ItemIsUserCheckable); bloodmoonItem->setData(Qt::CheckStateRole, Qt::Unchecked); } else { bloodmoonItem->setText(tr("Bloodmoon")); bloodmoonItem->setData(Qt::CheckStateRole, Qt::Checked); } componentsList->addItem(bloodmoonItem); } } bool Wizard::ComponentSelectionPage::validatePage() { QStringList components(field(QLatin1String("installation.components")).toStringList()); QString path(field(QLatin1String("installation.path")).toString()); // qDebug() << components << path << mWizard->mInstallations[path]; if (field(QLatin1String("installation.new")).toBool() == false) { if (components.contains(QLatin1String("Tribunal")) && !components.contains(QLatin1String("Bloodmoon"))) { if (mWizard->mInstallations[path].hasBloodmoon) { QMessageBox msgBox; msgBox.setWindowTitle(tr("About to install Tribunal after Bloodmoon")); msgBox.setIcon(QMessageBox::Information); msgBox.setStandardButtons(QMessageBox::Cancel); msgBox.setText(tr("

You are about to install Tribunal

\

Bloodmoon is already installed on your computer.

\

However, it is recommended that you install Tribunal before Bloodmoon.

\

Would you like to re-install Bloodmoon?

")); QAbstractButton *reinstallButton = msgBox.addButton(tr("Re-install &Bloodmoon"), QMessageBox::ActionRole); msgBox.exec(); if (msgBox.clickedButton() == reinstallButton) { // Force reinstallation mWizard->mInstallations[path].hasBloodmoon = false; QList items = componentsList->findItems(QLatin1String("Bloodmoon"), Qt::MatchStartsWith); foreach (QListWidgetItem *item, items) { item->setText(QLatin1String("Bloodmoon")); item->setCheckState(Qt::Checked); } return true; } } } } return true; } int Wizard::ComponentSelectionPage::nextId() const { #ifdef OPENMW_USE_UNSHIELD if (isCommitPage()) { return MainWizard::Page_Installation; } else { return MainWizard::Page_Import; } #else return MainWizard::Page_Import; #endif } openmw-openmw-0.38.0/apps/wizard/componentselectionpage.hpp000066400000000000000000000011621264522266000241400ustar00rootroot00000000000000#ifndef COMPONENTSELECTIONPAGE_HPP #define COMPONENTSELECTIONPAGE_HPP #include #include "ui_componentselectionpage.h" namespace Wizard { class MainWizard; class ComponentSelectionPage : public QWizardPage, private Ui::ComponentSelectionPage { Q_OBJECT public: ComponentSelectionPage(QWidget *parent); int nextId() const; virtual bool validatePage(); private slots: void updateButton(QListWidgetItem *item); private: MainWizard *mWizard; protected: void initializePage(); }; } #endif // COMPONENTSELECTIONPAGE_HPP openmw-openmw-0.38.0/apps/wizard/conclusionpage.cpp000066400000000000000000000036701264522266000224050ustar00rootroot00000000000000#include "conclusionpage.hpp" #include #include "mainwizard.hpp" Wizard::ConclusionPage::ConclusionPage(QWidget *parent) : QWizardPage(parent) { mWizard = qobject_cast(parent); setupUi(this); setPixmap(QWizard::WatermarkPixmap, QPixmap(QLatin1String(":/images/intropage-background.png"))); } void Wizard::ConclusionPage::initializePage() { // Write the path to openmw.cfg if (field(QLatin1String("installation.new")).toBool() == true) { QString path(field(QLatin1String("installation.path")).toString()); mWizard->addInstallation(path); } if (!mWizard->mError) { if ((field(QLatin1String("installation.new")).toBool() == true) || (field(QLatin1String("installation.import-settings")).toBool() == true)) { qDebug() << "IMPORT SETTINGS"; mWizard->runSettingsImporter(); } } if (!mWizard->mError) { if (field(QLatin1String("installation.new")).toBool() == true) { textLabel->setText(tr("

The OpenMW Wizard successfully installed Morrowind on your computer.

\

Click Finish to close the Wizard.

")); } else { textLabel->setText(tr("

The OpenMW Wizard successfully modified your existing Morrowind installation.

\

Click Finish to close the Wizard.

")); } } else { textLabel->setText(tr("

The OpenMW Wizard failed to install Morrowind on your computer.

\

Please report any bugs you might have encountered to our \ bug tracker.
Make sure to include the installation log.


")); } } int Wizard::ConclusionPage::nextId() const { return -1; } openmw-openmw-0.38.0/apps/wizard/conclusionpage.hpp000066400000000000000000000007171264522266000224110ustar00rootroot00000000000000#ifndef CONCLUSIONPAGE_HPP #define CONCLUSIONPAGE_HPP #include #include "ui_conclusionpage.h" namespace Wizard { class MainWizard; class ConclusionPage : public QWizardPage, private Ui::ConclusionPage { Q_OBJECT public: ConclusionPage(QWidget *parent); int nextId() const; private: MainWizard *mWizard; protected: void initializePage(); }; } #endif // CONCLUSIONPAGE_HPP openmw-openmw-0.38.0/apps/wizard/existinginstallationpage.cpp000066400000000000000000000114311264522266000244770ustar00rootroot00000000000000#include "existinginstallationpage.hpp" #include #include #include #include #include #include "mainwizard.hpp" Wizard::ExistingInstallationPage::ExistingInstallationPage(QWidget *parent) : QWizardPage(parent) { mWizard = qobject_cast(parent); setupUi(this); // Add a placeholder item to the list of installations QListWidgetItem *emptyItem = new QListWidgetItem(tr("No existing installations detected")); emptyItem->setFlags(Qt::NoItemFlags); installationsList->insertItem(0, emptyItem); } void Wizard::ExistingInstallationPage::initializePage() { // Add the available installation paths QStringList paths(mWizard->mInstallations.keys()); // Hide the default item if there are installations to choose from installationsList->item(0)->setHidden(!paths.isEmpty()); foreach (const QString &path, paths) { QListWidgetItem *item = new QListWidgetItem(path); if (installationsList->findItems(path, Qt::MatchExactly).isEmpty()) installationsList->addItem(item); } connect(installationsList, SIGNAL(currentTextChanged(QString)), this, SLOT(textChanged(QString))); connect(installationsList,SIGNAL(itemSelectionChanged()), this, SIGNAL(completeChanged())); } bool Wizard::ExistingInstallationPage::validatePage() { // See if Morrowind.ini is detected, if not, ask the user // It can be missing entirely // Or failed to be detected due to the target being a symlink QString path(field(QLatin1String("installation.path")).toString()); QFile file(mWizard->mInstallations[path].iniPath); if (!file.exists()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error detecting Morrowind configuration")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Cancel); msgBox.setText(QObject::tr("
Could not find Morrowind.ini

\ The Wizard needs to update settings in this file.

\ Press \"Browse...\" to specify the location manually.
")); QAbstractButton *browseButton = msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole); msgBox.exec(); QString iniFile; if (msgBox.clickedButton() == browseButton) { iniFile = QFileDialog::getOpenFileName( this, QObject::tr("Select configuration file"), QDir::currentPath(), QString(tr("Morrowind configuration file (*.ini)"))); } if (iniFile.isEmpty()) { return false; // Cancel was clicked; } // A proper Morrowind.ini was selected, set it QFileInfo info(iniFile); mWizard->mInstallations[path].iniPath = info.absoluteFilePath(); } return true; } void Wizard::ExistingInstallationPage::on_browseButton_clicked() { QString selectedFile = QFileDialog::getOpenFileName( this, tr("Select master file"), QDir::currentPath(), QString(tr("Morrowind master file (*.esm)")), NULL, QFileDialog::DontResolveSymlinks); if (selectedFile.isEmpty()) return; QFileInfo info(selectedFile); if (!info.exists()) return; if (!mWizard->findFiles(QLatin1String("Morrowind"), info.absolutePath())) return; // No valid Morrowind installation found QString path(QDir::toNativeSeparators(info.absolutePath())); QList items = installationsList->findItems(path, Qt::MatchExactly); if (items.isEmpty()) { // Path is not yet in the list, add it mWizard->addInstallation(path); // Hide the default item installationsList->item(0)->setHidden(true); QListWidgetItem *item = new QListWidgetItem(path); installationsList->addItem(item); installationsList->setCurrentItem(item); // Select it too } else { installationsList->setCurrentItem(items.first()); } // Update the button emit completeChanged(); } void Wizard::ExistingInstallationPage::textChanged(const QString &text) { // Set the installation path manually, as registerField doesn't work // Because it doesn't accept two widgets operating on a single field if (!text.isEmpty()) mWizard->setField(QLatin1String("installation.path"), text); } bool Wizard::ExistingInstallationPage::isComplete() const { if (installationsList->selectionModel()->hasSelection()) { return true; } else { return false; } } int Wizard::ExistingInstallationPage::nextId() const { return MainWizard::Page_LanguageSelection; } openmw-openmw-0.38.0/apps/wizard/existinginstallationpage.hpp000066400000000000000000000013171264522266000245060ustar00rootroot00000000000000#ifndef EXISTINGINSTALLATIONPAGE_HPP #define EXISTINGINSTALLATIONPAGE_HPP #include #include "ui_existinginstallationpage.h" namespace Wizard { class MainWizard; class ExistingInstallationPage : public QWizardPage, private Ui::ExistingInstallationPage { Q_OBJECT public: ExistingInstallationPage(QWidget *parent); int nextId() const; virtual bool isComplete() const; virtual bool validatePage(); private slots: void on_browseButton_clicked(); void textChanged(const QString &text); private: MainWizard *mWizard; protected: void initializePage(); }; } #endif // EXISTINGINSTALLATIONPAGE_HPP openmw-openmw-0.38.0/apps/wizard/importpage.cpp000066400000000000000000000007001264522266000215320ustar00rootroot00000000000000#include "importpage.hpp" #include "mainwizard.hpp" Wizard::ImportPage::ImportPage(QWidget *parent) : QWizardPage(parent) { mWizard = qobject_cast(parent); setupUi(this); registerField(QLatin1String("installation.import-settings"), importCheckBox); registerField(QLatin1String("installation.import-addons"), addonsCheckBox); } int Wizard::ImportPage::nextId() const { return MainWizard::Page_Conclusion; } openmw-openmw-0.38.0/apps/wizard/importpage.hpp000066400000000000000000000006041264522266000215420ustar00rootroot00000000000000#ifndef IMPORTPAGE_HPP #define IMPORTPAGE_HPP #include #include "ui_importpage.h" namespace Wizard { class MainWizard; class ImportPage : public QWizardPage, private Ui::ImportPage { Q_OBJECT public: ImportPage(QWidget *parent); int nextId() const; private: MainWizard *mWizard; }; } #endif // IMPORTPAGE_HPP openmw-openmw-0.38.0/apps/wizard/inisettings.cpp000066400000000000000000000142331264522266000217310ustar00rootroot00000000000000#include "inisettings.hpp" #include #include #include #include #include #include #include Wizard::IniSettings::IniSettings() { } Wizard::IniSettings::~IniSettings() { } QStringList Wizard::IniSettings::findKeys(const QString &text) { QStringList result; foreach (const QString &key, mSettings.keys()) { if (key.startsWith(text)) result << key; } return result; } bool Wizard::IniSettings::readFile(QTextStream &stream) { // Look for a square bracket, "'\\[" // that has one or more "not nothing" in it, "([^]]+)" // and is closed with a square bracket, "\\]" QRegExp sectionRe(QLatin1String("^\\[([^]]+)\\]")); // Find any character(s) that is/are not equal sign(s), "[^=]+" // followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*" // and one or more periods, "(.+)" QRegExp keyRe(QLatin1String("^([^=]+)\\s*=\\s*(.+)$")); QString currentSection; while (!stream.atEnd()) { const QString line(stream.readLine()); if (line.isEmpty() || line.startsWith(QLatin1Char(';'))) continue; if (sectionRe.exactMatch(line)) { currentSection = sectionRe.cap(1); } else if (keyRe.indexIn(line) != -1) { QString key = keyRe.cap(1).trimmed(); QString value = keyRe.cap(2).trimmed(); // Append the section, but only if there is one if (!currentSection.isEmpty()) key = currentSection + QLatin1Char('/') + key; mSettings[key] = QVariant(value); } } return true; } bool Wizard::IniSettings::writeFile(const QString &path, QTextStream &stream) { // Look for a square bracket, "'\\[" // that has one or more "not nothing" in it, "([^]]+)" // and is closed with a square bracket, "\\]" QRegExp sectionRe(QLatin1String("^\\[([^]]+)\\]")); // Find any character(s) that is/are not equal sign(s), "[^=]+" // followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*" // and one or more periods, "(.+)" QRegExp keyRe(QLatin1String("^([^=]+)\\s*=\\s*(.+)$")); const QStringList keys(mSettings.keys()); QString currentSection; QString buffer; while (!stream.atEnd()) { const QString line(stream.readLine()); if (line.isEmpty() || line.startsWith(QLatin1Char(';'))) { buffer.append(line + QLatin1String("\n")); continue; } if (sectionRe.exactMatch(line)) { buffer.append(line + QLatin1String("\n")); currentSection = sectionRe.cap(1); } else if (keyRe.indexIn(line) != -1) { QString key(keyRe.cap(1).trimmed()); QString lookupKey(key); // Append the section, but only if there is one if (!currentSection.isEmpty()) lookupKey = currentSection + QLatin1Char('/') + key; buffer.append(key + QLatin1Char('=') + mSettings[lookupKey].toString() + QLatin1String("\n")); mSettings.remove(lookupKey); } } // Add the new settings to the buffer QHashIterator i(mSettings); while (i.hasNext()) { i.next(); QStringList fullKey(i.key().split(QLatin1Char('/'))); QString section(fullKey.at(0)); section.prepend(QLatin1Char('[')); section.append(QLatin1Char(']')); QString key(fullKey.at(1)); int index = buffer.lastIndexOf(section); if (index != -1) { // Look for the next section index = buffer.indexOf(QLatin1Char('['), index + 1); if (index == -1 ) { // We are at the last section, append it to the bottom of the file buffer.append(QString("\n%1=%2").arg(key, i.value().toString())); mSettings.remove(i.key()); continue; } else { // Not at last section, add the key at the index buffer.insert(index - 1, QString("\n%1=%2").arg(key, i.value().toString())); mSettings.remove(i.key()); } } else { // Add the section to the end of the file, because it's not found buffer.append(QString("\n%1\n").arg(section)); i.previous(); } } // Now we reopen the file, this time we write QFile file(path); if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { QTextStream in(&file); in.setCodec(stream.codec()); // Write the updated buffer to an empty file in << buffer; file.flush(); file.close(); } else { return false; } return true; } bool Wizard::IniSettings::parseInx(const QString &path) { QFile file(path); if (file.open(QIODevice::ReadOnly)) { const QByteArray data(file.readAll()); const QByteArray pattern("\x21\x00\x1A\x01\x04\x00\x04\x97\xFF\x06", 10); int i = 0; while ((i = data.indexOf(pattern, i)) != -1) { int next = data.indexOf(pattern, i + 1); if (next == -1) break; QByteArray array(data.mid(i, (next - i))); // Skip some invalid entries if (array.contains("\x04\x96\xFF")) { ++i; continue; } // Remove the pattern from the beginning array.remove(0, 12); int index = array.indexOf("\x06"); const QString section(array.left(index)); // Figure how many characters to read for the key int lenght = array.indexOf("\x06", section.length() + 3) - (section.length() + 3); const QString key(array.mid(section.length() + 3, lenght)); QString value(array.mid(section.length() + key.length() + 6)); // Add the value setValue(section + QLatin1Char('/') + key, QVariant(value)); ++i; } file.close(); } else { qDebug() << "Failed to open INX file: " << path; return false; } return true; } openmw-openmw-0.38.0/apps/wizard/inisettings.hpp000066400000000000000000000021721264522266000217350ustar00rootroot00000000000000#ifndef INISETTINGS_HPP #define INISETTINGS_HPP #include #include class QTextStream; namespace Wizard { typedef QHash SettingsMap; class IniSettings { public: explicit IniSettings(); ~IniSettings(); inline QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const { return mSettings.value(key, defaultValue); } inline QList values() const { return mSettings.values(); } inline void setValue(const QString &key, const QVariant &value) { mSettings.insert(key, value); } inline void remove(const QString &key) { mSettings.remove(key); } QStringList findKeys(const QString &text); bool readFile(QTextStream &stream); bool writeFile(const QString &path, QTextStream &stream); bool parseInx(const QString &path); private: int getLastNewline(const QString &buffer, int from); SettingsMap mSettings; }; } #endif // INISETTINGS_HPP openmw-openmw-0.38.0/apps/wizard/installationpage.cpp000066400000000000000000000173611264522266000227340ustar00rootroot00000000000000#include "installationpage.hpp" #include #include #include #include #include #include "mainwizard.hpp" #include "inisettings.hpp" Wizard::InstallationPage::InstallationPage(QWidget *parent) : QWizardPage(parent) { mWizard = qobject_cast(parent); setupUi(this); mFinished = false; mThread = new QThread(); mUnshield = new UnshieldWorker(); mUnshield->moveToThread(mThread); connect(mThread, SIGNAL(started()), mUnshield, SLOT(extract())); connect(mUnshield, SIGNAL(finished()), mThread, SLOT(quit())); connect(mUnshield, SIGNAL(finished()), this, SLOT(installationFinished()), Qt::QueuedConnection); connect(mUnshield, SIGNAL(error(QString, QString)), this, SLOT(installationError(QString, QString)), Qt::QueuedConnection); connect(mUnshield, SIGNAL(textChanged(QString)), installProgressLabel, SLOT(setText(QString)), Qt::QueuedConnection); connect(mUnshield, SIGNAL(textChanged(QString)), logTextEdit, SLOT(appendPlainText(QString)), Qt::QueuedConnection); connect(mUnshield, SIGNAL(textChanged(QString)), mWizard, SLOT(addLogText(QString)), Qt::QueuedConnection); connect(mUnshield, SIGNAL(progressChanged(int)), installProgressBar, SLOT(setValue(int)), Qt::QueuedConnection); connect(mUnshield, SIGNAL(requestFileDialog(Wizard::Component)), this, SLOT(showFileDialog(Wizard::Component)), Qt::QueuedConnection); } Wizard::InstallationPage::~InstallationPage() { if (mThread->isRunning()) { mUnshield->stopWorker(); mThread->quit(); mThread->wait(); } delete mUnshield; delete mThread; } void Wizard::InstallationPage::initializePage() { QString path(field(QLatin1String("installation.path")).toString()); QStringList components(field(QLatin1String("installation.components")).toStringList()); logTextEdit->appendPlainText(QString("Installing to %1").arg(path)); logTextEdit->appendPlainText(QString("Installing %1.").arg(components.join(", "))); installProgressBar->setMinimum(0); // Set the progressbar maximum to a multiple of 100 // That way installing all three components would yield 300% // When one component is done the bar will be filled by 33% if (field(QLatin1String("installation.new")).toBool() == true) { installProgressBar->setMaximum((components.count() * 100)); } else { if (components.contains(QLatin1String("Tribunal")) && !mWizard->mInstallations[path].hasTribunal) installProgressBar->setMaximum(100); if (components.contains(QLatin1String("Bloodmoon")) && !mWizard->mInstallations[path].hasBloodmoon) installProgressBar->setMaximum(installProgressBar->maximum() + 100); } startInstallation(); } void Wizard::InstallationPage::startInstallation() { QStringList components(field(QLatin1String("installation.components")).toStringList()); QString path(field(QLatin1String("installation.path")).toString()); if (field(QLatin1String("installation.new")).toBool() == true) { // Always install Morrowind mUnshield->setInstallComponent(Wizard::Component_Morrowind, true); if (components.contains(QLatin1String("Tribunal"))) mUnshield->setInstallComponent(Wizard::Component_Tribunal, true); if (components.contains(QLatin1String("Bloodmoon"))) mUnshield->setInstallComponent(Wizard::Component_Bloodmoon, true); } else { // Morrowind should already be installed mUnshield->setInstallComponent(Wizard::Component_Morrowind, false); if (components.contains(QLatin1String("Tribunal")) && !mWizard->mInstallations[path].hasTribunal) mUnshield->setInstallComponent(Wizard::Component_Tribunal, true); if (components.contains(QLatin1String("Bloodmoon")) && !mWizard->mInstallations[path].hasBloodmoon) mUnshield->setInstallComponent(Wizard::Component_Bloodmoon, true); // Set the location of the Morrowind.ini to update mUnshield->setIniPath(mWizard->mInstallations[path].iniPath); mUnshield->setupSettings(); } // Set the installation target path mUnshield->setPath(path); // Set the right codec to use for Morrowind.ini QString language(field(QLatin1String("installation.language")).toString()); if (language == QLatin1String("Polish")) { mUnshield->setIniCodec(QTextCodec::codecForName("windows-1250")); } else if (language == QLatin1String("Russian")) { mUnshield->setIniCodec(QTextCodec::codecForName("windows-1251")); } else { mUnshield->setIniCodec(QTextCodec::codecForName("windows-1252")); } mThread->start(); } void Wizard::InstallationPage::showFileDialog(Wizard::Component component) { QString name; switch (component) { case Wizard::Component_Morrowind: name = QLatin1String("Morrowind"); break; case Wizard::Component_Tribunal: name = QLatin1String("Tribunal"); break; case Wizard::Component_Bloodmoon: name = QLatin1String("Bloodmoon"); break; } QString path = QFileDialog::getExistingDirectory(this, tr("Select %1 installation media").arg(name), QDir::rootPath()); if (path.isEmpty()) { logTextEdit->appendHtml(tr("


\ Error: The installation was aborted by the user

")); mWizard->addLogText(QLatin1String("Error: The installation was aborted by the user")); mWizard->mError = true; emit completeChanged(); return; } mUnshield->setDiskPath(path); } void Wizard::InstallationPage::installationFinished() { QMessageBox msgBox; msgBox.setWindowTitle(tr("Installation finished")); msgBox.setIcon(QMessageBox::Information); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("Installation completed successfully!")); msgBox.exec(); mFinished = true; emit completeChanged(); } void Wizard::InstallationPage::installationError(const QString &text, const QString &details) { installProgressLabel->setText(tr("Installation failed!")); logTextEdit->appendHtml(tr("


\ Error: %1

").arg(text)); logTextEdit->appendHtml(tr("

\ %1

").arg(details)); mWizard->addLogText(QLatin1String("Error: ") + text); mWizard->addLogText(details); mWizard->mError = true; QMessageBox msgBox; msgBox.setWindowTitle(tr("An error occurred")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

The Wizard has encountered an error

\

The error reported was:

%1

\

Press "Show Details..." for more information.

").arg(text)); msgBox.setDetailedText(details); msgBox.exec(); emit completeChanged(); } bool Wizard::InstallationPage::isComplete() const { if (!mWizard->mError) { return mFinished; } else { return true; } } int Wizard::InstallationPage::nextId() const { if (field(QLatin1String("installation.new")).toBool() == true) { return MainWizard::Page_Conclusion; } else { if (!mWizard->mError) { return MainWizard::Page_Import; } else { return MainWizard::Page_Conclusion; } } } openmw-openmw-0.38.0/apps/wizard/installationpage.hpp000066400000000000000000000017411264522266000227340ustar00rootroot00000000000000#ifndef INSTALLATIONPAGE_HPP #define INSTALLATIONPAGE_HPP #include #include "unshield/unshieldworker.hpp" #include "ui_installationpage.h" #include "inisettings.hpp" class QThread; namespace Wizard { class MainWizard; class IniSettings; class UnshieldWorker; class InstallationPage : public QWizardPage, private Ui::InstallationPage { Q_OBJECT public: InstallationPage(QWidget *parent); ~InstallationPage(); int nextId() const; virtual bool isComplete() const; private: MainWizard *mWizard; bool mFinished; QThread* mThread; UnshieldWorker *mUnshield; void startInstallation(); private slots: void showFileDialog(Wizard::Component component); void installationFinished(); void installationError(const QString &text, const QString &details); protected: void initializePage(); }; } #endif // INSTALLATIONPAGE_HPP openmw-openmw-0.38.0/apps/wizard/installationtargetpage.cpp000066400000000000000000000065531264522266000241440ustar00rootroot00000000000000#include "installationtargetpage.hpp" #include #include #include #include "mainwizard.hpp" Wizard::InstallationTargetPage::InstallationTargetPage(QWidget *parent, const Files::ConfigurationManager &cfg) : QWizardPage(parent), mCfgMgr(cfg) { mWizard = qobject_cast(parent); setupUi(this); registerField(QLatin1String("installation.path*"), targetLineEdit); } void Wizard::InstallationTargetPage::initializePage() { QString path(QFile::decodeName(mCfgMgr.getUserDataPath().string().c_str())); path.append(QDir::separator() + QLatin1String("data")); QDir dir(path); targetLineEdit->setText(QDir::toNativeSeparators(dir.absolutePath())); } bool Wizard::InstallationTargetPage::validatePage() { QString path(field(QLatin1String("installation.path")).toString()); qDebug() << "Validating path: " << path; if (!QFile::exists(path)) { QDir dir; if (!dir.mkpath(path)) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error creating destination")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not create the destination directory

\

Please make sure you have the right permissions \ and try again, or specify a different location.

")); msgBox.exec(); return false; } } QFileInfo info(path); if (!info.isWritable()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Insufficient permissions")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not write to the destination directory

\

Please make sure you have the right permissions \ and try again, or specify a different location.

")); msgBox.exec(); return false; } if (mWizard->findFiles(QLatin1String("Morrowind"), path)) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Destination not empty")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

The destination directory is not empty

\

An existing Morrowind installation is present in the specified location.

\

Please specify a different location, or go back and select the location as an existing installation.

")); msgBox.exec(); return false; } return true; } void Wizard::InstallationTargetPage::on_browseButton_clicked() { QString selectedPath = QFileDialog::getExistingDirectory( this, tr("Select where to install Morrowind"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); qDebug() << selectedPath; QFileInfo info(selectedPath); if (!info.exists()) return; if (info.isWritable()) targetLineEdit->setText(info.absoluteFilePath()); } int Wizard::InstallationTargetPage::nextId() const { return MainWizard::Page_LanguageSelection; } openmw-openmw-0.38.0/apps/wizard/installationtargetpage.hpp000066400000000000000000000013721264522266000241430ustar00rootroot00000000000000#ifndef INSTALLATIONTARGETPAGE_HPP #define INSTALLATIONTARGETPAGE_HPP #include #include "ui_installationtargetpage.h" namespace Files { struct ConfigurationManager; } namespace Wizard { class MainWizard; class InstallationTargetPage : public QWizardPage, private Ui::InstallationTargetPage { Q_OBJECT public: InstallationTargetPage(QWidget *parent, const Files::ConfigurationManager &cfg); int nextId() const; virtual bool validatePage(); private slots: void on_browseButton_clicked(); private: MainWizard *mWizard; const Files::ConfigurationManager &mCfgMgr; protected: void initializePage(); }; } #endif // INSTALLATIONTARGETPAGE_HPP openmw-openmw-0.38.0/apps/wizard/intropage.cpp000066400000000000000000000006041264522266000213560ustar00rootroot00000000000000#include "intropage.hpp" #include "mainwizard.hpp" Wizard::IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent) { mWizard = qobject_cast(parent); setupUi(this); setPixmap(QWizard::WatermarkPixmap, QPixmap(QLatin1String(":/images/intropage-background.png"))); } int Wizard::IntroPage::nextId() const { return MainWizard::Page_MethodSelection; } openmw-openmw-0.38.0/apps/wizard/intropage.hpp000066400000000000000000000005741264522266000213710ustar00rootroot00000000000000#ifndef INTROPAGE_HPP #define INTROPAGE_HPP #include #include "ui_intropage.h" namespace Wizard { class MainWizard; class IntroPage : public QWizardPage, private Ui::IntroPage { Q_OBJECT public: IntroPage(QWidget *parent); int nextId() const; private: MainWizard *mWizard; }; } #endif // INTROPAGE_HPP openmw-openmw-0.38.0/apps/wizard/languageselectionpage.cpp000066400000000000000000000027321264522266000237200ustar00rootroot00000000000000#include "languageselectionpage.hpp" #include "mainwizard.hpp" #include Wizard::LanguageSelectionPage::LanguageSelectionPage(QWidget *parent) : QWizardPage(parent) { mWizard = qobject_cast(parent); setupUi(this); registerField(QLatin1String("installation.language"), languageComboBox); } void Wizard::LanguageSelectionPage::initializePage() { QStringList languages; languages << QLatin1String("English") << QLatin1String("French") << QLatin1String("German") << QLatin1String("Italian") << QLatin1String("Polish") << QLatin1String("Russian") << QLatin1String("Spanish"); languageComboBox->addItems(languages); } int Wizard::LanguageSelectionPage::nextId() const { if (field(QLatin1String("installation.new")).toBool() == true) { return MainWizard::Page_ComponentSelection; } else { QString path(field(QLatin1String("installation.path")).toString()); if (path.isEmpty()) return MainWizard::Page_ComponentSelection; // Check if we have to install something if (mWizard->mInstallations[path].hasMorrowind == true && mWizard->mInstallations[path].hasTribunal == true && mWizard->mInstallations[path].hasBloodmoon == true) { return MainWizard::Page_Import; } else { return MainWizard::Page_ComponentSelection; } } } openmw-openmw-0.38.0/apps/wizard/languageselectionpage.hpp000066400000000000000000000007761264522266000237330ustar00rootroot00000000000000#ifndef LANGUAGESELECTIONPAGE_HPP #define LANGUAGESELECTIONPAGE_HPP #include #include "ui_languageselectionpage.h" namespace Wizard { class MainWizard; class LanguageSelectionPage : public QWizardPage, private Ui::LanguageSelectionPage { Q_OBJECT public: LanguageSelectionPage(QWidget *parent); int nextId() const; private: MainWizard *mWizard; protected: void initializePage(); }; } #endif // LANGUAGESELECTIONPAGE_HPP openmw-openmw-0.38.0/apps/wizard/main.cpp000066400000000000000000000022501264522266000203110ustar00rootroot00000000000000#include #include #include #include #include "mainwizard.hpp" #ifdef MAC_OS_X_VERSION_MIN_REQUIRED #undef MAC_OS_X_VERSION_MIN_REQUIRED // We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154 #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #endif // MAC_OS_X_VERSION_MIN_REQUIRED int main(int argc, char *argv[]) { QApplication app(argc, argv); // Now we make sure the current dir is set to application path QDir dir(QCoreApplication::applicationDirPath()); #ifdef Q_OS_MAC if (dir.dirName() == "MacOS") { dir.cdUp(); dir.cdUp(); dir.cdUp(); } // force Qt to load only LOCAL plugins, don't touch system Qt installation QDir pluginsPath(QCoreApplication::applicationDirPath()); pluginsPath.cdUp(); pluginsPath.cd("Plugins"); QStringList libraryPaths; libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); app.setLibraryPaths(libraryPaths); #endif QDir::setCurrent(dir.absolutePath()); Wizard::MainWizard wizard; wizard.show(); return app.exec(); } openmw-openmw-0.38.0/apps/wizard/mainwizard.cpp000066400000000000000000000366571264522266000215540ustar00rootroot00000000000000#include "mainwizard.hpp" #include #include #include #include #include #include #include #include "intropage.hpp" #include "methodselectionpage.hpp" #include "languageselectionpage.hpp" #include "existinginstallationpage.hpp" #include "installationtargetpage.hpp" #include "componentselectionpage.hpp" #include "importpage.hpp" #include "conclusionpage.hpp" #ifdef OPENMW_USE_UNSHIELD #include "installationpage.hpp" #endif using namespace Process; Wizard::MainWizard::MainWizard(QWidget *parent) : QWizard(parent), mInstallations(), mError(false), mGameSettings(mCfgMgr) { #ifndef Q_OS_MAC setWizardStyle(QWizard::ModernStyle); #else setWizardStyle(QWizard::ClassicStyle); #endif setWindowTitle(tr("OpenMW Wizard")); setWindowIcon(QIcon(QLatin1String(":/images/openmw-wizard.png"))); setMinimumWidth(550); // Set the property for comboboxes to the text instead of index setDefaultProperty("QComboBox", "currentText", "currentIndexChanged"); setDefaultProperty("ComponentListWidget", "mCheckedItems", "checkedItemsChanged"); mImporterInvoker = new ProcessInvoker(); connect(mImporterInvoker->getProcess(), SIGNAL(started()), this, SLOT(importerStarted())); connect(mImporterInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(importerFinished(int,QProcess::ExitStatus))); mLogError = tr("

Could not open %1 for writing

\

Please make sure you have the right permissions \ and try again.

"); setupLog(); setupGameSettings(); setupLauncherSettings(); setupInstallations(); setupPages(); const boost::filesystem::path& installedPath = mCfgMgr.getInstallPath(); if (!installedPath.empty()) { addInstallation(toQString(installedPath)); } } Wizard::MainWizard::~MainWizard() { delete mImporterInvoker; } void Wizard::MainWizard::setupLog() { QString logPath(toQString(mCfgMgr.getLogPath())); logPath.append(QLatin1String("wizard.log")); QFile file(logPath); if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error opening Wizard log file")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(mLogError.arg(file.fileName())); msgBox.exec(); return qApp->quit(); } addLogText(QString("Started OpenMW Wizard on %1").arg(QDateTime::currentDateTime().toString())); qDebug() << logPath; } void Wizard::MainWizard::addLogText(const QString &text) { QString logPath(toQString(mCfgMgr.getLogPath())); logPath.append(QLatin1String("wizard.log")); QFile file(logPath); if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error opening Wizard log file")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(mLogError.arg(file.fileName())); msgBox.exec(); return qApp->quit(); } if (!file.isSequential()) file.seek(file.size()); QTextStream out(&file); if (!text.isEmpty()) out << text << endl; // file.close(); } void Wizard::MainWizard::setupGameSettings() { QString userPath(toQString(mCfgMgr.getUserConfigPath())); QString globalPath(toQString(mCfgMgr.getGlobalPath())); QString message(tr("

Could not open %1 for reading

\

Please make sure you have the right permissions \ and try again.

")); // Load the user config file first, separately // So we can write it properly, uncontaminated QString path(userPath + QLatin1String("openmw.cfg")); QFile file(path); qDebug() << "Loading config file:" << path.toUtf8().constData(); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(message.arg(file.fileName())); msgBox.exec(); return qApp->quit(); } QTextStream stream(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); mGameSettings.readUserFile(stream); } // Now the rest QStringList paths; paths.append(userPath + QLatin1String("openmw.cfg")); paths.append(QLatin1String("openmw.cfg")); paths.append(globalPath + QLatin1String("openmw.cfg")); foreach (const QString &path, paths) { qDebug() << "Loading config file:" << path.toUtf8().constData(); QFile file(path); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(message.arg(file.fileName())); return qApp->quit(); } QTextStream stream(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); mGameSettings.readFile(stream); } file.close(); } } void Wizard::MainWizard::setupLauncherSettings() { QString path(toQString(mCfgMgr.getUserConfigPath())); path.append(QLatin1String(Config::LauncherSettings::sLauncherConfigFileName)); QString message(tr("

Could not open %1 for reading

\

Please make sure you have the right permissions \ and try again.

")); QFile file(path); qDebug() << "Loading config file:" << path.toUtf8().constData(); if (file.exists()) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(message.arg(file.fileName())); msgBox.exec(); return qApp->quit(); } QTextStream stream(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); mLauncherSettings.readFile(stream); } file.close(); } void Wizard::MainWizard::setupInstallations() { // Check if the paths actually contain a Morrowind installation foreach (const QString path, mGameSettings.getDataDirs()) { if (findFiles(QLatin1String("Morrowind"), path)) addInstallation(path); } } void Wizard::MainWizard::runSettingsImporter() { QString path(field(QLatin1String("installation.path")).toString()); // Create the file if it doesn't already exist, else the importer will fail QString userPath(toQString(mCfgMgr.getUserConfigPath())); QFile file(userPath + QLatin1String("openmw.cfg")); if (!file.exists()) { if (!file.open(QIODevice::ReadWrite)) { // File cannot be created QMessageBox msgBox; msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not open or create %1 for writing

\

Please make sure you have the right permissions \ and try again.

").arg(file.fileName())); msgBox.exec(); return qApp->quit(); } file.close(); } // Construct the arguments to run the importer QStringList arguments; // Import plugin selection? if (field(QLatin1String("installation.new")).toBool() == true || field(QLatin1String("installation.import-addons")).toBool() == true) arguments.append(QLatin1String("--game-files")); arguments.append(QLatin1String("--encoding")); // Set encoding QString language(field(QLatin1String("installation.language")).toString()); if (language == QLatin1String("Polish")) { arguments.append(QLatin1String("win1250")); } else if (language == QLatin1String("Russian")) { arguments.append(QLatin1String("win1251")); } else { arguments.append(QLatin1String("win1252")); } // Now the paths arguments.append(QLatin1String("--ini")); if (field(QLatin1String("installation.new")).toBool() == true) { arguments.append(path + QDir::separator() + QLatin1String("Morrowind.ini")); } else { arguments.append(mInstallations[path].iniPath); } arguments.append(QLatin1String("--cfg")); arguments.append(userPath + QLatin1String("openmw.cfg")); if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false)) return qApp->quit(); } void Wizard::MainWizard::addInstallation(const QString &path) { qDebug() << "add installation in: " << path; Installation install;// = new Installation(); install.hasMorrowind = findFiles(QLatin1String("Morrowind"), path); install.hasTribunal = findFiles(QLatin1String("Tribunal"), path); install.hasBloodmoon = findFiles(QLatin1String("Bloodmoon"), path); // Try to autodetect the Morrowind.ini location QDir dir(path); QFile file(dir.filePath("Morrowind.ini")); // Try the parent directory // In normal Morrowind installations that's where Morrowind.ini is if (!file.exists()) { dir.cdUp(); file.setFileName(dir.filePath(QLatin1String("Morrowind.ini"))); } if (file.exists()) install.iniPath = file.fileName(); mInstallations.insert(QDir::toNativeSeparators(path), install); // Add it to the openmw.cfg too if (!mGameSettings.getDataDirs().contains(path)) { mGameSettings.setMultiValue(QLatin1String("data"), path); mGameSettings.addDataDir(path); } } void Wizard::MainWizard::setupPages() { setPage(Page_Intro, new IntroPage(this)); setPage(Page_MethodSelection, new MethodSelectionPage(this)); setPage(Page_LanguageSelection, new LanguageSelectionPage(this)); setPage(Page_ExistingInstallation, new ExistingInstallationPage(this)); setPage(Page_InstallationTarget, new InstallationTargetPage(this, mCfgMgr)); setPage(Page_ComponentSelection, new ComponentSelectionPage(this)); #ifdef OPENMW_USE_UNSHIELD setPage(Page_Installation, new InstallationPage(this)); #endif setPage(Page_Import, new ImportPage(this)); setPage(Page_Conclusion, new ConclusionPage(this)); setStartId(Page_Intro); } void Wizard::MainWizard::importerStarted() { } void Wizard::MainWizard::importerFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode != 0 || exitStatus == QProcess::CrashExit) return; // Re-read the settings setupGameSettings(); } void Wizard::MainWizard::accept() { writeSettings(); QWizard::accept(); } void Wizard::MainWizard::reject() { QMessageBox msgBox; msgBox.setWindowTitle(tr("Quit Wizard")); msgBox.setIcon(QMessageBox::Question); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setText(tr("Are you sure you want to exit the Wizard?")); if (msgBox.exec() == QMessageBox::Yes) { QWizard::reject(); } } void Wizard::MainWizard::writeSettings() { // Write the encoding and language settings QString language(field(QLatin1String("installation.language")).toString()); mLauncherSettings.setValue(QLatin1String("Settings/language"), language); if (language == QLatin1String("Polish")) { mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250")); } else if (language == QLatin1String("Russian")) { mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251")); } else { mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252")); } // Write the installation path so that openmw can find them QString path(field(QLatin1String("installation.path")).toString()); // Make sure the installation path is the last data= entry mGameSettings.removeDataDir(path); mGameSettings.addDataDir(path); QString userPath(toQString(mCfgMgr.getUserConfigPath())); QDir dir(userPath); if (!dir.exists()) { if (!dir.mkpath(userPath)) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error creating OpenMW configuration directory")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not create %1

\

Please make sure you have the right permissions \ and try again.

").arg(userPath)); msgBox.exec(); return qApp->quit(); } } // Game settings QFile file(userPath + QLatin1String("openmw.cfg")); if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { // File cannot be opened or created QMessageBox msgBox; msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not open %1 for writing

\

Please make sure you have the right permissions \ and try again.

").arg(file.fileName())); msgBox.exec(); return qApp->quit(); } QTextStream stream(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); mGameSettings.writeFile(stream); file.close(); // Launcher settings file.setFileName(userPath + QLatin1String(Config::LauncherSettings::sLauncherConfigFileName)); if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { // File cannot be opened or created QMessageBox msgBox; msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not open %1 for writing

\

Please make sure you have the right permissions \ and try again.

").arg(file.fileName())); msgBox.exec(); return qApp->quit(); } stream.setDevice(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); mLauncherSettings.writeFile(stream); file.close(); } bool Wizard::MainWizard::findFiles(const QString &name, const QString &path) { QDir dir(path); if (!dir.exists()) return false; // TODO: add MIME handling to make sure the files are real return (dir.entryList().contains(name + QLatin1String(".esm"), Qt::CaseInsensitive) && dir.entryList().contains(name + QLatin1String(".bsa"), Qt::CaseInsensitive)); } QString Wizard::MainWizard::toQString(const boost::filesystem::path& path) { return QString::fromUtf8(path.string().c_str()); } openmw-openmw-0.38.0/apps/wizard/mainwizard.hpp000066400000000000000000000037471264522266000215530ustar00rootroot00000000000000#ifndef MAINWIZARD_HPP #define MAINWIZARD_HPP #include #include #include #include #ifndef Q_MOC_RUN #include #endif #include #include namespace Wizard { class MainWizard : public QWizard { Q_OBJECT public: struct Installation { bool hasMorrowind; bool hasTribunal; bool hasBloodmoon; QString iniPath; }; enum { Page_Intro, Page_MethodSelection, Page_LanguageSelection, Page_ExistingInstallation, Page_InstallationTarget, Page_ComponentSelection, Page_Installation, Page_Import, Page_Conclusion }; MainWizard(QWidget *parent = 0); ~MainWizard(); bool findFiles(const QString &name, const QString &path); void addInstallation(const QString &path); void runSettingsImporter(); QMap mInstallations; Files::ConfigurationManager mCfgMgr; Process::ProcessInvoker *mImporterInvoker; bool mError; public slots: void addLogText(const QString &text); private: /// convert boost::filesystem::path to QString QString toQString(const boost::filesystem::path& path); void setupLog(); void setupGameSettings(); void setupLauncherSettings(); void setupInstallations(); void setupPages(); void writeSettings(); Config::GameSettings mGameSettings; Config::LauncherSettings mLauncherSettings; QString mLogError; private slots: void importerStarted(); void importerFinished(int exitCode, QProcess::ExitStatus exitStatus); void accept(); void reject(); }; } #endif // MAINWIZARD_HPP openmw-openmw-0.38.0/apps/wizard/methodselectionpage.cpp000066400000000000000000000013011264522266000234040ustar00rootroot00000000000000#include "methodselectionpage.hpp" #include #include "mainwizard.hpp" Wizard::MethodSelectionPage::MethodSelectionPage(QWidget *parent) : QWizardPage(parent) { mWizard = qobject_cast(parent); setupUi(this); #ifndef OPENMW_USE_UNSHIELD newLocationRadioButton->setEnabled(false); existingLocationRadioButton->setChecked(true); #endif registerField(QLatin1String("installation.new"), newLocationRadioButton); } int Wizard::MethodSelectionPage::nextId() const { if (field(QLatin1String("installation.new")).toBool() == true) { return MainWizard::Page_InstallationTarget; } else { return MainWizard::Page_ExistingInstallation; } } openmw-openmw-0.38.0/apps/wizard/methodselectionpage.hpp000066400000000000000000000007031264522266000234160ustar00rootroot00000000000000#ifndef METHODSELECTIONPAGE_HPP #define METHODSELECTIONPAGE_HPP #include #include "ui_methodselectionpage.h" namespace Wizard { class MainWizard; class MethodSelectionPage : public QWizardPage, private Ui::MethodSelectionPage { Q_OBJECT public: MethodSelectionPage(QWidget *parent); int nextId() const; private: MainWizard *mWizard; }; } #endif // METHODSELECTIONPAGE_HPP openmw-openmw-0.38.0/apps/wizard/unshield/000077500000000000000000000000001264522266000204755ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/wizard/unshield/unshieldworker.cpp000066400000000000000000000652751264522266000242650ustar00rootroot00000000000000#include "unshieldworker.hpp" #include #include #include #include #include #include #include #include #include #include #include Wizard::UnshieldWorker::UnshieldWorker(QObject *parent) : QObject(parent), mIniSettings() { unshield_set_log_level(0); mPath = QString(); mIniPath = QString(); mDiskPath = QString(); // Default to Latin encoding mIniCodec = QTextCodec::codecForName("windows-1252"); mInstallMorrowind = false; mInstallTribunal = false; mInstallBloodmoon = false; mMorrowindDone = false; mTribunalDone = false; mBloodmoonDone = false; mStopped = false; qRegisterMetaType("Wizard::Component"); } Wizard::UnshieldWorker::~UnshieldWorker() { } void Wizard::UnshieldWorker::stopWorker() { mStopped = true; } void Wizard::UnshieldWorker::setInstallComponent(Wizard::Component component, bool install) { QWriteLocker writeLock(&mLock); switch (component) { case Wizard::Component_Morrowind: mInstallMorrowind = install; break; case Wizard::Component_Tribunal: mInstallTribunal = install; break; case Wizard::Component_Bloodmoon: mInstallBloodmoon = install; break; } } bool Wizard::UnshieldWorker::getInstallComponent(Component component) { QReadLocker readLock(&mLock); switch (component) { case Wizard::Component_Morrowind: return mInstallMorrowind; case Wizard::Component_Tribunal: return mInstallTribunal; case Wizard::Component_Bloodmoon: return mInstallBloodmoon; } return false; } void Wizard::UnshieldWorker::setComponentDone(Component component, bool done) { QWriteLocker writeLock(&mLock); switch (component) { case Wizard::Component_Morrowind: mMorrowindDone = done; break; case Wizard::Component_Tribunal: mTribunalDone = done; break; case Wizard::Component_Bloodmoon: mBloodmoonDone = done; break; } } bool Wizard::UnshieldWorker::getComponentDone(Component component) { QReadLocker readLock(&mLock); switch (component) { case Wizard::Component_Morrowind: return mMorrowindDone; case Wizard::Component_Tribunal: return mTribunalDone; case Wizard::Component_Bloodmoon: return mBloodmoonDone; } return false; } void Wizard::UnshieldWorker::setPath(const QString &path) { QWriteLocker writeLock(&mLock); mPath = path; } void Wizard::UnshieldWorker::setIniPath(const QString &path) { QWriteLocker writeLock(&mLock); mIniPath = path; } void Wizard::UnshieldWorker::setDiskPath(const QString &path) { QWriteLocker writeLock(&mLock); mDiskPath = path; mWait.wakeAll(); } QString Wizard::UnshieldWorker::getPath() { QReadLocker readLock(&mLock); return mPath; } QString Wizard::UnshieldWorker::getIniPath() { QReadLocker readLock(&mLock); return mIniPath; } QString Wizard::UnshieldWorker::getDiskPath() { QReadLocker readLock(&mLock); return mDiskPath; } void Wizard::UnshieldWorker::setIniCodec(QTextCodec *codec) { QWriteLocker writeLock(&mLock); mIniCodec = codec; } bool Wizard::UnshieldWorker::setupSettings() { // Create Morrowind.ini settings map if (getIniPath().isEmpty()) return false; QFile file(getIniPath()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { emit error(tr("Failed to open Morrowind configuration file!"), tr("Opening %1 failed: %2.").arg(getIniPath(), file.errorString())); return false; } QTextStream stream(&file); stream.setCodec(mIniCodec); mIniSettings.readFile(stream); return true; } bool Wizard::UnshieldWorker::writeSettings() { if (getIniPath().isEmpty()) return false; QFile file(getIniPath()); if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { emit error(tr("Failed to open Morrowind configuration file!"), tr("Opening %1 failed: %2.").arg(getIniPath(), file.errorString())); return false; } QTextStream stream(&file); stream.setCodec(mIniCodec); if (!mIniSettings.writeFile(getIniPath(), stream)) { emit error(tr("Failed to write Morrowind configuration file!"), tr("Writing to %1 failed: %2.").arg(getIniPath(), file.errorString())); return false; } return true; } bool Wizard::UnshieldWorker::removeDirectory(const QString &dirName) { bool result = false; QDir dir(dirName); if (dir.exists(dirName)) { QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)); foreach(QFileInfo info, list) { if (info.isDir()) { result = removeDirectory(info.absoluteFilePath()); } else { result = QFile::remove(info.absoluteFilePath()); } if (!result) return result; } result = dir.rmdir(dirName); } return result; } bool Wizard::UnshieldWorker::copyFile(const QString &source, const QString &destination, bool keepSource) { QDir dir; QFile file; QFileInfo info(destination); if (info.exists()) { if (!dir.remove(info.absoluteFilePath())) return false; } if (file.copy(source, destination)) { if (!keepSource) { if (!file.remove(source)) return false; } else { return true; } } else { return false; } return true; } bool Wizard::UnshieldWorker::copyDirectory(const QString &source, const QString &destination, bool keepSource) { QDir sourceDir(source); QDir destDir(destination); bool result = true; if (!destDir.exists()) { if (!sourceDir.mkpath(destination)) return false; } destDir.refresh(); if (!destDir.exists()) return false; QFileInfoList list(sourceDir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)); foreach (const QFileInfo &info, list) { QString relativePath(info.absoluteFilePath()); relativePath.remove(source); QString destinationPath(destDir.absolutePath() + relativePath); if (info.isSymLink()) continue; if (info.isDir()) { result = copyDirectory(info.absoluteFilePath(), destinationPath); } else { result = copyFile(info.absoluteFilePath(), destinationPath); } } if (!keepSource) return result && removeDirectory(sourceDir.absolutePath()); return result; } bool Wizard::UnshieldWorker::installFile(const QString &fileName, const QString &path, Qt::MatchFlags flags, bool keepSource) { return installFiles(fileName, path, flags, true, keepSource); } bool Wizard::UnshieldWorker::installFiles(const QString &fileName, const QString &path, Qt::MatchFlags flags, bool keepSource, bool single) { QDir dir(path); if (!dir.exists()) return false; QStringList files(findFiles(fileName, path, flags)); foreach (const QString &file, files) { QFileInfo info(file); emit textChanged(tr("Installing: %1").arg(info.fileName())); if (single) { return copyFile(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource); } else { if (!copyFile(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource)) return false; } } return true; } bool Wizard::UnshieldWorker::installDirectories(const QString &dirName, const QString &path, bool recursive, bool keepSource) { QDir dir(path); if (!dir.exists()) return false; QStringList directories(findDirectories(dirName, path, recursive)); foreach (const QString &dir, directories) { QFileInfo info(dir); emit textChanged(tr("Installing: %1 directory").arg(info.fileName())); if (!copyDirectory(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName(), keepSource)) return false; } return true; } void Wizard::UnshieldWorker::extract() { if (getInstallComponent(Wizard::Component_Morrowind)) { if (!getComponentDone(Wizard::Component_Morrowind)) if (!setupComponent(Wizard::Component_Morrowind)) return; } if (getInstallComponent(Wizard::Component_Tribunal)) { if (!getComponentDone(Wizard::Component_Tribunal)) if (!setupComponent(Wizard::Component_Tribunal)) return; } if (getInstallComponent(Wizard::Component_Bloodmoon)) { if (!getComponentDone(Wizard::Component_Bloodmoon)) if (!setupComponent(Wizard::Component_Bloodmoon)) return; } // Update Morrowind configuration if (getInstallComponent(Wizard::Component_Tribunal)) { mIniSettings.setValue(QLatin1String("Archives/Archive 0"), QVariant(QString("Tribunal.bsa"))); mIniSettings.setValue(QLatin1String("Game Files/GameFile1"), QVariant(QString("Tribunal.esm"))); } if (getInstallComponent(Wizard::Component_Bloodmoon)) { mIniSettings.setValue(QLatin1String("Archives/Archive 0"), QVariant(QString("Bloodmoon.bsa"))); mIniSettings.setValue(QLatin1String("Game Files/GameFile1"), QVariant(QString("Bloodmoon.esm"))); } if (getInstallComponent(Wizard::Component_Tribunal) && getInstallComponent(Wizard::Component_Bloodmoon)) { mIniSettings.setValue(QLatin1String("Archives/Archive 0"), QVariant(QString("Tribunal.bsa"))); mIniSettings.setValue(QLatin1String("Archives/Archive 1"), QVariant(QString("Bloodmoon.bsa"))); mIniSettings.setValue(QLatin1String("Game Files/GameFile1"), QVariant(QString("Tribunal.esm"))); mIniSettings.setValue(QLatin1String("Game Files/GameFile2"), QVariant(QString("Bloodmoon.esm"))); } // Write the settings to the Morrowind config file if (!writeSettings()) return; // Remove the temporary directory removeDirectory(getPath() + QDir::separator() + QLatin1String("extract-temp")); // Fill the progress bar int total = 0; if (getInstallComponent(Wizard::Component_Morrowind)) total = 100; if (getInstallComponent(Wizard::Component_Tribunal)) total = total + 100; if (getInstallComponent(Wizard::Component_Bloodmoon)) total = total + 100; emit textChanged(tr("Installation finished!")); emit progressChanged(total); emit finished(); } bool Wizard::UnshieldWorker::setupComponent(Component component) { QString name; switch (component) { case Wizard::Component_Morrowind: name = QLatin1String("Morrowind"); break; case Wizard::Component_Tribunal: name = QLatin1String("Tribunal"); break; case Wizard::Component_Bloodmoon: name = QLatin1String("Bloodmoon"); break; } if (name.isEmpty()) { emit error(tr("Component parameter is invalid!"), tr("An invalid component parameter was supplied.")); return false; } bool found = false; QString cabFile; QDir disk; // Keep showing the file dialog until we find the necessary install files while (!found) { if (getDiskPath().isEmpty()) { QReadLocker readLock(&mLock); emit requestFileDialog(component); mWait.wait(&mLock); disk.setPath(getDiskPath()); } else { disk.setPath(getDiskPath()); } QStringList list(findFiles(QLatin1String("data1.hdr"), disk.absolutePath())); foreach (const QString &file, list) { qDebug() << "current archive: " << file; if (component == Wizard::Component_Morrowind) { bool morrowindFound = findInCab(QLatin1String("Morrowind.bsa"), file); bool tribunalFound = findInCab(QLatin1String("Tribunal.bsa"), file); bool bloodmoonFound = findInCab(QLatin1String("Bloodmoon.bsa"), file); if (morrowindFound) { // Check if we have correct archive, other archives have Morrowind.bsa too if ((tribunalFound && bloodmoonFound) || (!tribunalFound && !bloodmoonFound)) { cabFile = file; found = true; // We have a GoTY disk or a Morrowind-only disk } } } else { if (findInCab(name + QLatin1String(".bsa"), file)) { cabFile = file; found = true; } } } if (!found) { QReadLocker readLock(&mLock); emit requestFileDialog(component); mWait.wait(&mLock); } } if (installComponent(component, cabFile)) { setComponentDone(component, true); return true; } else { return false; } return true; } bool Wizard::UnshieldWorker::installComponent(Component component, const QString &path) { QString name; switch (component) { case Wizard::Component_Morrowind: name = QLatin1String("Morrowind"); break; case Wizard::Component_Tribunal: name = QLatin1String("Tribunal"); break; case Wizard::Component_Bloodmoon: name = QLatin1String("Bloodmoon"); break; } if (name.isEmpty()) { emit error(tr("Component parameter is invalid!"), tr("An invalid component parameter was supplied.")); return false; } emit textChanged(tr("Installing %1").arg(name)); QFileInfo info(path); if (!info.exists()) { emit error(tr("Installation media path not set!"), tr("The source path for %1 was not set.").arg(name)); return false; } // Create temporary extract directory // TODO: Use QTemporaryDir in Qt 5.0 QString tempPath(getPath() + QDir::separator() + QLatin1String("extract-temp")); QDir temp; // Make sure the temporary folder is empty removeDirectory(tempPath); if (!temp.mkpath(tempPath)) { emit error(tr("Cannot create temporary directory!"), tr("Failed to create %1.").arg(tempPath)); return false; } temp.setPath(tempPath); if (!temp.mkdir(name)) { emit error(tr("Cannot create temporary directory!"), tr("Failed to create %1.").arg(temp.absoluteFilePath(name))); return false; } if (!temp.cd(name)) { emit error(tr("Cannot move into temporary directory!"), tr("Failed to move into %1.").arg(temp.absoluteFilePath(name))); return false; } // Extract the installation files if (!extractCab(info.absoluteFilePath(), temp.absolutePath())) return false; // Move the files from the temporary path to the destination folder emit textChanged(tr("Moving installation files")); // Install extracted directories QStringList directories; directories << QLatin1String("BookArt") << QLatin1String("Fonts") << QLatin1String("Icons") << QLatin1String("Meshes") << QLatin1String("Music") << QLatin1String("Sound") << QLatin1String("Splash") << QLatin1String("Textures") << QLatin1String("Video"); foreach (const QString &dir, directories) { if (!installDirectories(dir, temp.absolutePath())) { emit error(tr("Could not install directory!"), tr("Installing %1 to %2 failed.").arg(dir, temp.absolutePath())); return false; } } // Install directories from disk foreach (const QString &dir, directories) { if (!installDirectories(dir, info.absolutePath(), false, true)) { emit error(tr("Could not install directory!"), tr("Installing %1 to %2 failed.").arg(dir, info.absolutePath())); return false; } } // Install translation files QStringList extensions; extensions << QLatin1String(".cel") << QLatin1String(".top") << QLatin1String(".mrk"); foreach (const QString &extension, extensions) { if (!installFiles(extension, info.absolutePath(), Qt::MatchEndsWith)) { emit error(tr("Could not install translation file!"), tr("Failed to install *%1 files.").arg(extension)); return false; } } if (component == Wizard::Component_Morrowind) { QStringList files; files << QLatin1String("Morrowind.esm") << QLatin1String("Morrowind.bsa"); foreach (const QString &file, files) { if (!installFile(file, temp.absolutePath())) { emit error(tr("Could not install Morrowind data file!"), tr("Failed to install %1.").arg(file)); return false; } } // Copy Morrowind configuration file if (!installFile(QLatin1String("Morrowind.ini"), temp.absolutePath())) { emit error(tr("Could not install Morrowind configuration file!"), tr("Failed to install %1.").arg(QLatin1String("Morrowind.ini"))); return false; } // Setup Morrowind configuration setIniPath(getPath() + QDir::separator() + QLatin1String("Morrowind.ini")); if (!setupSettings()) return false; } if (component == Wizard::Component_Tribunal) { QFileInfo sounds(temp.absoluteFilePath(QLatin1String("Sounds"))); QString dest(getPath() + QDir::separator() + QLatin1String("Sound")); if (sounds.exists()) { emit textChanged(tr("Installing: Sound directory")); if (!copyDirectory(sounds.absoluteFilePath(), dest)) { emit error(tr("Could not install directory!"), tr("Installing %1 to %2 failed.").arg(sounds.absoluteFilePath(), dest)); return false; } } QStringList files; files << QLatin1String("Tribunal.esm") << QLatin1String("Tribunal.bsa"); foreach (const QString &file, files) { if (!installFile(file, temp.absolutePath())) { emit error(tr("Could not find Tribunal data file!"), tr("Failed to find %1.").arg(file)); return false; } } } if (component == Wizard::Component_Bloodmoon) { QFileInfo original(getPath() + QDir::separator() + QLatin1String("Tribunal.esm")); if (original.exists()) { if (!installFile(QLatin1String("Tribunal.esm"), temp.absolutePath())) { emit error(tr("Could not find Tribunal patch file!"), tr("Failed to find %1.").arg(QLatin1String("Tribunal.esm"))); return false; } } QStringList files; files << QLatin1String("Bloodmoon.esm") << QLatin1String("Bloodmoon.bsa"); foreach (const QString &file, files) { if (!installFile(file, temp.absolutePath())) { emit error(tr("Could not find Bloodmoon data file!"), tr("Failed to find %1.").arg(file)); return false; } } // Load Morrowind configuration settings from the setup script QStringList list(findFiles(QLatin1String("setup.inx"), getDiskPath())); emit textChanged(tr("Updating Morrowind configuration file")); foreach (const QString &inx, list) { mIniSettings.parseInx(inx); } } // Finally, install Data Files directories from temp and disk QStringList datafiles(findDirectories(QLatin1String("Data Files"), temp.absolutePath())); datafiles.append(findDirectories(QLatin1String("Data Files"), info.absolutePath())); foreach (const QString &dir, datafiles) { QFileInfo info(dir); emit textChanged(tr("Installing: %1 directory").arg(info.fileName())); if (!copyDirectory(info.absoluteFilePath(), getPath())) { emit error(tr("Could not install directory!"), tr("Installing %1 to %2 failed.").arg(info.absoluteFilePath(), getPath())); return false; } } emit textChanged(tr("%1 installation finished!").arg(name)); return true; } bool Wizard::UnshieldWorker::extractFile(Unshield *unshield, const QString &destination, const QString &prefix, int index, int counter) { bool success = false; QString path(destination); path.append(QDir::separator()); int directory = unshield_file_directory(unshield, index); if (!prefix.isEmpty()) path.append(prefix + QDir::separator()); if (directory >= 0) path.append(QString::fromUtf8(unshield_directory_name(unshield, directory)) + QDir::separator()); // Ensure the path has the right separators path.replace(QLatin1Char('\\'), QDir::separator()); path = QDir::toNativeSeparators(path); // Ensure the target path exists QDir dir; if (!dir.mkpath(path)) return false; QString fileName(path); fileName.append(QString::fromUtf8(unshield_file_name(unshield, index))); // Calculate the percentage done int progress = (((float) counter / (float) unshield_file_count(unshield)) * 100); if (getComponentDone(Wizard::Component_Morrowind)) progress = progress + 100; if (getComponentDone(Wizard::Component_Tribunal)) progress = progress + 100; emit textChanged(tr("Extracting: %1").arg(QString::fromUtf8(unshield_file_name(unshield, index)))); emit progressChanged(progress); QByteArray array(fileName.toUtf8()); success = unshield_file_save(unshield, index, array.constData()); if (!success) { qDebug() << "error"; dir.remove(fileName); } return success; } bool Wizard::UnshieldWorker::extractCab(const QString &cabFile, const QString &destination) { bool success = false; QByteArray array(cabFile.toUtf8()); Unshield *unshield; unshield = unshield_open(array.constData()); if (!unshield) { emit error(tr("Failed to open InstallShield Cabinet File."), tr("Opening %1 failed.").arg(cabFile)); unshield_close(unshield); return false; } int counter = 0; for (int i=0; ifirst_file; j<=group->last_file; ++j) { if (mStopped) { qDebug() << "We're asked to stop!"; unshield_close(unshield); return true; } if (unshield_file_is_valid(unshield, j)) { success = extractFile(unshield, destination, group->name, j, counter); if (!success) { QString name(QString::fromUtf8(unshield_file_name(unshield, j))); emit error(tr("Failed to extract %1.").arg(name), tr("Complete path: %1").arg(destination + QDir::separator() + name)); unshield_close(unshield); return false; } ++counter; } } } unshield_close(unshield); return success; } QString Wizard::UnshieldWorker::findFile(const QString &fileName, const QString &path) { return findFiles(fileName, path).first(); } QStringList Wizard::UnshieldWorker::findFiles(const QString &fileName, const QString &path, int depth, bool recursive, bool directories, Qt::MatchFlags flags) { static const int MAXIMUM_DEPTH = 10; if (depth >= MAXIMUM_DEPTH) { qWarning("Maximum directory depth limit reached."); return QStringList(); } QStringList result; QDir dir(path); // Prevent parsing over the complete filesystem if (dir == QDir::rootPath()) return QStringList(); if (!dir.exists()) return QStringList(); QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files, QDir::DirsFirst)); foreach(QFileInfo info, list) { if (info.isSymLink()) continue; if (info.isDir()) { if (directories) { if (info.fileName() == fileName) { result.append(info.absoluteFilePath()); } else { if (recursive) result.append(findFiles(fileName, info.absoluteFilePath(), depth + 1, recursive, true)); } } else { if (recursive) result.append(findFiles(fileName, info.absoluteFilePath(), depth + 1)); } } else { if (directories) break; switch (flags) { case Qt::MatchExactly: if (info.fileName() == fileName) result.append(info.absoluteFilePath()); break; case Qt::MatchEndsWith: if (info.fileName().endsWith(fileName)) result.append(info.absoluteFilePath()); break; } } } return result; } QStringList Wizard::UnshieldWorker::findDirectories(const QString &dirName, const QString &path, bool recursive) { return findFiles(dirName, path, 0, true, true); } bool Wizard::UnshieldWorker::findInCab(const QString &fileName, const QString &cabFile) { QByteArray array(cabFile.toUtf8()); Unshield *unshield; unshield = unshield_open(array.constData()); if (!unshield) { emit error(tr("Failed to open InstallShield Cabinet File."), tr("Opening %1 failed.").arg(cabFile)); unshield_close(unshield); return false; } for (int i=0; ifirst_file; j<=group->last_file; ++j) { if (unshield_file_is_valid(unshield, j)) { QString current(QString::fromUtf8(unshield_file_name(unshield, j))); if (current.toLower() == fileName.toLower()) { unshield_close(unshield); return true; // File is found! } } } } unshield_close(unshield); return false; } openmw-openmw-0.38.0/apps/wizard/unshield/unshieldworker.hpp000066400000000000000000000066211264522266000242600ustar00rootroot00000000000000#ifndef UNSHIELDWORKER_HPP #define UNSHIELDWORKER_HPP #include #include #include #include #include #include #include #include "../inisettings.hpp" namespace Wizard { enum Component { Component_Morrowind, Component_Tribunal, Component_Bloodmoon }; class UnshieldWorker : public QObject { Q_OBJECT public: UnshieldWorker(QObject *parent = 0); ~UnshieldWorker(); void stopWorker(); void setInstallComponent(Wizard::Component component, bool install); void setDiskPath(const QString &path); void setPath(const QString &path); void setIniPath(const QString &path); QString getPath(); QString getIniPath(); void setIniCodec(QTextCodec *codec); bool setupSettings(); private: bool writeSettings(); bool getInstallComponent(Component component); QString getDiskPath(); void setComponentDone(Component component, bool done = true); bool getComponentDone(Component component); bool removeDirectory(const QString &dirName); bool copyFile(const QString &source, const QString &destination, bool keepSource = true); bool copyDirectory(const QString &source, const QString &destination, bool keepSource = true); bool extractCab(const QString &cabFile, const QString &destination); bool extractFile(Unshield *unshield, const QString &destination, const QString &prefix, int index, int counter); bool findInCab(const QString &fileName, const QString &cabFile); QString findFile(const QString &fileName, const QString &path); QStringList findFiles(const QString &fileName, const QString &path, int depth = 0, bool recursive = true, bool directories = false, Qt::MatchFlags flags = Qt::MatchExactly); QStringList findDirectories(const QString &dirName, const QString &path, bool recursive = true); bool installFile(const QString &fileName, const QString &path, Qt::MatchFlags flags = Qt::MatchExactly, bool keepSource = false); bool installFiles(const QString &fileName, const QString &path, Qt::MatchFlags flags = Qt::MatchExactly, bool keepSource = false, bool single = false); bool installDirectories(const QString &dirName, const QString &path, bool recursive = true, bool keepSource = false); bool installComponent(Component component, const QString &path); bool setupComponent(Component component); bool mInstallMorrowind; bool mInstallTribunal; bool mInstallBloodmoon; bool mMorrowindDone; bool mTribunalDone; bool mBloodmoonDone; bool mStopped; QString mPath; QString mIniPath; QString mDiskPath; IniSettings mIniSettings; QTextCodec *mIniCodec; QWaitCondition mWait; QReadWriteLock mLock; public slots: void extract(); signals: void finished(); void requestFileDialog(Wizard::Component component); void textChanged(const QString &text); void error(const QString &text, const QString &details); void progressChanged(int progress); }; } #endif // UNSHIELDWORKER_HPP openmw-openmw-0.38.0/apps/wizard/utils/000077500000000000000000000000001264522266000200225ustar00rootroot00000000000000openmw-openmw-0.38.0/apps/wizard/utils/componentlistwidget.cpp000066400000000000000000000022161264522266000246310ustar00rootroot00000000000000#include "componentlistwidget.hpp" #include #include ComponentListWidget::ComponentListWidget(QWidget *parent) : QListWidget(parent) { mCheckedItems = QStringList(); connect(this, SIGNAL(itemChanged(QListWidgetItem *)), this, SLOT(updateCheckedItems(QListWidgetItem *))); connect(model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(updateCheckedItems(QModelIndex, int, int))); } QStringList ComponentListWidget::checkedItems() { mCheckedItems.removeDuplicates(); return mCheckedItems; } void ComponentListWidget::updateCheckedItems(const QModelIndex &index, int start, int end) { updateCheckedItems(item(start)); } void ComponentListWidget::updateCheckedItems(QListWidgetItem *item) { if (!item) return; QString text = item->text(); if (item->checkState() == Qt::Checked) { if (!mCheckedItems.contains(text)) mCheckedItems.append(text); } else { if (mCheckedItems.contains(text)) mCheckedItems.removeAll(text); } mCheckedItems.removeDuplicates(); emit checkedItemsChanged(mCheckedItems); } openmw-openmw-0.38.0/apps/wizard/utils/componentlistwidget.hpp000066400000000000000000000011021264522266000246270ustar00rootroot00000000000000#ifndef COMPONENTLISTWIDGET_HPP #define COMPONENTLISTWIDGET_HPP #include class ComponentListWidget : public QListWidget { Q_OBJECT Q_PROPERTY(QStringList mCheckedItems READ checkedItems) public: ComponentListWidget(QWidget *parent = 0); QStringList mCheckedItems; QStringList checkedItems(); signals: void checkedItemsChanged(const QStringList &items); private slots: void updateCheckedItems(QListWidgetItem *item); void updateCheckedItems(const QModelIndex &index, int start, int end); }; #endif // COMPONENTLISTWIDGET_HPP openmw-openmw-0.38.0/cmake/000077500000000000000000000000001264522266000154775ustar00rootroot00000000000000openmw-openmw-0.38.0/cmake/COPYING-CMAKE-SCRIPTS000066400000000000000000000027221264522266000205000ustar00rootroot00000000000000The following files are derived from the Thermite project (http://www.thermite3d.org) and are covered under the license below. FindMYGUI.cmake, FindBullet.cmake Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. openmw-openmw-0.38.0/cmake/FindBullet.cmake000066400000000000000000000050021264522266000205260ustar00rootroot00000000000000# - Try to find the Bullet physics engine # # This module defines the following variables # # BULLET_FOUND - Was bullet found # BULLET_INCLUDE_DIRS - the Bullet include directories # BULLET_LIBRARIES - Link to this, by default it includes # all bullet components (Dynamics, # Collision, LinearMath, & SoftBody) # # This module accepts the following variables # # BULLET_ROOT - Can be set to bullet install path or Windows build path # # Copyright (c) 2009, Philip Lowman # # Redistribution AND use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. set(BULLET_ROOT $ENV{BULLET_ROOT}) macro(_FIND_BULLET_LIBRARY _var) find_library(${_var} NAMES ${ARGN} PATHS ${BULLET_ROOT} ${BULLET_ROOT}/lib/Debug ${BULLET_ROOT}/lib/Release ${BULLET_ROOT}/out/release8/libs ${BULLET_ROOT}/out/debug8/libs PATH_SUFFIXES lib ) mark_as_advanced(${_var}) endmacro() macro(_BULLET_APPEND_LIBRARIES _list _release) set(_debug ${_release}_DEBUG) if(${_debug}) set(${_list} ${${_list}} optimized ${${_release}} debug ${${_debug}}) else() set(${_list} ${${_list}} ${${_release}}) endif() endmacro() find_path(BULLET_INCLUDE_DIR NAMES btBulletCollisionCommon.h PATHS ${BULLET_ROOT}/include ${BULLET_ROOT}/src PATH_SUFFIXES bullet ) # Find the libraries #_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY BulletDynamics) #_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_Debug BulletDynamics_d) _FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision) _FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_Debug BulletCollision_d) _FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY BulletMath LinearMath) _FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG BulletMath_Debug BulletMath_d LinearMath_debug LinearMath_d) # handle the QUIETLY and REQUIRED arguments and set BULLET_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Bullet DEFAULT_MSG #BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY BULLET_INCLUDE_DIR) set(BULLET_INCLUDE_DIRS ${BULLET_INCLUDE_DIR}) if(BULLET_FOUND) #_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_DYNAMICS_LIBRARY) _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_COLLISION_LIBRARY) _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_MATH_LIBRARY) endif() openmw-openmw-0.38.0/cmake/FindFFmpeg.cmake000066400000000000000000000133461264522266000204550ustar00rootroot00000000000000# vim: ts=2 sw=2 # - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC) # # Once done this will define # FFMPEG_FOUND - System has the all required components. # FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers. # FFMPEG_LIBRARIES - Link these to use the required ffmpeg components. # FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components. # # For each of the components it will additionaly set. # - AVCODEC # - AVDEVICE # - AVFORMAT # - AVUTIL # - POSTPROCESS # - SWSCALE # - SWRESAMPLE # the following variables will be defined # _FOUND - System has # _INCLUDE_DIRS - Include directory necessary for using the headers # _LIBRARIES - Link these to use # _DEFINITIONS - Compiler switches required for using # _VERSION - The components version # # Copyright (c) 2006, Matthias Kretz, # Copyright (c) 2008, Alexander Neundorf, # Copyright (c) 2011, Michael Jansen, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. include(FindPackageHandleStandardArgs) # The default components were taken from a survey over other FindFFMPEG.cmake files if (NOT FFmpeg_FIND_COMPONENTS) set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) endif () # ### Macro: set_component_found # # Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present. # macro(set_component_found _component ) if (${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS) # message(STATUS " - ${_component} found.") set(${_component}_FOUND TRUE) else () # message(STATUS " - ${_component} not found.") endif () endmacro() # ### Macro: find_component # # Checks for the given component by invoking pkgconfig and then looking up the libraries and # include directories. # macro(find_component _component _pkgconfig _library _header) if (NOT WIN32) # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PC_${_component} ${_pkgconfig}) endif () endif (NOT WIN32) find_path(${_component}_INCLUDE_DIRS ${_header} HINTS ${FFMPEGSDK_INC} ${PC_LIB${_component}_INCLUDEDIR} ${PC_LIB${_component}_INCLUDE_DIRS} PATH_SUFFIXES ffmpeg ) find_library(${_component}_LIBRARIES NAMES ${_library} HINTS ${FFMPEGSDK_LIB} ${PC_LIB${_component}_LIBDIR} ${PC_LIB${_component}_LIBRARY_DIRS} ) set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.") set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.") set_component_found(${_component}) mark_as_advanced( ${_component}_INCLUDE_DIRS ${_component}_LIBRARIES ${_component}_DEFINITIONS ${_component}_VERSION) endmacro() # Check for cached results. If there are skip the costly part. if (NOT FFMPEG_LIBRARIES) set (FFMPEGSDK $ENV{FFMPEG_HOME}) if (FFMPEGSDK) set (FFMPEGSDK_INC "${FFMPEGSDK}/include") set (FFMPEGSDK_LIB "${FFMPEGSDK}/lib") endif () # Check for all possible component. find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) find_component(AVFORMAT libavformat avformat libavformat/avformat.h) find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h) find_component(AVUTIL libavutil avutil libavutil/avutil.h) find_component(SWSCALE libswscale swscale libswscale/swscale.h) find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h) find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h) find_component(AVRESAMPLE libavresample avresample libavresample/avresample.h) # Check if the required components were found and add their stuff to the FFMPEG_* vars. foreach (_component ${FFmpeg_FIND_COMPONENTS}) if (${_component}_FOUND) # message(STATUS "Required component ${_component} present.") set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES}) set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS}) list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS}) else () # message(STATUS "Required component ${_component} missing.") endif () endforeach () # Build the include path with duplicates removed. if (FFMPEG_INCLUDE_DIRS) list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) endif () # cache the vars. set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE) set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE) set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE) mark_as_advanced(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARIES FFMPEG_DEFINITIONS) endif () # Now set the noncached _FOUND vars for the components. foreach (_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWSCALE SWRESAMPLE AVRESAMPLE) set_component_found(${_component}) endforeach () # Compile the list of required vars set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) foreach (_component ${FFmpeg_FIND_COMPONENTS}) list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS) endforeach () # Give a nice error message if some of the required vars are missing. find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS}) openmw-openmw-0.38.0/cmake/FindFreetype.cmake000066400000000000000000000051571264522266000210750ustar00rootroot00000000000000#------------------------------------------------------------------- # This file is part of the CMake build system for OGRE # (Object-oriented Graphics Rendering Engine) # For the latest info, see http://www.ogre3d.org/ # # The contents of this file are placed in the public domain. Feel # free to make use of it in any way you like. #------------------------------------------------------------------- # - Try to find FreeType # Once done, this will define # # FREETYPE_FOUND - system has FreeType # FREETYPE_INCLUDE_DIRS - the FreeType include directories # FREETYPE_LIBRARIES - link these to use FreeType include(FindPkgMacros) findpkg_begin(FREETYPE) # Get path, convert backslashes as ${ENV_${var}} getenv_path(FREETYPE_HOME) # construct search paths set(FREETYPE_PREFIX_PATH ${FREETYPE_HOME} ${ENV_FREETYPE_HOME}) create_search_paths(FREETYPE) # redo search if prefix path changed clear_if_changed(FREETYPE_PREFIX_PATH FREETYPE_LIBRARY_FWK FREETYPE_LIBRARY_REL FREETYPE_LIBRARY_DBG FREETYPE_INCLUDE_DIR ) set(FREETYPE_LIBRARY_NAMES freetype2311 freetype239 freetype238 freetype235 freetype219 freetype) get_debug_names(FREETYPE_LIBRARY_NAMES) use_pkgconfig(FREETYPE_PKGC freetype2) # prefer static library over framework set(CMAKE_FIND_FRAMEWORK "LAST") message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}") findpkg_framework(FREETYPE) message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}") find_path(FREETYPE_INCLUDE_DIR NAMES freetype/freetype.h HINTS ${FREETYPE_INC_SEARCH_PATH} ${FREETYPE_PKGC_INCLUDE_DIRS} PATH_SUFFIXES freetype2) find_path(FREETYPE_FT2BUILD_INCLUDE_DIR NAMES ft2build.h HINTS ${FREETYPE_INC_SEARCH_PATH} ${FREETYPE_PKGC_INCLUDE_DIRS}) if (SYMBIAN) set(ORIGINAL_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}) set(CMAKE_PREFIX_PATH ${CMAKE_SYSYEM_OUT_DIR}) message(STATUS "Lib will be searched in Symbian out dir: ${CMAKE_SYSYEM_OUT_DIR}") endif (SYMBIAN) find_library(FREETYPE_LIBRARY_REL NAMES ${FREETYPE_LIBRARY_NAMES} HINTS ${FREETYPE_LIB_SEARCH_PATH} ${FREETYPE_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel) find_library(FREETYPE_LIBRARY_DBG NAMES ${FREETYPE_LIBRARY_NAMES_DBG} HINTS ${FREETYPE_LIB_SEARCH_PATH} ${FREETYPE_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug) if (SYMBIAN) set(CMAKE_PREFIX_PATH ${ORIGINAL_CMAKE_PREFIX_PATH}) endif (SYMBIAN) make_library_set(FREETYPE_LIBRARY) findpkg_finish(FREETYPE) mark_as_advanced(FREETYPE_FT2BUILD_INCLUDE_DIR) if (NOT FREETYPE_FT2BUILD_INCLUDE_DIR STREQUAL FREETYPE_INCLUDE_DIR) set(FREETYPE_INCLUDE_DIRS ${FREETYPE_INCLUDE_DIRS} ${FREETYPE_FT2BUILD_INCLUDE_DIR}) endif () # Reset framework finding set(CMAKE_FIND_FRAMEWORK "FIRST") openmw-openmw-0.38.0/cmake/FindLIBUNSHIELD.cmake000066400000000000000000000022541264522266000210470ustar00rootroot00000000000000# Locate LIBUNSHIELD # This module defines # LIBUNSHIELD_LIBRARY # LIBUNSHIELD_FOUND, if false, do not try to link to LibUnshield # LIBUNSHIELD_INCLUDE_DIR, where to find the headers # # Created by Tom Mason (wheybags) for OpenMW (http://openmw.org), based on FindMPG123.cmake # # Ripped off from other sources. In fact, this file is so generic (I # just did a search and replace on another file) that I wonder why the # CMake guys haven't wrapped this entire thing in a single # function. Do we really need to repeat this stuff for every single # library when they all work the same? FIND_PATH(LIBUNSHIELD_INCLUDE_DIR libunshield.h HINTS PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /sw # Fink /opt/local # DarwinPorts /opt/csw # Blastwave /opt /usr/include ) FIND_LIBRARY(LIBUNSHIELD_LIBRARY unshield HINTS # PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64 PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /sw /opt/local /opt/csw /opt /usr/lib ) IF(LIBUNSHIELD_LIBRARY AND LIBUNSHIELD_INCLUDE_DIR) SET(LIBUNSHIELD_FOUND "YES") ENDIF(LIBUNSHIELD_LIBRARY AND LIBUNSHIELD_INCLUDE_DIR) openmw-openmw-0.38.0/cmake/FindMyGUI.cmake000066400000000000000000000145741264522266000202470ustar00rootroot00000000000000# - Find MyGUI includes and library # # This module defines # MYGUI_INCLUDE_DIRS # MYGUI_LIBRARIES, the libraries to link against to use MYGUI. # MYGUI_LIB_DIR, the location of the libraries # MYGUI_FOUND, If false, do not try to use MYGUI # # Copyright © 2007, Matt Williams # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. CMAKE_POLICY(PUSH) include(FindPkgMacros) include(PreprocessorUtils) # IF (MYGUI_LIBRARIES AND MYGUI_INCLUDE_DIRS) # SET(MYGUI_FIND_QUIETLY TRUE) # ENDIF (MYGUI_LIBRARIES AND MYGUI_INCLUDE_DIRS) IF (WIN32) #Windows MESSAGE(STATUS "Looking for MyGUI") IF(MINGW) FIND_PATH ( MYGUI_INCLUDE_DIRS MyGUI.h PATH_SUFFIXES MYGUI) FIND_LIBRARY ( MYGUI_LIBRARIES_REL NAMES libMyGUIEngine${CMAKE_SHARED_LIBRARY_SUFFIX} HINTS ${MYGUI_LIB_DIR} PATH_SUFFIXES "" release relwithdebinfo minsizerel ) FIND_LIBRARY ( MYGUI_LIBRARIES_DBG NAMES libMyGUIEngine_d${CMAKE_SHARED_LIBRARY_SUFFIX} HINTS ${MYGUI_LIB_DIR} PATH_SUFFIXES "" debug ) make_library_set ( MYGUI_LIBRARIES ) MESSAGE ("${MYGUI_LIBRARIES}") ENDIF(MINGW) SET(MYGUISDK $ENV{MYGUI_HOME}) IF (MYGUISDK) findpkg_begin ( "MYGUI" ) MESSAGE(STATUS "Using MyGUI in MyGUI SDK") STRING(REGEX REPLACE "[\\]" "/" MYGUISDK "${MYGUISDK}" ) find_path ( MYGUI_INCLUDE_DIRS MyGUI.h "${MYGUISDK}/MyGUIEngine/include" NO_DEFAULT_PATH ) SET ( MYGUI_LIB_DIR ${MYGUISDK}/lib ${MYGUISDK}/*/lib ) if ( MYGUI_STATIC ) set(LIB_SUFFIX "Static") find_package(freetype) endif ( MYGUI_STATIC ) find_library ( MYGUI_LIBRARIES_REL NAMES MyGUIEngine${LIB_SUFFIX}.lib HINTS ${MYGUI_LIB_DIR} PATH_SUFFIXES "" release relwithdebinfo minsizerel ) find_library ( MYGUI_LIBRARIES_DBG NAMES MyGUIEngine${LIB_SUFFIX}_d.lib HINTS ${MYGUI_LIB_DIR} PATH_SUFFIXES "" debug ) make_library_set ( MYGUI_LIBRARIES ) MESSAGE ("${MYGUI_LIBRARIES}") #findpkg_finish ( "MYGUI" ) ENDIF (MYGUISDK) ELSE (WIN32) #Unix CMAKE_MINIMUM_REQUIRED(VERSION 2.4.7 FATAL_ERROR) FIND_PACKAGE(PkgConfig) IF(MYGUI_STATIC) # don't use pkgconfig on OS X, find freetype & append it's libs to resulting MYGUI_LIBRARIES IF (NOT APPLE) PKG_SEARCH_MODULE(MYGUI MYGUIStatic MyGUIStatic) IF (MYGUI_INCLUDE_DIRS) SET(MYGUI_INCLUDE_DIRS ${MYGUI_INCLUDE_DIRS}) SET(MYGUI_LIB_DIR ${MYGUI_LIBDIR}) SET(MYGUI_LIBRARIES ${MYGUI_LIBRARIES} CACHE STRING "") ELSE (MYGUI_INCLUDE_DIRS) FIND_PATH(MYGUI_INCLUDE_DIRS MyGUI.h PATHS /usr/local/include /usr/include PATH_SUFFIXES MyGUI MYGUI) FIND_LIBRARY(MYGUI_LIBRARIES myguistatic PATHS /usr/lib /usr/local/lib) SET(MYGUI_LIB_DIR ${MYGUI_LIBRARIES}) STRING(REGEX REPLACE "(.*)/.*" "\\1" MYGUI_LIB_DIR "${MYGUI_LIB_DIR}") STRING(REGEX REPLACE ".*/" "" MYGUI_LIBRARIES "${MYGUI_LIBRARIES}") ENDIF (MYGUI_INCLUDE_DIRS) ELSE (NOT APPLE) SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${MYGUI_DEPENDENCIES_DIR}) FIND_PACKAGE(freetype) FIND_PATH(MYGUI_INCLUDE_DIRS MyGUI.h PATHS /usr/local/include /usr/include PATH_SUFFIXES MyGUI MYGUI) FIND_LIBRARY(MYGUI_LIBRARIES MyGUIEngineStatic PATHS /usr/lib /usr/local/lib) SET(MYGUI_LIB_DIR ${MYGUI_LIBRARIES}) STRING(REGEX REPLACE "(.*)/.*" "\\1" MYGUI_LIB_DIR "${MYGUI_LIB_DIR}") STRING(REGEX REPLACE ".*/" "" MYGUI_LIBRARIES "${MYGUI_LIBRARIES}") ENDIF (NOT APPLE) ELSE(MYGUI_STATIC) PKG_SEARCH_MODULE(MYGUI MYGUI MyGUI) IF (MYGUI_INCLUDE_DIRS) SET(MYGUI_INCLUDE_DIRS ${MYGUI_INCLUDE_DIRS}) SET(MYGUI_LIB_DIR ${MYGUI_LIBDIR}) SET(MYGUI_LIBRARIES ${MYGUI_LIBRARIES} CACHE STRING "") ELSE (MYGUI_INCLUDE_DIRS) FIND_PATH(MYGUI_INCLUDE_DIRS MyGUI.h PATHS /usr/local/include /usr/include PATH_SUFFIXES MyGUI MYGUI) FIND_LIBRARY(MYGUI_LIBRARIES mygui PATHS /usr/lib /usr/local/lib) SET(MYGUI_LIB_DIR ${MYGUI_LIBRARIES}) STRING(REGEX REPLACE "(.*)/.*" "\\1" MYGUI_LIB_DIR "${MYGUI_LIB_DIR}") STRING(REGEX REPLACE ".*/" "" MYGUI_LIBRARIES "${MYGUI_LIBRARIES}") ENDIF (MYGUI_INCLUDE_DIRS) ENDIF(MYGUI_STATIC) ENDIF (WIN32) #Do some preparation IF (NOT WIN32) # This does not work on Windows for paths with spaces in them SEPARATE_ARGUMENTS(MYGUI_INCLUDE_DIRS) SEPARATE_ARGUMENTS(MYGUI_LIBRARIES) ENDIF (NOT WIN32) SET(MYGUI_LIBRARIES ${MYGUI_LIBRARIES} ${FREETYPE_LIBRARIES}) SET(MYGUI_INCLUDE_DIRS ${MYGUI_INCLUDE_DIRS} CACHE PATH "") SET(MYGUI_LIBRARIES ${MYGUI_LIBRARIES} CACHE STRING "") SET(MYGUI_LIB_DIR ${MYGUI_LIB_DIR} CACHE PATH "") IF (NOT APPLE OR NOT MYGUI_STATIC) # we need explicit freetype libs only on OS X for static build, for other cases just make it TRUE SET(FREETYPE_LIBRARIES TRUE) ENDIF (NOT APPLE OR NOT MYGUI_STATIC) IF (MYGUI_INCLUDE_DIRS AND MYGUI_LIBRARIES AND FREETYPE_LIBRARIES) SET(MYGUI_FOUND TRUE) ENDIF (MYGUI_INCLUDE_DIRS AND MYGUI_LIBRARIES AND FREETYPE_LIBRARIES) IF (MYGUI_FOUND) MARK_AS_ADVANCED(MYGUI_LIB_DIR) IF (NOT MYGUI_FIND_QUIETLY) MESSAGE(STATUS " libraries : ${MYGUI_LIBRARIES} from ${MYGUI_LIB_DIR}") MESSAGE(STATUS " includes : ${MYGUI_INCLUDE_DIRS}") ENDIF (NOT MYGUI_FIND_QUIETLY) find_file(MYGUI_PREQUEST_FILE NAMES MyGUI_Prerequest.h PATHS ${MYGUI_INCLUDE_DIRS}) file(READ ${MYGUI_PREQUEST_FILE} MYGUI_TEMP_VERSION_CONTENT) get_preprocessor_entry(MYGUI_TEMP_VERSION_CONTENT MYGUI_VERSION_MAJOR MYGUI_VERSION_MAJOR) get_preprocessor_entry(MYGUI_TEMP_VERSION_CONTENT MYGUI_VERSION_MINOR MYGUI_VERSION_MINOR) get_preprocessor_entry(MYGUI_TEMP_VERSION_CONTENT MYGUI_VERSION_PATCH MYGUI_VERSION_PATCH) set(MYGUI_VERSION "${MYGUI_VERSION_MAJOR}.${MYGUI_VERSION_MINOR}.${MYGUI_VERSION_PATCH}") IF (NOT MYGUI_FIND_QUIETLY) MESSAGE(STATUS "MyGUI version: ${MYGUI_VERSION}") ENDIF (NOT MYGUI_FIND_QUIETLY) ENDIF (MYGUI_FOUND) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(MyGUI DEFAULT_MSG MYGUI_INCLUDE_DIRS FREETYPE_LIBRARIES MYGUI_LIBRARIES) CMAKE_POLICY(POP) openmw-openmw-0.38.0/cmake/FindOpenGLES.cmake000066400000000000000000000047071264522266000206660ustar00rootroot00000000000000#------------------------------------------------------------------- # This file is part of the CMake build system for OGRE # (Object-oriented Graphics Rendering Engine) # For the latest info, see http://www.ogre3d.org/ # # The contents of this file are placed in the public domain. Feel # free to make use of it in any way you like. #------------------------------------------------------------------- # - Try to find OpenGLES # Once done this will define # # OPENGLES_FOUND - system has OpenGLES # OPENGLES_INCLUDE_DIR - the GL include directory # OPENGLES_LIBRARIES - Link these to use OpenGLES IF (WIN32) IF (CYGWIN) FIND_PATH(OPENGLES_INCLUDE_DIR GLES/gl.h ) FIND_LIBRARY(OPENGLES_gl_LIBRARY libgles_cm ) ELSE (CYGWIN) IF(BORLAND) SET (OPENGLES_gl_LIBRARY import32 CACHE STRING "OpenGL ES 1.x library for win32") ELSE(BORLAND) #MS compiler - todo - fix the following line: SET (OPENGLES_gl_LIBRARY ${OGRE_SOURCE_DIR}/Dependencies/lib/release/libgles_cm.lib CACHE STRING "OpenGL ES 1.x library for win32") ENDIF(BORLAND) ENDIF (CYGWIN) ELSE (WIN32) IF (APPLE) #create_search_paths(/Developer/Platforms) #findpkg_framework(OpenGLES) #set(OPENGLES_gl_LIBRARY "-framework OpenGLES") ELSE(APPLE) FIND_PATH(OPENGLES_INCLUDE_DIR GLES/gl.h /opt/vc/include /opt/graphics/OpenGL/include /usr/openwin/share/include /usr/X11R6/include /usr/include ) FIND_LIBRARY(OPENGLES_gl_LIBRARY NAMES GLES_CM GLESv1_CM PATHS /opt/vc/lib /opt/graphics/OpenGL/lib /usr/openwin/lib /usr/shlib /usr/X11R6/lib /usr/lib ) # On Unix OpenGL most certainly always requires X11. # Feel free to tighten up these conditions if you don't # think this is always true. #IF (OPENGLES_gl_LIBRARY) # IF(NOT X11_FOUND) # INCLUDE(FindX11) # ENDIF(NOT X11_FOUND) # IF (X11_FOUND) # SET (OPENGLES_LIBRARIES ${X11_LIBRARIES}) # ENDIF (X11_FOUND) #ENDIF (OPENGLES_gl_LIBRARY) ENDIF(APPLE) ENDIF (WIN32) SET( OPENGLES_FOUND "NO" ) IF(OPENGLES_gl_LIBRARY) SET( OPENGLES_LIBRARIES ${OPENGLES_gl_LIBRARY} ${OPENGLES_LIBRARIES}) SET( OPENGLES_FOUND "YES" ) ENDIF(OPENGLES_gl_LIBRARY) MARK_AS_ADVANCED( OPENGLES_INCLUDE_DIR OPENGLES_gl_LIBRARY ) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(OPENGLES REQUIRED_VARS OPENGLES_LIBRARIES OPENGLES_INCLUDE_DIR) openmw-openmw-0.38.0/cmake/FindPkgMacros.cmake000066400000000000000000000136211264522266000211730ustar00rootroot00000000000000#------------------------------------------------------------------- # This file is part of the CMake build system for OGRE # (Object-oriented Graphics Rendering Engine) # For the latest info, see http://www.ogre3d.org/ # # The contents of this file are placed in the public domain. Feel # free to make use of it in any way you like. #------------------------------------------------------------------- ################################################################## # Provides some common functionality for the FindPackage modules ################################################################## # Begin processing of package macro(findpkg_begin PREFIX) if (NOT ${PREFIX}_FIND_QUIETLY) message(STATUS "Looking for ${PREFIX}...") endif () endmacro(findpkg_begin) # Display a status message unless FIND_QUIETLY is set macro(pkg_message PREFIX) if (NOT ${PREFIX}_FIND_QUIETLY) message(STATUS ${ARGN}) endif () endmacro(pkg_message) # Get environment variable, define it as ENV_$var and make sure backslashes are converted to forward slashes macro(getenv_path VAR) set(ENV_${VAR} $ENV{${VAR}}) # replace won't work if var is blank if (ENV_${VAR}) string( REGEX REPLACE "\\\\" "/" ENV_${VAR} ${ENV_${VAR}} ) endif () endmacro(getenv_path) # Construct search paths for includes and libraries from a PREFIX_PATH macro(create_search_paths PREFIX) foreach(dir ${${PREFIX}_PREFIX_PATH}) set(${PREFIX}_INC_SEARCH_PATH ${${PREFIX}_INC_SEARCH_PATH} ${dir}/include ${dir}/Include ${dir}/include/${PREFIX} ${dir}/Headers) set(${PREFIX}_LIB_SEARCH_PATH ${${PREFIX}_LIB_SEARCH_PATH} ${dir}/lib ${dir}/Lib ${dir}/lib/${PREFIX} ${dir}/Libs) set(${PREFIX}_BIN_SEARCH_PATH ${${PREFIX}_BIN_SEARCH_PATH} ${dir}/bin) endforeach(dir) set(${PREFIX}_FRAMEWORK_SEARCH_PATH ${${PREFIX}_PREFIX_PATH}) endmacro(create_search_paths) # clear cache variables if a certain variable changed macro(clear_if_changed TESTVAR) # test against internal check variable # HACK: Apparently, adding a variable to the cache cleans up the list # a bit. We need to also remove any empty strings from the list, but # at the same time ensure that we are actually dealing with a list. list(APPEND ${TESTVAR} "") list(REMOVE_ITEM ${TESTVAR} "") if (NOT "${${TESTVAR}}" STREQUAL "${${TESTVAR}_INT_CHECK}") message(STATUS "${TESTVAR} changed.") foreach(var ${ARGN}) set(${var} "NOTFOUND" CACHE STRING "x" FORCE) endforeach(var) endif () set(${TESTVAR}_INT_CHECK ${${TESTVAR}} CACHE INTERNAL "x" FORCE) endmacro(clear_if_changed) # Try to get some hints from pkg-config, if available macro(use_pkgconfig PREFIX PKGNAME) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(${PREFIX} ${PKGNAME}) endif () endmacro (use_pkgconfig) # Couple a set of release AND debug libraries (or frameworks) macro(make_library_set PREFIX) if (${PREFIX}_FWK) set(${PREFIX} ${${PREFIX}_FWK}) elseif (${PREFIX}_REL AND ${PREFIX}_DBG) set(${PREFIX} optimized ${${PREFIX}_REL} debug ${${PREFIX}_DBG}) elseif (${PREFIX}_REL) set(${PREFIX} ${${PREFIX}_REL}) elseif (${PREFIX}_DBG) set(${PREFIX} ${${PREFIX}_DBG}) endif () endmacro(make_library_set) # Generate debug names from given release names macro(get_debug_names PREFIX) foreach(i ${${PREFIX}}) set(${PREFIX}_DBG ${${PREFIX}_DBG} ${i}d ${i}D ${i}_d ${i}_D ${i}_debug ${i}) endforeach(i) endmacro(get_debug_names) # Add the parent dir from DIR to VAR macro(add_parent_dir VAR DIR) get_filename_component(${DIR}_TEMP "${${DIR}}/.." ABSOLUTE) set(${VAR} ${${VAR}} ${${DIR}_TEMP}) endmacro(add_parent_dir) # Do the final processing for the package find. macro(findpkg_finish PREFIX) # skip if already processed during this run if (NOT ${PREFIX}_FOUND) if (${PREFIX}_INCLUDE_DIR AND ${PREFIX}_LIBRARY) set(${PREFIX}_FOUND TRUE) set(${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIR}) set(${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARY}) if (NOT ${PREFIX}_FIND_QUIETLY) message(STATUS "Found ${PREFIX}: ${${PREFIX}_LIBRARIES}") endif () else () if (NOT ${PREFIX}_FIND_QUIETLY) message(STATUS "Could not locate ${PREFIX}") endif () if (${PREFIX}_FIND_REQUIRED) message(FATAL_ERROR "Required library ${PREFIX} not found! Install the library (including dev packages) and try again. If the library is already installed, set the missing variables manually in cmake.") endif () endif () mark_as_advanced(${PREFIX}_INCLUDE_DIR ${PREFIX}_LIBRARY ${PREFIX}_LIBRARY_REL ${PREFIX}_LIBRARY_DBG ${PREFIX}_LIBRARY_FWK) endif () endmacro(findpkg_finish) # Slightly customised framework finder MACRO(findpkg_framework fwk) IF(APPLE) SET(${fwk}_FRAMEWORK_PATH ${${fwk}_FRAMEWORK_SEARCH_PATH} ${CMAKE_FRAMEWORK_PATH} ~/Library/Frameworks /Library/Frameworks /System/Library/Frameworks /Network/Library/Frameworks /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk/System/Library/Frameworks/ ${CMAKE_CURRENT_SOURCE_DIR}/lib/Release ${CMAKE_CURRENT_SOURCE_DIR}/lib/Debug ) # These could be arrays of paths, add each individually to the search paths foreach(i ${OGRE_PREFIX_PATH}) set(${fwk}_FRAMEWORK_PATH ${${fwk}_FRAMEWORK_PATH} ${i}/lib/Release ${i}/lib/Debug) endforeach(i) foreach(i ${OGRE_PREFIX_BUILD}) set(${fwk}_FRAMEWORK_PATH ${${fwk}_FRAMEWORK_PATH} ${i}/lib/Release ${i}/lib/Debug) endforeach(i) FOREACH(dir ${${fwk}_FRAMEWORK_PATH}) SET(fwkpath ${dir}/${fwk}.framework) IF(EXISTS ${fwkpath}) SET(${fwk}_FRAMEWORK_INCLUDES ${${fwk}_FRAMEWORK_INCLUDES} ${fwkpath}/Headers ${fwkpath}/PrivateHeaders) SET(${fwk}_FRAMEWORK_PATH ${dir}) if (NOT ${fwk}_LIBRARY_FWK) SET(${fwk}_LIBRARY_FWK "-framework ${fwk}") endif () ENDIF(EXISTS ${fwkpath}) ENDFOREACH(dir) ENDIF(APPLE) ENDMACRO(findpkg_framework) openmw-openmw-0.38.0/cmake/FindSDL2.cmake000066400000000000000000000161101264522266000200050ustar00rootroot00000000000000# Locate SDL2 library # This module defines # SDL2_LIBRARY, the name of the library to link against # SDL2_FOUND, if false, do not try to link to SDL2 # SDL2_INCLUDE_DIR, where to find SDL.h # # This module responds to the the flag: # SDL2_BUILDING_LIBRARY # If this is defined, then no SDL2_main will be linked in because # only applications need main(). # Otherwise, it is assumed you are building an application and this # module will attempt to locate and set the the proper link flags # as part of the returned SDL2_LIBRARY variable. # # Don't forget to include SDL2main.h and SDL2main.m your project for the # OS X framework based version. (Other versions link to -lSDL2main which # this module will try to find on your behalf.) Also for OS X, this # module will automatically add the -framework Cocoa on your behalf. # # # Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration # and no SDL2_LIBRARY, it means CMake did not find your SDL2 library # (SDL2.dll, libsdl2.so, SDL2.framework, etc). # Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. # Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value # as appropriate. These values are used to generate the final SDL2_LIBRARY # variable, but when these values are unset, SDL2_LIBRARY does not get created. # # # $SDL2DIR is an environment variable that would # correspond to the ./configure --prefix=$SDL2DIR # used in building SDL2. # l.e.galup 9-20-02 # # Modified by Eric Wing. # Added code to assist with automated building by using environmental variables # and providing a more controlled/consistent search behavior. # Added new modifications to recognize OS X frameworks and # additional Unix paths (FreeBSD, etc). # Also corrected the header search path to follow "proper" SDL2 guidelines. # Added a search for SDL2main which is needed by some platforms. # Added a search for threads which is needed by some platforms. # Added needed compile switches for MinGW. # # On OSX, this will prefer the Framework version (if found) over others. # People will have to manually change the cache values of # SDL2_LIBRARY to override this selection or set the CMake environment # CMAKE_INCLUDE_PATH to modify the search paths. # # Note that the header path has changed from SDL2/SDL.h to just SDL.h # This needed to change because "proper" SDL2 convention # is #include "SDL.h", not . This is done for portability # reasons because not all systems place things in SDL2/ (see FreeBSD). # # Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake # module with the minor edit of changing "SDL" to "SDL2" where necessary. This # was not created for redistribution, and exists temporarily pending official # SDL2 CMake modules. #============================================================================= # Copyright 2003-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) FIND_PATH(SDL2_INCLUDE_DIR SDL.h HINTS $ENV{SDL2DIR} PATH_SUFFIXES include/SDL2 include PATHS ~/Library/Frameworks /Library/Frameworks /usr/local/include/SDL2 /usr/include/SDL2 /sw # Fink /opt/local # DarwinPorts /opt/csw # Blastwave /opt ) #MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}") FIND_LIBRARY(SDL2_LIBRARY_PATH NAMES SDL2 HINTS $ENV{SDL2DIR} PATH_SUFFIXES lib64 lib PATHS /sw /opt/local /opt/csw /opt ) set(SDL2_LIBRARY_ONLY ${SDL2_LIBRARY_PATH} CACHE STRING "The SDL2 library, with no other libraries.") set(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_PATH}) #MESSAGE("SDL2_LIBRARY_TEMP is ${SDL2_LIBRARY_TEMP}") IF(NOT SDL2_BUILDING_LIBRARY) IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") # Non-OS X framework versions expect you to also dynamically link to # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms # seem to provide SDL2main for compatibility even though they don't # necessarily need it. FIND_LIBRARY(SDL2MAIN_LIBRARY NAMES SDL2main HINTS $ENV{SDL2DIR} PATH_SUFFIXES lib64 lib PATHS /sw /opt/local /opt/csw /opt ) ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") ENDIF(NOT SDL2_BUILDING_LIBRARY) # SDL2 may require threads on your system. # The Apple build may not need an explicit flag because one of the # frameworks may already provide it. # But for non-OSX systems, I will use the CMake Threads package. IF(NOT APPLE) FIND_PACKAGE(Threads) ENDIF(NOT APPLE) # MinGW needs an additional library, mwindows # It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows # (Actually on second look, I think it only needs one of the m* libraries.) IF(MINGW) SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") ENDIF(MINGW) SET(SDL2_FOUND "NO") IF(SDL2_LIBRARY_TEMP) # For SDL2main IF(NOT SDL2_BUILDING_LIBRARY) IF(SDL2MAIN_LIBRARY) SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) ENDIF(SDL2MAIN_LIBRARY) ENDIF(NOT SDL2_BUILDING_LIBRARY) # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. # CMake doesn't display the -framework Cocoa string in the UI even # though it actually is there if I modify a pre-used variable. # I think it has something to do with the CACHE STRING. # So I use a temporary variable until the end so I can set the # "real" variable in one-shot. IF(APPLE) SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") ENDIF(APPLE) # For threads, as mentioned Apple doesn't need this. # In fact, there seems to be a problem if I used the Threads package # and try using this line, so I'm just skipping it entirely for OS X. IF(NOT APPLE) SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) ENDIF(NOT APPLE) # For MinGW library IF(MINGW) SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) ENDIF(MINGW) IF(WIN32) SET(SDL2_LIBRARY_TEMP winmm imm32 version msimg32 ${SDL2_LIBRARY_TEMP}) ENDIF(WIN32) # Set the final string here so the GUI reflects the final state. SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") # Set the temp variable to INTERNAL so it is not seen in the CMake GUI SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") SET(SDL2_FOUND "YES") ENDIF(SDL2_LIBRARY_TEMP) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) IF(SDL2_STATIC) if (UNIX AND NOT APPLE) EXECUTE_PROCESS(COMMAND sdl2-config --static-libs OUTPUT_VARIABLE SDL2_LINK_FLAGS) STRING(REGEX REPLACE "(\r?\n)+$" "" SDL2_LINK_FLAGS "${SDL2_LINK_FLAGS}") SET(SDL2_LIBRARY ${SDL2_LINK_FLAGS}) ENDIF() ENDIF(SDL2_STATIC) openmw-openmw-0.38.0/cmake/GitVersion.cmake000066400000000000000000000014521264522266000205740ustar00rootroot00000000000000execute_process ( COMMAND ${GIT_EXECUTABLE} rev-list --tags --max-count=1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} RESULT_VARIABLE EXITCODE1 OUTPUT_VARIABLE TAGHASH OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process ( COMMAND ${GIT_EXECUTABLE} rev-parse HEAD WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} RESULT_VARIABLE EXITCODE2 OUTPUT_VARIABLE COMMITHASH OUTPUT_STRIP_TRAILING_WHITESPACE) string (COMPARE EQUAL "${EXITCODE1}:${EXITCODE2}" "0:0" SUCCESS) if (SUCCESS) set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") set(OPENMW_VERSION_TAGHASH "${TAGHASH}") message(STATUS "OpenMW version ${OPENMW_VERSION}") else (SUCCESS) message(WARNING "Failed to get valid version information from Git") endif (SUCCESS) configure_file(${VERSION_IN_FILE} ${VERSION_FILE}) openmw-openmw-0.38.0/cmake/OpenMWMacros.cmake000066400000000000000000000102001264522266000210040ustar00rootroot00000000000000function(enable_unity_build UB_SUFFIX SOURCE_VARIABLE_NAME) set(files ${SOURCE_VARIABLE_NAME}) # Generate a unique filename for the unity build translation unit set(unit_build_file ${CMAKE_CURRENT_BINARY_DIR}/ub_${UB_SUFFIX}.cpp) # Exclude all translation units from compilation set_source_files_properties(${files} PROPERTIES HEADER_FILE_ONLY true) # Open the ub file FILE(WRITE ${unit_build_file} "// Unity Build generated by CMake\n") # Add include statement for each translation unit foreach(source_file ${files} ) FILE( APPEND ${unit_build_file} "#include <${source_file}>\n") endforeach(source_file) # Complement list of translation units with the name of ub set(${SOURCE_VARIABLE_NAME} ${${SOURCE_VARIABLE_NAME}} ${unit_build_file} PARENT_SCOPE) endfunction(enable_unity_build) macro (add_openmw_dir dir) set (files) set (cppfiles) foreach (u ${ARGN}) # Add cpp and hpp to OPENMW_FILES file (GLOB ALL "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND OPENMW_FILES "${f}") endforeach (f) # Add cpp to unity build file (GLOB ALL "${dir}/${u}.cpp") foreach (f ${ALL}) list (APPEND cppfiles "${f}") endforeach (f) endforeach (u) if (OPENMW_UNITY_BUILD) enable_unity_build(${dir} "${cppfiles}") list (APPEND OPENMW_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${dir}.cpp) endif() source_group ("apps\\openmw\\${dir}" FILES ${files}) endmacro (add_openmw_dir) macro (add_component_dir dir) set (files) set (cppfiles) foreach (u ${ARGN}) file (GLOB ALL "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND COMPONENT_FILES "${f}") endforeach (f) # Add cpp to unity build file (GLOB ALL "${dir}/${u}.cpp") foreach (f ${ALL}) list (APPEND cppfiles "${f}") endforeach (f) endforeach (u) if (OPENMW_UNITY_BUILD) enable_unity_build(${dir} "${cppfiles}") list (APPEND COMPONENT_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${dir}.cpp) endif() source_group ("components\\${dir}" FILES ${files}) endmacro (add_component_dir) macro (add_component_qt_dir dir) set (files) foreach (u ${ARGN}) file (GLOB ALL "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND COMPONENT_FILES "${f}") endforeach (f) file (GLOB MOC_H "${dir}/${u}.hpp") foreach (fi ${MOC_H}) list (APPEND COMPONENT_MOC_FILES "${fi}") endforeach (fi) endforeach (u) source_group ("components\\${dir}" FILES ${files}) endmacro (add_component_qt_dir) macro (copy_all_files source_dir destination_dir files) foreach (f ${files}) get_filename_component(filename ${f} NAME) configure_file(${source_dir}/${f} ${destination_dir}/${filename} COPYONLY) endforeach (f) endmacro (copy_all_files) macro (add_file project type file) list (APPEND ${project}${type} ${file}) endmacro (add_file) macro (add_unit project dir unit) add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp") add_file (${project} _SRC ${comp} "${dir}/${unit}.cpp") endmacro (add_unit) macro (add_qt_unit project dir unit) add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp") add_file (${project} _HDR_QT ${comp} "${dir}/${unit}.hpp") add_file (${project} _SRC ${comp} "${dir}/${unit}.cpp") endmacro (add_qt_unit) macro (add_hdr project dir unit) add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp") endmacro (add_hdr) macro (add_qt_hdr project dir unit) add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp") add_file (${project} _HDR_QT ${comp} "${dir}/${unit}.hpp") endmacro (add_qt_hdr) macro (opencs_units dir) foreach (u ${ARGN}) add_qt_unit (OPENCS ${dir} ${u}) endforeach (u) endmacro (opencs_units) macro (opencs_units_noqt dir) foreach (u ${ARGN}) add_unit (OPENCS ${dir} ${u}) endforeach (u) endmacro (opencs_units_noqt) macro (opencs_hdrs dir) foreach (u ${ARGN}) add_qt_hdr (OPENCS ${dir} ${u}) endforeach (u) endmacro (opencs_hdrs) macro (opencs_hdrs_noqt dir) foreach (u ${ARGN}) add_hdr (OPENCS ${dir} ${u}) endforeach (u) endmacro (opencs_hdrs_noqt) openmw-openmw-0.38.0/cmake/PreprocessorUtils.cmake000066400000000000000000000030731264522266000222130ustar00rootroot00000000000000#------------------------------------------------------------------- # This file is part of the CMake build system for OGRE # (Object-oriented Graphics Rendering Engine) # For the latest info, see http://www.ogre3d.org/ # # The contents of this file are placed in the public domain. Feel # free to make use of it in any way you like. #------------------------------------------------------------------- macro(get_preprocessor_entry CONTENTS KEYWORD VARIABLE) string(REGEX MATCH "# *define +${KEYWORD} +((\"([^\n]*)\")|([^ \n]*))" PREPROC_TEMP_VAR ${${CONTENTS}} ) if (CMAKE_MATCH_3) set(${VARIABLE} ${CMAKE_MATCH_3}) else () set(${VARIABLE} ${CMAKE_MATCH_4}) endif () endmacro() macro(has_preprocessor_entry CONTENTS KEYWORD VARIABLE) string(REGEX MATCH "\n *# *define +(${KEYWORD})" PREPROC_TEMP_VAR ${${CONTENTS}} ) if (CMAKE_MATCH_1) set(${VARIABLE} TRUE) else () set(${VARIABLE} FALSE) endif () endmacro() macro(replace_preprocessor_entry VARIABLE KEYWORD NEW_VALUE) string(REGEX REPLACE "(// *)?# *define +${KEYWORD} +[^ \n]*" "#define ${KEYWORD} ${NEW_VALUE}" ${VARIABLE}_TEMP ${${VARIABLE}} ) set(${VARIABLE} ${${VARIABLE}_TEMP}) endmacro() macro(set_preprocessor_entry VARIABLE KEYWORD ENABLE) if (${ENABLE}) set(TMP_REPLACE_STR "#define ${KEYWORD}") else () set(TMP_REPLACE_STR "// #define ${KEYWORD}") endif () string(REGEX REPLACE "(// *)?# *define +${KEYWORD} *\n" ${TMP_REPLACE_STR} ${VARIABLE}_TEMP ${${VARIABLE}} ) set(${VARIABLE} ${${VARIABLE}_TEMP}) endmacro() openmw-openmw-0.38.0/components/000077500000000000000000000000001264522266000166045ustar00rootroot00000000000000openmw-openmw-0.38.0/components/CMakeLists.txt000066400000000000000000000156361264522266000213570ustar00rootroot00000000000000project (Components) # Version file set (VERSION_IN_FILE "${OpenMW_SOURCE_DIR}/files/version.in") set (VERSION_FILE "${OpenMW_BINARY_DIR}/resources/version") if (GIT_CHECKOUT) add_custom_target (git-version COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} -DVERSION_IN_FILE=${VERSION_IN_FILE} -DVERSION_FILE=${VERSION_FILE} -DOPENMW_VERSION_MAJOR=${OPENMW_VERSION_MAJOR} -DOPENMW_VERSION_MINOR=${OPENMW_VERSION_MINOR} -DOPENMW_VERSION_RELEASE=${OPENMW_VERSION_RELEASE} -DOPENMW_VERSION=${OPENMW_VERSION} -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/GitVersion.cmake VERBATIM) else (GIT_CHECKOUT) configure_file(${VERSION_IN_FILE} ${VERSION_FILE}) endif (GIT_CHECKOUT) if (OPENGL_ES) find_package(OpenGLES REQUIRED) else() find_package(OpenGL REQUIRED) endif() # source files add_component_dir (settings settings ) add_component_dir (bsa bsa_file ) add_component_dir (vfs manager archive bsaarchive filesystemarchive registerarchives ) add_component_dir (resource scenemanager keyframemanager texturemanager resourcesystem bulletshapemanager bulletshape niffilemanager objectcache ) add_component_dir (sceneutil clone attach lightmanager visitor util statesetupdater controller skeleton riggeometry lightcontroller positionattitudetransform # not used yet #workqueue ) add_component_dir (nif controlled effect niftypes record controller extra node record_ptr data niffile property nifkey base nifstream ) add_component_dir (nifosg nifloader controller particle userdata ) add_component_dir (nifbullet bulletnifloader ) add_component_dir (to_utf8 to_utf8 ) add_component_dir (esm attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile aisequence magiceffects util custommarkerstate stolenitems transport ) add_component_dir (esmterrain storage ) add_component_dir (misc utf8stream stringops resourcehelpers rng ) IF(NOT WIN32 AND NOT APPLE) add_definitions(-DGLOBAL_DATA_PATH="${GLOBAL_DATA_PATH}") add_definitions(-DGLOBAL_CONFIG_PATH="${GLOBAL_CONFIG_PATH}") ENDIF() add_component_dir (files linuxpath androidpath windowspath macospath fixedpath multidircollection collections configurationmanager lowlevelfile constrainedfilestream memorystream ) add_component_dir (compiler context controlparser errorhandler exception exprparser extensions fileparser generator lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser quickfileparser discardparser junkparser ) add_component_dir (interpreter context controlopcodes genericopcodes installopcodes interpreter localopcodes mathopcodes miscopcodes opcodes runtime scriptopcodes spatialopcodes types defines ) add_component_dir (translation translation ) add_component_dir (terrain storage world buffercache defs terraingrid material ) add_component_dir (loadinglistener loadinglistener ) add_component_dir (myguiplatform myguirendermanager myguidatamanager myguiplatform myguitexture myguiloglistener additivelayer scalinglayer ) add_component_dir (widgets box imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets ) add_component_dir (fontloader fontloader ) add_component_dir (sdlutil sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper OISCompat events sdlcursormanager ) add_component_dir (version version ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) if (USE_QT) add_component_qt_dir (contentselector model/modelitem model/esmfile model/naturalsort model/contentmodel model/loadordererror view/combobox view/contentselector ) add_component_qt_dir (config gamesettings launchersettings settingsbase ) add_component_qt_dir (process processinvoker ) if (DESIRED_QT_VERSION MATCHES 4) include(${QT_USE_FILE}) QT4_WRAP_UI(ESM_UI_HDR ${ESM_UI}) QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) else() QT5_WRAP_UI(ESM_UI_HDR ${ESM_UI}) QT5_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) endif() endif() if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" AND NOT APPLE) add_definitions(-fPIC) endif() endif () include_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR}) if (OPENGL_ES) set(GL_LIB ${OPENGLES_gl_LIBRARY}) else() set(GL_LIB ${OPENGL_gl_LIBRARY}) endif() target_link_libraries(components ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${OSG_LIBRARIES} ${OPENTHREADS_LIBRARIES} ${OSGPARTICLE_LIBRARIES} ${OSGUTIL_LIBRARIES} ${OSGDB_LIBRARIES} ${OSGVIEWER_LIBRARIES} ${OSGGA_LIBRARIES} ${OSGFX_LIBRARIES} ${OSGANIMATION_LIBRARIES} ${BULLET_LIBRARIES} ${SDL2_LIBRARY} # For MyGUI platform ${GL_LIB} ${MYGUI_LIBRARIES} ) if (WIN32) target_link_libraries(components ${Boost_LOCALE_LIBRARY}) endif() if (USE_QT) if (DESIRED_QT_VERSION MATCHES 4) target_link_libraries(components ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY}) else() qt5_use_modules(components Widgets Core) endif() endif() if (GIT_CHECKOUT) add_dependencies (components git-version) endif (GIT_CHECKOUT) if (WIN32) target_link_libraries(components shlwapi) if(MINGW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOGDI") endif(MINGW) endif() # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT}) endif() # Make the variable accessible for other subdirectories set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) openmw-openmw-0.38.0/components/bsa/000077500000000000000000000000001264522266000173515ustar00rootroot00000000000000openmw-openmw-0.38.0/components/bsa/bsa_file.cpp000066400000000000000000000123571264522266000216310ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (bsa_file.cpp) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #include "bsa_file.hpp" #include #include #include using namespace std; using namespace Bsa; /// Error handling void BSAFile::fail(const string &msg) { throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + filename); } /// Read header information from the input source void BSAFile::readHeader() { /* * The layout of a BSA archive is as follows: * * - 12 bytes header, contains 3 ints: * id number - equal to 0x100 * dirsize - size of the directory block (see below) * numfiles - number of files * * ---------- start of directory block ----------- * * - 8 bytes*numfiles, each record contains: * fileSize * offset into data buffer (see below) * * - 4 bytes*numfiles, each record is an offset into the following name buffer * * - name buffer, indexed by the previous table, each string is * null-terminated. Size is (dirsize - 12*numfiles). * * ---------- end of directory block ------------- * * - 8*filenum - hash table block, we currently ignore this * * ----------- start of data buffer -------------- * * - The rest of the archive is file data, indexed by the * offsets in the directory block. The offsets start at 0 at * the beginning of this buffer. * */ assert(!isLoaded); namespace bfs = boost::filesystem; bfs::ifstream input(bfs::path(filename), std::ios_base::binary); // Total archive size std::streamoff fsize = 0; if(input.seekg(0, std::ios_base::end)) { fsize = input.tellg(); input.seekg(0); } if(fsize < 12) fail("File too small to be a valid BSA archive"); // Get essential header numbers size_t dirsize, filenum; { // First 12 bytes uint32_t head[3]; input.read(reinterpret_cast(head), 12); if(head[0] != 0x100) fail("Unrecognized BSA header"); // Total number of bytes used in size/offset-table + filename // sections. dirsize = head[1]; // Number of files filenum = head[2]; } // Each file must take up at least 21 bytes of data in the bsa. So // if files*21 overflows the file size then we are guaranteed that // the archive is corrupt. if((filenum*21 > unsigned(fsize -12)) || (dirsize+8*filenum > unsigned(fsize -12)) ) fail("Directory information larger than entire archive"); // Read the offset info into a temporary buffer std::vector offsets(3*filenum); input.read(reinterpret_cast(&offsets[0]), 12*filenum); // Read the string table stringBuf.resize(dirsize-12*filenum); input.read(&stringBuf[0], stringBuf.size()); // Check our position assert(input.tellg() == std::streampos(12+dirsize)); // Calculate the offset of the data buffer. All file offsets are // relative to this. 12 header bytes + directory + hash table // (skipped) size_t fileDataOffset = 12 + dirsize + 8*filenum; // Set up the the FileStruct table files.resize(filenum); for(size_t i=0;i fsize) fail("Archive contains offsets outside itself"); // Add the file name to the lookup lookup[fs.name] = i; } isLoaded = true; } /// Get the index of a given file name, or -1 if not found int BSAFile::getIndex(const char *str) const { Lookup::const_iterator it = lookup.find(str); if(it == lookup.end()) return -1; int res = it->second; assert(res >= 0 && (size_t)res < files.size()); return res; } /// Open an archive file. void BSAFile::open(const string &file) { filename = file; readHeader(); } Files::IStreamPtr BSAFile::getFile(const char *file) { assert(file); int i = getIndex(file); if(i == -1) fail("File not found: " + string(file)); const FileStruct &fs = files[i]; return Files::openConstrainedFileStream (filename.c_str (), fs.offset, fs.fileSize); } Files::IStreamPtr BSAFile::getFile(const FileStruct *file) { return Files::openConstrainedFileStream (filename.c_str (), file->offset, file->fileSize); } openmw-openmw-0.38.0/components/bsa/bsa_file.hpp000066400000000000000000000064241264522266000216340ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (bsa_file.h) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #ifndef BSA_BSA_FILE_H #define BSA_BSA_FILE_H #include #include #include #include #include #include namespace Bsa { /** This class is used to read "Bethesda Archive Files", or BSAs. */ class BSAFile { public: /// Represents one file entry in the archive struct FileStruct { // File size and offset in file. We store the offset from the // beginning of the file, not the offset into the data buffer // (which is what is stored in the archive.) uint32_t fileSize, offset; // Zero-terminated file name const char *name; }; typedef std::vector FileList; private: /// Table of files in this archive FileList files; /// Filename string buffer std::vector stringBuf; /// True when an archive has been loaded bool isLoaded; /// Used for error messages std::string filename; /// Case insensitive string comparison struct iltstr { bool operator()(const char *s1, const char *s2) const { return Misc::StringUtils::ciLess(s1, s2); } }; /** A map used for fast file name lookup. The value is the index into the files[] vector above. The iltstr ensures that file name checks are case insensitive. */ typedef std::map Lookup; Lookup lookup; /// Error handling void fail(const std::string &msg); /// Read header information from the input source void readHeader(); /// Get the index of a given file name, or -1 if not found int getIndex(const char *str) const; public: /* ----------------------------------- * BSA management methods * ----------------------------------- */ BSAFile() : isLoaded(false) { } /// Open an archive file. void open(const std::string &file); /* ----------------------------------- * Archive file routines * ----------------------------------- */ /// Check if a file exists bool exists(const char *file) const { return getIndex(file) != -1; } /** Open a file contained in the archive. Throws an exception if the file doesn't exist. */ Files::IStreamPtr getFile(const char *file); Files::IStreamPtr getFile(const FileStruct* file); /// Get a list of all files const FileList &getList() const { return files; } }; } #endif openmw-openmw-0.38.0/components/compiler/000077500000000000000000000000001264522266000204165ustar00rootroot00000000000000openmw-openmw-0.38.0/components/compiler/context.hpp000066400000000000000000000027301264522266000226150ustar00rootroot00000000000000#ifndef COMPILER_CONTEXT_H_INCLUDED #define COMPILER_CONTEXT_H_INCLUDED #include namespace Compiler { class Extensions; class Context { const Extensions *mExtensions; public: Context() : mExtensions (0) {} virtual ~Context() {} virtual bool canDeclareLocals() const = 0; ///< Is the compiler allowed to declare local variables? void setExtensions (const Extensions *extensions = 0) { mExtensions = extensions; } const Extensions *getExtensions() const { return mExtensions; } virtual char getGlobalType (const std::string& name) const = 0; ///< 'l: long, 's': short, 'f': float, ' ': does not exist. virtual std::pair getMemberType (const std::string& name, const std::string& id) const = 0; ///< Return type of member variable \a name in script \a id or in script of reference of /// \a id /// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist. /// second: true: script of reference virtual bool isId (const std::string& name) const = 0; ///< Does \a name match an ID, that can be referenced? virtual bool isJournalId (const std::string& name) const = 0; ///< Does \a name match a journal ID? }; } #endif openmw-openmw-0.38.0/components/compiler/controlparser.cpp000066400000000000000000000201551264522266000240220ustar00rootroot00000000000000#include "controlparser.hpp" #include #include #include #include "scanner.hpp" #include "generator.hpp" #include "errorhandler.hpp" #include "skipparser.hpp" namespace Compiler { bool ControlParser::parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner) { if (keyword==Scanner::K_endif || keyword==Scanner::K_elseif || keyword==Scanner::K_else) { std::pair entry; if (mState!=IfElseBodyState) mExprParser.append (entry.first); std::copy (mCodeBlock.begin(), mCodeBlock.end(), std::back_inserter (entry.second)); mIfCode.push_back (entry); mCodeBlock.clear(); if (keyword==Scanner::K_endif) { // store code for if-cascade Codes codes; for (IfCodes::reverse_iterator iter (mIfCode.rbegin()); iter!=mIfCode.rend(); ++iter) { Codes block; if (iter!=mIfCode.rbegin()) Generator::jump (iter->second, codes.size()+1); if (!iter->first.empty()) { // if or elseif std::copy (iter->first.begin(), iter->first.end(), std::back_inserter (block)); Generator::jumpOnZero (block, iter->second.size()+1); } std::copy (iter->second.begin(), iter->second.end(), std::back_inserter (block)); std::swap (codes, block); std::copy (block.begin(), block.end(), std::back_inserter (codes)); } std::copy (codes.begin(), codes.end(), std::back_inserter (mCode)); mIfCode.clear(); mState = IfEndifState; } else if (keyword==Scanner::K_elseif) { mExprParser.reset(); scanner.scan (mExprParser); mState = IfElseifEndState; } else if (keyword==Scanner::K_else) { mState = IfElseJunkState; /// \todo should be IfElseEndState; add an option for that } return true; } else if (keyword==Scanner::K_if || keyword==Scanner::K_while) { // nested ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals); if (parser.parseKeyword (keyword, loc, scanner)) scanner.scan (parser); parser.appendCode (mCodeBlock); return true; } else { mLineParser.reset(); if (mLineParser.parseKeyword (keyword, loc, scanner)) scanner.scan (mLineParser); return true; } } bool ControlParser::parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner) { if (keyword==Scanner::K_endwhile) { Codes loop; Codes expr; mExprParser.append (expr); Generator::jump (loop, -static_cast (mCodeBlock.size()+expr.size())); std::copy (expr.begin(), expr.end(), std::back_inserter (mCode)); Codes skip; Generator::jumpOnZero (skip, mCodeBlock.size()+loop.size()+1); std::copy (skip.begin(), skip.end(), std::back_inserter (mCode)); std::copy (mCodeBlock.begin(), mCodeBlock.end(), std::back_inserter (mCode)); Codes loop2; Generator::jump (loop2, -static_cast (mCodeBlock.size()+expr.size()+skip.size())); if (loop.size()!=loop2.size()) throw std::logic_error ( "internal compiler error: failed to generate a while loop"); std::copy (loop2.begin(), loop2.end(), std::back_inserter (mCode)); mState = WhileEndwhileState; return true; } else if (keyword==Scanner::K_if || keyword==Scanner::K_while) { // nested ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals); if (parser.parseKeyword (keyword, loc, scanner)) scanner.scan (parser); parser.appendCode (mCodeBlock); return true; } else { mLineParser.reset(); if (mLineParser.parseKeyword (keyword, loc, scanner)) scanner.scan (mLineParser); return true; } } ControlParser::ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mLineParser (errorHandler, context, locals, literals, mCodeBlock), mExprParser (errorHandler, context, locals, literals), mState (StartState) { } void ControlParser::appendCode (std::vector& code) const { std::copy (mCode.begin(), mCode.end(), std::back_inserter (code)); } bool ControlParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState || mState==WhileBodyState) { scanner.putbackName (name, loc); mLineParser.reset(); scanner.scan (mLineParser); return true; } return Parser::parseName (name, loc, scanner); } bool ControlParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (mState==StartState) { if (keyword==Scanner::K_if || keyword==Scanner::K_elseif) { if (keyword==Scanner::K_elseif) getErrorHandler().warning ("elseif without matching if", loc); mExprParser.reset(); scanner.scan (mExprParser); mState = IfEndState; return true; } else if (keyword==Scanner::K_while) { mExprParser.reset(); scanner.scan (mExprParser); mState = WhileEndState; return true; } } else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState || mState==IfElseJunkState) { if (parseIfBody (keyword, loc, scanner)) return true; } else if (mState==WhileBodyState) { if ( parseWhileBody (keyword, loc, scanner)) return true; } return Parser::parseKeyword (keyword, loc, scanner); } bool ControlParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_newline) { switch (mState) { case IfEndState: mState = IfBodyState; return true; case IfElseifEndState: mState = IfElseifBodyState; return true; case IfElseEndState: mState = IfElseBodyState; return true; case IfElseJunkState: mState = IfElseBodyState; return true; case WhileEndState: mState = WhileBodyState; return true; case IfBodyState: case IfElseifBodyState: case IfElseBodyState: case WhileBodyState: return true; // empty line case IfEndifState: case WhileEndwhileState: return false; default: ; } } else if (code==Scanner::S_open && mState==IfElseJunkState) { SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); mState = IfElseBodyState; return true; } return Parser::parseSpecial (code, loc, scanner); } void ControlParser::reset() { mCode.clear(); mCodeBlock.clear(); mIfCode.clear(); mState = StartState; Parser::reset(); } } openmw-openmw-0.38.0/components/compiler/controlparser.hpp000066400000000000000000000042711264522266000240300ustar00rootroot00000000000000#ifndef COMPILER_CONTROLPARSER_H_INCLUDED #define COMPILER_CONTROLPARSER_H_INCLUDED #include #include #include "parser.hpp" #include "exprparser.hpp" #include "lineparser.hpp" namespace Compiler { class Locals; class Literals; // Control structure parser class ControlParser : public Parser { enum State { StartState, IfEndState, IfBodyState, IfElseifEndState, IfElseifBodyState, IfElseEndState, IfElseBodyState, IfEndifState, WhileEndState, WhileBodyState, WhileEndwhileState, IfElseJunkState }; typedef std::vector Codes; typedef std::vector > IfCodes; Locals& mLocals; Literals& mLiterals; Codes mCode; Codes mCodeBlock; IfCodes mIfCode; // condition, body LineParser mLineParser; ExprParser mExprParser; State mState; bool parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner); bool parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner); public: ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals); void appendCode (std::vector& code) const; ///< store generated code in \a code. virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? void reset(); ///< Reset parser to clean state. }; } #endif openmw-openmw-0.38.0/components/compiler/declarationparser.cpp000066400000000000000000000042151264522266000246260ustar00rootroot00000000000000#include "declarationparser.hpp" #include #include "scanner.hpp" #include "errorhandler.hpp" #include "skipparser.hpp" #include "locals.hpp" Compiler::DeclarationParser::DeclarationParser (ErrorHandler& errorHandler, const Context& context, Locals& locals) : Parser (errorHandler, context), mLocals (locals), mState (State_Begin), mType (0) {} bool Compiler::DeclarationParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (mState==State_Name) { std::string name2 = ::Misc::StringUtils::lowerCase (name); char type = mLocals.getType (name2); if (type!=' ') { /// \todo add option to make re-declared local variables an error getErrorHandler().warning ("can't re-declare local variable (ignoring declaration)", loc); mState = State_End; return true; } mLocals.declare (mType, name2); mState = State_End; return true; } return Parser::parseName (name, loc, scanner); } bool Compiler::DeclarationParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (mState==State_Begin) { switch (keyword) { case Scanner::K_short: mType = 's'; break; case Scanner::K_long: mType = 'l'; break; case Scanner::K_float: mType = 'f'; break; default: mType = 0; } if (mType) { mState = State_Name; return true; } } else if (mState==State_Name) { // allow keywords to be used as local variable names. MW script compiler, you suck! /// \todo option to disable this atrocity. return parseName (loc.mLiteral, loc, scanner); } return Parser::parseKeyword (keyword, loc, scanner); } bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_newline && mState==State_End) return false; return Parser::parseSpecial (code, loc, scanner); } void Compiler::DeclarationParser::reset() { mState = State_Begin; } openmw-openmw-0.38.0/components/compiler/declarationparser.hpp000066400000000000000000000021361264522266000246330ustar00rootroot00000000000000#ifndef COMPILER_DECLARATIONPARSER_H_INCLUDED #define COMPILER_DECLARATIONPARSER_H_INCLUDED #include "parser.hpp" namespace Compiler { class Locals; class DeclarationParser : public Parser { enum State { State_Begin, State_Name, State_End }; Locals& mLocals; State mState; char mType; public: DeclarationParser (ErrorHandler& errorHandler, const Context& context, Locals& locals); virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? void reset(); }; } #endif openmw-openmw-0.38.0/components/compiler/discardparser.cpp000066400000000000000000000032241264522266000237510ustar00rootroot00000000000000#include "discardparser.hpp" #include "scanner.hpp" namespace Compiler { DiscardParser::DiscardParser (ErrorHandler& errorHandler, const Context& context) : Parser (errorHandler, context), mState (StartState) { } bool DiscardParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) { if (mState==StartState || mState==CommaState || mState==MinusState) { start(); return false; } return Parser::parseInt (value, loc, scanner); } bool DiscardParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) { if (mState==StartState || mState==CommaState || mState==MinusState) { start(); return false; } return Parser::parseFloat (value, loc, scanner); } bool DiscardParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (mState==StartState || mState==CommaState) { start(); return false; } return Parser::parseName (name, loc, scanner); } bool DiscardParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_comma && mState==StartState) { mState = CommaState; return true; } if (code==Scanner::S_minus && (mState==StartState || mState==CommaState)) { mState = MinusState; return true; } return Parser::parseSpecial (code, loc, scanner); } void DiscardParser::reset() { mState = StartState; Parser::reset(); } } openmw-openmw-0.38.0/components/compiler/discardparser.hpp000066400000000000000000000024401264522266000237550ustar00rootroot00000000000000#ifndef COMPILER_DISCARDPARSER_H_INCLUDED #define COMPILER_DISCARDPARSER_H_INCLUDED #include "parser.hpp" namespace Compiler { /// \brief Parse a single optional numeric value or string and discard it class DiscardParser : public Parser { enum State { StartState, CommaState, MinusState }; State mState; public: DiscardParser (ErrorHandler& errorHandler, const Context& context); virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. /// \return fetch another token? virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); ///< Handle a float token. /// \return fetch another token? virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? virtual void reset(); ///< Reset parser to clean state. }; } #endif openmw-openmw-0.38.0/components/compiler/errorhandler.cpp000066400000000000000000000040501264522266000236100ustar00rootroot00000000000000#include "errorhandler.hpp" namespace Compiler { ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0), mWarningsMode (1), mDowngradeErrors (false) {} ErrorHandler::~ErrorHandler() {} // Was compiling successful? bool ErrorHandler::isGood() const { return mErrors==0; } // Return number of errors int ErrorHandler::countErrors() const { return mErrors; } // Return number of warnings int ErrorHandler::countWarnings() const { return mWarnings; } // Generate a warning message. void ErrorHandler::warning (const std::string& message, const TokenLoc& loc) { if (mWarningsMode==1 || // temporarily change from mode 2 to mode 1 if error downgrading is enabled to // avoid infinite recursion (mWarningsMode==2 && mDowngradeErrors)) { ++mWarnings; report (message, loc, WarningMessage); } else if (mWarningsMode==2) error (message, loc); } // Generate an error message. void ErrorHandler::error (const std::string& message, const TokenLoc& loc) { if (mDowngradeErrors) { warning (message, loc); return; } ++mErrors; report (message, loc, ErrorMessage); } // Generate an error message for an unexpected EOF. void ErrorHandler::endOfFile() { ++mErrors; report ("unexpected end of file", ErrorMessage); } // Remove all previous error/warning events void ErrorHandler::reset() { mErrors = mWarnings = 0; } void ErrorHandler::setWarningsMode (int mode) { mWarningsMode = mode; } void ErrorHandler::downgradeErrors (bool downgrade) { mDowngradeErrors = downgrade; } ErrorDowngrade::ErrorDowngrade (ErrorHandler& handler) : mHandler (handler) { mHandler.downgradeErrors (true); } ErrorDowngrade::~ErrorDowngrade() { mHandler.downgradeErrors (false); } } openmw-openmw-0.38.0/components/compiler/errorhandler.hpp000066400000000000000000000042741264522266000236250ustar00rootroot00000000000000#ifndef COMPILER_ERRORHANDLER_H_INCLUDED #define COMPILER_ERRORHANDLER_H_INCLUDED #include namespace Compiler { struct TokenLoc; /// \brief Error handling /// /// This class collects errors and provides an interface for reporting them to the user. class ErrorHandler { int mWarnings; int mErrors; int mWarningsMode; bool mDowngradeErrors; protected: enum Type { WarningMessage, ErrorMessage }; private: // mutators virtual void report (const std::string& message, const TokenLoc& loc, Type type) = 0; ///< Report error to the user. virtual void report (const std::string& message, Type type) = 0; ///< Report a file related error public: ErrorHandler(); ///< constructor virtual ~ErrorHandler(); ///< destructor bool isGood() const; ///< Was compiling successful? int countErrors() const; ///< Return number of errors int countWarnings() const; ///< Return number of warnings void warning (const std::string& message, const TokenLoc& loc); ///< Generate a warning message. void error (const std::string& message, const TokenLoc& loc); ///< Generate an error message. void endOfFile(); ///< Generate an error message for an unexpected EOF. virtual void reset(); ///< Remove all previous error/warning events void setWarningsMode (int mode); ///< // 0 ignore, 1 rate as warning, 2 rate as error /// Treat errors as warnings. void downgradeErrors (bool downgrade); }; class ErrorDowngrade { ErrorHandler& mHandler; /// not implemented ErrorDowngrade (const ErrorDowngrade&); /// not implemented ErrorDowngrade& operator= (const ErrorDowngrade&); public: ErrorDowngrade (ErrorHandler& handler); ~ErrorDowngrade(); }; } #endif openmw-openmw-0.38.0/components/compiler/exception.hpp000066400000000000000000000016121264522266000231250ustar00rootroot00000000000000#ifndef COMPILER_EXCEPTION_H_INCLUDED #define COMPILER_EXCEPTION_H_INCLUDED #include namespace Compiler { /// \brief Exception: Error while parsing the source class SourceException : public std::exception { public: virtual const char *what() const throw() { return "compile error";} ///< Return error message }; /// \brief Exception: File error class FileException : public SourceException { public: virtual const char *what() const throw() { return "can't read file"; } ///< Return error message }; /// \brief Exception: EOF condition encountered class EOFException : public SourceException { public: virtual const char *what() const throw() { return "end of file"; } ///< Return error message }; } #endif openmw-openmw-0.38.0/components/compiler/exprparser.cpp000066400000000000000000000577641264522266000233400ustar00rootroot00000000000000#include "exprparser.hpp" #include #include #include #include #include #include #include "generator.hpp" #include "scanner.hpp" #include "errorhandler.hpp" #include "locals.hpp" #include "stringparser.hpp" #include "extensions.hpp" #include "context.hpp" #include "discardparser.hpp" #include "junkparser.hpp" namespace Compiler { int ExprParser::getPriority (char op) const { switch (op) { case '(': return 0; case 'e': // == case 'n': // != case 'l': // < case 'L': // <= case 'g': // < case 'G': // >= return 1; case '+': case '-': return 2; case '*': case '/': return 3; case 'm': return 4; } return 0; } char ExprParser::getOperandType (int Index) const { assert (!mOperands.empty()); assert (Index>=0); assert (Index (mOperands.size())); return mOperands[mOperands.size()-1-Index]; } char ExprParser::getOperator() const { assert (!mOperators.empty()); return mOperators[mOperators.size()-1]; } bool ExprParser::isOpen() const { return std::find (mOperators.begin(), mOperators.end(), '(')!=mOperators.end(); } void ExprParser::popOperator() { assert (!mOperators.empty()); mOperators.resize (mOperators.size()-1); } void ExprParser::popOperand() { assert (!mOperands.empty()); mOperands.resize (mOperands.size()-1); } void ExprParser::replaceBinaryOperands() { char t1 = getOperandType (1); char t2 = getOperandType(); popOperand(); popOperand(); if (t1==t2) mOperands.push_back (t1); else if (t1=='f' || t2=='f') mOperands.push_back ('f'); else throw std::logic_error ("failed to determine result operand type"); } void ExprParser::pop() { char op = getOperator(); switch (op) { case 'm': Generator::negate (mCode, getOperandType()); popOperator(); break; case '+': Generator::add (mCode, getOperandType (1), getOperandType()); popOperator(); replaceBinaryOperands(); break; case '-': Generator::sub (mCode, getOperandType (1), getOperandType()); popOperator(); replaceBinaryOperands(); break; case '*': Generator::mul (mCode, getOperandType (1), getOperandType()); popOperator(); replaceBinaryOperands(); break; case '/': Generator::div (mCode, getOperandType (1), getOperandType()); popOperator(); replaceBinaryOperands(); break; case 'e': case 'n': case 'l': case 'L': case 'g': case 'G': Generator::compare (mCode, op, getOperandType (1), getOperandType()); popOperator(); popOperand(); popOperand(); mOperands.push_back ('l'); break; default: throw std::logic_error ("unknown operator"); } } void ExprParser::pushIntegerLiteral (int value) { mNextOperand = false; mOperands.push_back ('l'); Generator::pushInt (mCode, mLiterals, value); } void ExprParser::pushFloatLiteral (float value) { mNextOperand = false; mOperands.push_back ('f'); Generator::pushFloat (mCode, mLiterals, value); } void ExprParser::pushBinaryOperator (char c) { while (!mOperators.empty() && getPriority (getOperator())>=getPriority (c)) pop(); mOperators.push_back (c); mNextOperand = true; } void ExprParser::close() { while (getOperator()!='(') pop(); popOperator(); } int ExprParser::parseArguments (const std::string& arguments, Scanner& scanner) { return parseArguments (arguments, scanner, mCode); } bool ExprParser::handleMemberAccess (const std::string& name) { mMemberOp = false; std::string name2 = Misc::StringUtils::lowerCase (name); std::string id = Misc::StringUtils::lowerCase (mExplicit); std::pair type = getContext().getMemberType (name2, id); if (type.first!=' ') { Generator::fetchMember (mCode, mLiterals, type.first, name2, id, !type.second); mNextOperand = false; mExplicit.clear(); mOperands.push_back (type.first=='f' ? 'f' : 'l'); return true; } return false; } ExprParser::ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, bool argument) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mNextOperand (true), mFirst (true), mArgument (argument), mRefOp (false), mMemberOp (false) {} bool ExprParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) { if (!mExplicit.empty()) return Parser::parseInt (value, loc, scanner); mFirst = false; if (mNextOperand) { start(); pushIntegerLiteral (value); mTokenLoc = loc; return true; } else { // no comma was used between arguments scanner.putbackInt (value, loc); return false; } } bool ExprParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) { if (!mExplicit.empty()) return Parser::parseFloat (value, loc, scanner); mFirst = false; if (mNextOperand) { start(); pushFloatLiteral (value); mTokenLoc = loc; return true; } else { // no comma was used between arguments scanner.putbackFloat (value, loc); return false; } } bool ExprParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (!mExplicit.empty()) { if (mMemberOp && handleMemberAccess (name)) return true; return Parser::parseName (name, loc, scanner); } mFirst = false; if (mNextOperand) { start(); std::string name2 = Misc::StringUtils::lowerCase (name); char type = mLocals.getType (name2); if (type!=' ') { Generator::fetchLocal (mCode, type, mLocals.getIndex (name2)); mNextOperand = false; mOperands.push_back (type=='f' ? 'f' : 'l'); return true; } type = getContext().getGlobalType (name2); if (type!=' ') { Generator::fetchGlobal (mCode, mLiterals, type, name2); mNextOperand = false; mOperands.push_back (type=='f' ? 'f' : 'l'); return true; } // die in a fire, Morrowind script compiler! if (const Extensions *extensions = getContext().getExtensions()) { if (getContext().isJournalId (name2)) { // JournalID used as an argument. Use the index of that JournalID Generator::pushString (mCode, mLiterals, name2); int keyword = extensions->searchKeyword ("getjournalindex"); extensions->generateFunctionCode (keyword, mCode, mLiterals, mExplicit, 0); mNextOperand = false; mOperands.push_back ('l'); return true; } } if (mExplicit.empty() && getContext().isId (name2)) { mExplicit = name2; return true; } } else { // no comma was used between arguments scanner.putbackName (name, loc); return false; } return Parser::parseName (name, loc, scanner); } bool ExprParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (const Extensions *extensions = getContext().getExtensions()) { std::string argumentType; // ignored bool hasExplicit = false; // ignored if (extensions->isInstruction (keyword, argumentType, hasExplicit)) { // pretend this is not a keyword std::string name = loc.mLiteral; if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"') name = name.substr (1, name.size()-2); return parseName (name, loc, scanner); } } if (keyword==Scanner::K_end || keyword==Scanner::K_begin || keyword==Scanner::K_short || keyword==Scanner::K_long || keyword==Scanner::K_float || keyword==Scanner::K_if || keyword==Scanner::K_endif || keyword==Scanner::K_else || keyword==Scanner::K_elseif || keyword==Scanner::K_while || keyword==Scanner::K_endwhile || keyword==Scanner::K_return || keyword==Scanner::K_messagebox || keyword==Scanner::K_set || keyword==Scanner::K_to || keyword==Scanner::K_startscript || keyword==Scanner::K_stopscript || keyword==Scanner::K_enable || keyword==Scanner::K_disable) { return parseName (loc.mLiteral, loc, scanner); } mFirst = false; if (!mExplicit.empty()) { if (mRefOp && mNextOperand) { if (keyword==Scanner::K_getdisabled) { start(); mTokenLoc = loc; Generator::getDisabled (mCode, mLiterals, mExplicit); mOperands.push_back ('l'); mExplicit.clear(); mRefOp = false; std::vector ignore; parseArguments ("x", scanner, ignore); mNextOperand = false; return true; } else if (keyword==Scanner::K_getdistance) { start(); mTokenLoc = loc; parseArguments ("c", scanner); Generator::getDistance (mCode, mLiterals, mExplicit); mOperands.push_back ('f'); mExplicit.clear(); mRefOp = false; mNextOperand = false; return true; } else if (keyword==Scanner::K_scriptrunning) { start(); mTokenLoc = loc; parseArguments ("c", scanner); Generator::scriptRunning (mCode); mOperands.push_back ('l'); mExplicit.clear(); mRefOp = false; mNextOperand = false; return true; } // check for custom extensions if (const Extensions *extensions = getContext().getExtensions()) { char returnType; std::string argumentType; bool hasExplicit = true; if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit)) { if (!hasExplicit) { getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); mExplicit.clear(); } start(); mTokenLoc = loc; int optionals = parseArguments (argumentType, scanner); extensions->generateFunctionCode (keyword, mCode, mLiterals, mExplicit, optionals); mOperands.push_back (returnType); mExplicit.clear(); mRefOp = false; mNextOperand = false; return true; } } } return Parser::parseKeyword (keyword, loc, scanner); } if (mNextOperand) { if (keyword==Scanner::K_getsquareroot) { start(); mTokenLoc = loc; parseArguments ("f", scanner); Generator::squareRoot (mCode); mOperands.push_back ('f'); mNextOperand = false; return true; } else if (keyword==Scanner::K_menumode) { start(); mTokenLoc = loc; Generator::menuMode (mCode); mOperands.push_back ('l'); mNextOperand = false; return true; } else if (keyword==Scanner::K_random) { start(); mTokenLoc = loc; parseArguments ("l", scanner); Generator::random (mCode); mOperands.push_back ('l'); mNextOperand = false; return true; } else if (keyword==Scanner::K_scriptrunning) { start(); mTokenLoc = loc; parseArguments ("c", scanner); Generator::scriptRunning (mCode); mOperands.push_back ('l'); mNextOperand = false; return true; } else if (keyword==Scanner::K_getdistance) { start(); mTokenLoc = loc; parseArguments ("c", scanner); Generator::getDistance (mCode, mLiterals, ""); mOperands.push_back ('f'); mNextOperand = false; return true; } else if (keyword==Scanner::K_getsecondspassed) { start(); mTokenLoc = loc; Generator::getSecondsPassed (mCode); mOperands.push_back ('f'); mNextOperand = false; return true; } else if (keyword==Scanner::K_getdisabled) { start(); mTokenLoc = loc; Generator::getDisabled (mCode, mLiterals, ""); mOperands.push_back ('l'); std::vector ignore; parseArguments ("x", scanner, ignore); mNextOperand = false; return true; } else { // check for custom extensions if (const Extensions *extensions = getContext().getExtensions()) { start(); char returnType; std::string argumentType; bool hasExplicit = false; if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit)) { mTokenLoc = loc; int optionals = parseArguments (argumentType, scanner); extensions->generateFunctionCode (keyword, mCode, mLiterals, "", optionals); mOperands.push_back (returnType); mNextOperand = false; return true; } } } } else { // no comma was used between arguments scanner.putbackKeyword (keyword, loc); return false; } return Parser::parseKeyword (keyword, loc, scanner); } bool ExprParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (!mExplicit.empty()) { if (mRefOp && code==Scanner::S_open) { /// \todo add option to disable this workaround mOperators.push_back ('('); mTokenLoc = loc; return true; } if (!mRefOp && code==Scanner::S_ref) { mRefOp = true; return true; } if (!mMemberOp && code==Scanner::S_member) { mMemberOp = true; return true; } return Parser::parseSpecial (code, loc, scanner); } if (code==Scanner::S_comma) { mTokenLoc = loc; if (mFirst) { // leading comma mFirst = false; return true; } // end marker scanner.putbackSpecial (code, loc); return false; } mFirst = false; if (code==Scanner::S_newline) { // end marker mTokenLoc = loc; scanner.putbackSpecial (code, loc); return false; } if (code==Scanner::S_minus && mNextOperand) { // unary mOperators.push_back ('m'); mTokenLoc = loc; return true; } if (code ==Scanner::S_plus && mNextOperand) { // Also unary, but +, just ignore it mTokenLoc = loc; return true; } if (code==Scanner::S_open) { if (mNextOperand) { mOperators.push_back ('('); mTokenLoc = loc; return true; } else { // no comma was used between arguments scanner.putbackSpecial (code, loc); return false; } } if (code==Scanner::S_close && !mNextOperand) { if (isOpen()) { close(); return true; } mTokenLoc = loc; scanner.putbackSpecial (code, loc); return false; } if (!mNextOperand) { mTokenLoc = loc; char c = 0; // comparison switch (code) { case Scanner::S_plus: c = '+'; break; case Scanner::S_minus: c = '-'; break; case Scanner::S_mult: pushBinaryOperator ('*'); return true; case Scanner::S_div: pushBinaryOperator ('/'); return true; case Scanner::S_cmpEQ: c = 'e'; break; case Scanner::S_cmpNE: c = 'n'; break; case Scanner::S_cmpLT: c = 'l'; break; case Scanner::S_cmpLE: c = 'L'; break; case Scanner::S_cmpGT: c = 'g'; break; case Scanner::S_cmpGE: c = 'G'; break; } if (c) { if (mArgument && !isOpen()) { // expression ends here // Thank you Morrowind for this rotten syntax :( scanner.putbackSpecial (code, loc); return false; } pushBinaryOperator (c); return true; } } return Parser::parseSpecial (code, loc, scanner); } void ExprParser::reset() { mOperands.clear(); mOperators.clear(); mNextOperand = true; mCode.clear(); mFirst = true; mExplicit.clear(); mRefOp = false; mMemberOp = false; Parser::reset(); } char ExprParser::append (std::vector& code) { if (mOperands.empty() && mOperators.empty()) { getErrorHandler().error ("missing expression", mTokenLoc); return 'l'; } if (mNextOperand || mOperands.empty()) { getErrorHandler().error ("syntax error in expression", mTokenLoc); return 'l'; } while (!mOperators.empty()) pop(); std::copy (mCode.begin(), mCode.end(), std::back_inserter (code)); assert (mOperands.size()==1); return mOperands[0]; } int ExprParser::parseArguments (const std::string& arguments, Scanner& scanner, std::vector& code, int ignoreKeyword) { bool optional = false; int optionalCount = 0; ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true); StringParser stringParser (getErrorHandler(), getContext(), mLiterals); DiscardParser discardParser (getErrorHandler(), getContext()); JunkParser junkParser (getErrorHandler(), getContext(), ignoreKeyword); std::stack > stack; for (std::string::const_iterator iter (arguments.begin()); iter!=arguments.end(); ++iter) { if (*iter=='/') { optional = true; } else if (*iter=='S' || *iter=='c' || *iter=='x') { stringParser.reset(); if (optional || *iter=='x') stringParser.setOptional (true); if (*iter=='c') stringParser.smashCase(); scanner.scan (stringParser); if (optional && stringParser.isEmpty()) break; if (*iter!='x') { std::vector tmp; stringParser.append (tmp); stack.push (tmp); if (optional) ++optionalCount; } else getErrorHandler().warning("Ignoring extra argument", mTokenLoc); } else if (*iter=='X') { parser.reset(); parser.setOptional (true); scanner.scan (parser); if (parser.isEmpty()) break; else getErrorHandler().warning("Ignoring extra argument", mTokenLoc); } else if (*iter=='z') { discardParser.reset(); discardParser.setOptional (true); scanner.scan (discardParser); if (discardParser.isEmpty()) break; else getErrorHandler().warning("Ignoring extra argument", mTokenLoc); } else if (*iter=='j') { /// \todo disable this when operating in strict mode junkParser.reset(); scanner.scan (junkParser); } else { parser.reset(); if (optional) parser.setOptional (true); scanner.scan (parser); if (optional && parser.isEmpty()) break; std::vector tmp; char type = parser.append (tmp); if (type!=*iter) Generator::convert (tmp, type, *iter); stack.push (tmp); if (optional) ++optionalCount; } } while (!stack.empty()) { std::vector& tmp = stack.top(); std::copy (tmp.begin(), tmp.end(), std::back_inserter (code)); stack.pop(); } return optionalCount; } } openmw-openmw-0.38.0/components/compiler/exprparser.hpp000066400000000000000000000066351264522266000233340ustar00rootroot00000000000000#ifndef COMPILER_EXPRPARSER_H_INCLUDED #define COMPILER_EXPRPARSER_H_INCLUDED #include #include #include "parser.hpp" #include "tokenloc.hpp" namespace Compiler { class Locals; class Literals; class ExprParser : public Parser { Locals& mLocals; Literals& mLiterals; std::vector mOperands; std::vector mOperators; bool mNextOperand; TokenLoc mTokenLoc; std::vector mCode; bool mFirst; bool mArgument; std::string mExplicit; bool mRefOp; bool mMemberOp; int getPriority (char op) const; char getOperandType (int Index = 0) const; char getOperator() const; bool isOpen() const; void popOperator(); void popOperand(); void replaceBinaryOperands(); void pop(); void pushIntegerLiteral (int value); void pushFloatLiteral (float value); void pushBinaryOperator (char c); void close(); int parseArguments (const std::string& arguments, Scanner& scanner); bool handleMemberAccess (const std::string& name); public: ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, bool argument = false); ///< constructor /// \param argument Parser is used to parse function- or instruction- /// arguments (this influences the precedence rules). char getType() const; ///< Return type of parsed expression ('l' integer, 'f' float) virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. /// \return fetch another token? virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); ///< Handle a float token. /// \return fetch another token? virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? void reset(); ///< Reset parser to clean state. char append (std::vector& code); ///< Generate code for parsed expression. /// \return Type ('l': integer, 'f': float) int parseArguments (const std::string& arguments, Scanner& scanner, std::vector& code, int ignoreKeyword = -1); ///< Parse sequence of arguments specified by \a arguments. /// \param arguments Uses ScriptArgs typedef /// \see Compiler::ScriptArgs /// \param invert Store arguments in reverted order. /// \param ignoreKeyword A keyword that is seen as junk /// \return number of optional arguments }; } #endif openmw-openmw-0.38.0/components/compiler/extensions.cpp000066400000000000000000000153141264522266000233250ustar00rootroot00000000000000#include "extensions.hpp" #include #include #include "generator.hpp" #include "literals.hpp" namespace Compiler { Extensions::Extensions() : mNextKeywordIndex (-1) {} int Extensions::searchKeyword (const std::string& keyword) const { std::map::const_iterator iter = mKeywords.find (keyword); if (iter==mKeywords.end()) return 0; return iter->second; } bool Extensions::isFunction (int keyword, ScriptReturn& returnType, ScriptArgs& argumentType, bool& explicitReference) const { std::map::const_iterator iter = mFunctions.find (keyword); if (iter==mFunctions.end()) return false; if (explicitReference && iter->second.mCodeExplicit==-1) explicitReference = false; returnType = iter->second.mReturn; argumentType = iter->second.mArguments; return true; } bool Extensions::isInstruction (int keyword, ScriptArgs& argumentType, bool& explicitReference) const { std::map::const_iterator iter = mInstructions.find (keyword); if (iter==mInstructions.end()) return false; if (explicitReference && iter->second.mCodeExplicit==-1) explicitReference = false; argumentType = iter->second.mArguments; return true; } void Extensions::registerFunction (const std::string& keyword, ScriptReturn returnType, const ScriptArgs& argumentType, int code, int codeExplicit) { Function function; if (argumentType.find ('/')==std::string::npos) { function.mSegment = 5; assert (code>=33554432 && code<=67108863); assert (codeExplicit==-1 || (codeExplicit>=33554432 && codeExplicit<=67108863)); } else { function.mSegment = 3; assert (code>=0x20000 && code<=0x2ffff); assert (codeExplicit==-1 || (codeExplicit>=0x20000 && codeExplicit<=0x2ffff)); } int keywordIndex = mNextKeywordIndex--; mKeywords.insert (std::make_pair (keyword, keywordIndex)); function.mReturn = returnType; function.mArguments = argumentType; function.mCode = code; function.mCodeExplicit = codeExplicit; mFunctions.insert (std::make_pair (keywordIndex, function)); } void Extensions::registerInstruction (const std::string& keyword, const ScriptArgs& argumentType, int code, int codeExplicit) { Instruction instruction; if (argumentType.find ('/')==std::string::npos) { instruction.mSegment = 5; assert (code>=33554432 && code<=67108863); assert (codeExplicit==-1 || (codeExplicit>=33554432 && codeExplicit<=67108863)); } else { instruction.mSegment = 3; assert (code>=0x20000 && code<=0x2ffff); assert (codeExplicit==-1 || (codeExplicit>=0x20000 && codeExplicit<=0x2ffff)); } int keywordIndex = mNextKeywordIndex--; mKeywords.insert (std::make_pair (keyword, keywordIndex)); instruction.mArguments = argumentType; instruction.mCode = code; instruction.mCodeExplicit = codeExplicit; mInstructions.insert (std::make_pair (keywordIndex, instruction)); } void Extensions::generateFunctionCode (int keyword, std::vector& code, Literals& literals, const std::string& id, int optionalArguments) const { assert (optionalArguments>=0); std::map::const_iterator iter = mFunctions.find (keyword); if (iter==mFunctions.end()) throw std::logic_error ("unknown custom function keyword"); if (optionalArguments && iter->second.mSegment!=3) throw std::logic_error ("functions with optional arguments must be placed into segment 3"); if (!id.empty()) { if (iter->second.mCodeExplicit==-1) throw std::logic_error ("explicit references not supported"); int index = literals.addString (id); Generator::pushInt (code, literals, index); } switch (iter->second.mSegment) { case 3: if (optionalArguments>=256) throw std::logic_error ("number of optional arguments is too large for segment 3"); code.push_back (Generator::segment3 ( id.empty() ? iter->second.mCode : iter->second.mCodeExplicit, optionalArguments)); break; case 5: code.push_back (Generator::segment5 ( id.empty() ? iter->second.mCode : iter->second.mCodeExplicit)); break; default: throw std::logic_error ("unsupported code segment"); } } void Extensions::generateInstructionCode (int keyword, std::vector& code, Literals& literals, const std::string& id, int optionalArguments) const { assert (optionalArguments>=0); std::map::const_iterator iter = mInstructions.find (keyword); if (iter==mInstructions.end()) throw std::logic_error ("unknown custom instruction keyword"); if (optionalArguments && iter->second.mSegment!=3) throw std::logic_error ("instructions with optional arguments must be placed into segment 3"); if (!id.empty()) { if (iter->second.mCodeExplicit==-1) throw std::logic_error ("explicit references not supported"); int index = literals.addString (id); Generator::pushInt (code, literals, index); } switch (iter->second.mSegment) { case 3: if (optionalArguments>=256) throw std::logic_error ("number of optional arguments is too large for segment 3"); code.push_back (Generator::segment3 ( id.empty() ? iter->second.mCode : iter->second.mCodeExplicit, optionalArguments)); break; case 5: code.push_back (Generator::segment5 ( id.empty() ? iter->second.mCode : iter->second.mCodeExplicit)); break; default: throw std::logic_error ("unsupported code segment"); } } void Extensions::listKeywords (std::vector& keywords) const { for (std::map::const_iterator iter (mKeywords.begin()); iter!=mKeywords.end(); ++iter) keywords.push_back (iter->first); } } openmw-openmw-0.38.0/components/compiler/extensions.hpp000066400000000000000000000111301264522266000233220ustar00rootroot00000000000000#ifndef COMPILER_EXTENSIONS_H_INCLUDED #define COMPILER_EXTENSIONS_H_INCLUDED #include #include #include #include namespace Compiler { class Literals; /// Typedef for script arguments string /** Every character reperesents an argument to the command. All arguments are required until a /, after which every argument is optional.
Eg: fff/f represents 3 required floats followed by one optional float
f - Float
c - String, case smashed
l - Integer
s - Short
S - String, case preserved
x - Optional, ignored string argument. Emits a parser warning when this argument is supplied.
X - Optional, ignored numeric expression. Emits a parser warning when this argument is supplied.
z - Optional, ignored string or numeric argument. Emits a parser warning when this argument is supplied.
j - A piece of junk (either . or a specific keyword) **/ typedef std::string ScriptArgs; /// Typedef for script return char /** The character represents the type of data being returned.
f - float
S - String (Cell names)
l - Integer **/ typedef char ScriptReturn; /// \brief Collection of compiler extensions class Extensions { struct Function { char mReturn; ScriptArgs mArguments; int mCode; int mCodeExplicit; int mSegment; }; struct Instruction { ScriptArgs mArguments; int mCode; int mCodeExplicit; int mSegment; }; int mNextKeywordIndex; std::map mKeywords; std::map mFunctions; std::map mInstructions; public: Extensions(); int searchKeyword (const std::string& keyword) const; ///< Return extension keyword code, that is assigned to the string \a keyword. /// - if no match is found 0 is returned. /// - keyword must be all lower case. bool isFunction (int keyword, ScriptReturn& returnType, ScriptArgs& argumentType, bool& explicitReference) const; ///< Is this keyword registered with a function? If yes, return return and argument /// types. /// \param explicitReference In: has explicit reference; Out: set to false, if /// explicit reference is not available for this instruction. bool isInstruction (int keyword, ScriptArgs& argumentType, bool& explicitReference) const; ///< Is this keyword registered with a function? If yes, return argument types. /// \param explicitReference In: has explicit reference; Out: set to false, if /// explicit reference is not available for this instruction. void registerFunction (const std::string& keyword, ScriptReturn returnType, const ScriptArgs& argumentType, int code, int codeExplicit = -1); ///< Register a custom function /// - keyword must be all lower case. /// - keyword must be unique /// - if explicit references are not supported, segment5codeExplicit must be set to -1 /// \note Currently only segment 3 and segment 5 opcodes are supported. void registerInstruction (const std::string& keyword, const ScriptArgs& argumentType, int code, int codeExplicit = -1); ///< Register a custom instruction /// - keyword must be all lower case. /// - keyword must be unique /// - if explicit references are not supported, segment5codeExplicit must be set to -1 /// \note Currently only segment 3 and segment 5 opcodes are supported. void generateFunctionCode (int keyword, std::vector& code, Literals& literals, const std::string& id, int optionalArguments) const; ///< Append code for function to \a code. void generateInstructionCode (int keyword, std::vector& code, Literals& literals, const std::string& id, int optionalArguments) const; ///< Append code for function to \a code. void listKeywords (std::vector& keywords) const; ///< Append all known keywords to \a kaywords. }; } #endif openmw-openmw-0.38.0/components/compiler/extensions0.cpp000066400000000000000000001024531264522266000234060ustar00rootroot00000000000000#include "extensions0.hpp" #include "opcodes.hpp" #include "extensions.hpp" namespace Compiler { void registerExtensions (Extensions& extensions, bool consoleOnly) { Ai::registerExtensions (extensions); Animation::registerExtensions (extensions); Cell::registerExtensions (extensions); Container::registerExtensions (extensions); Control::registerExtensions (extensions); Dialogue::registerExtensions (extensions); Gui::registerExtensions (extensions); Misc::registerExtensions (extensions); Sky::registerExtensions (extensions); Sound::registerExtensions (extensions); Stats::registerExtensions (extensions); Transformation::registerExtensions (extensions); if (consoleOnly) { Console::registerExtensions (extensions); User::registerExtensions (extensions); } } namespace Ai { void registerExtensions (Extensions& extensions) { extensions.registerInstruction ("aiactivate", "c/l", opcodeAIActivate, opcodeAIActivateExplicit); extensions.registerInstruction ("aitravel", "fff/l", opcodeAiTravel, opcodeAiTravelExplicit); extensions.registerInstruction ("aiescort", "cffff/l", opcodeAiEscort, opcodeAiEscortExplicit); extensions.registerInstruction ("aiescortcell", "ccffff/l", opcodeAiEscortCell, opcodeAiEscortCellExplicit); extensions.registerInstruction ("aiwander", "fff/llllllllll", opcodeAiWander, opcodeAiWanderExplicit); extensions.registerInstruction ("aifollow", "cffff/llllllll", opcodeAiFollow, opcodeAiFollowExplicit); extensions.registerInstruction ("aifollowcell", "ccffff/l", opcodeAiFollowCell, opcodeAiFollowCellExplicit); extensions.registerFunction ("getaipackagedone", 'l', "", opcodeGetAiPackageDone, opcodeGetAiPackageDoneExplicit); extensions.registerFunction ("getcurrentaipackage", 'l', "", opcodeGetCurrentAiPackage, opcodeGetAiPackageDoneExplicit); extensions.registerFunction ("getdetected", 'l', "c", opcodeGetDetected, opcodeGetDetectedExplicit); extensions.registerInstruction ("sethello", "l", opcodeSetHello, opcodeSetHelloExplicit); extensions.registerInstruction ("setfight", "l", opcodeSetFight, opcodeSetFightExplicit); extensions.registerInstruction ("setflee", "l", opcodeSetFlee, opcodeSetFleeExplicit); extensions.registerInstruction ("setalarm", "l", opcodeSetAlarm, opcodeSetAlarmExplicit); extensions.registerInstruction ("modhello", "l", opcodeModHello, opcodeModHelloExplicit); extensions.registerInstruction ("modfight", "l", opcodeModFight, opcodeModFightExplicit); extensions.registerInstruction ("modflee", "l", opcodeModFlee, opcodeModFleeExplicit); extensions.registerInstruction ("modalarm", "l", opcodeModAlarm, opcodeModAlarmExplicit); extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction ("tai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction("startcombat", "c", opcodeStartCombat, opcodeStartCombatExplicit); extensions.registerInstruction("stopcombat", "x", opcodeStopCombat, opcodeStopCombatExplicit); extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit); extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit); extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit); extensions.registerFunction ("getalarm", 'l', "", opcodeGetAlarm, opcodeGetAlarmExplicit); extensions.registerFunction ("getlineofsight", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction ("getlos", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction("gettarget", 'l', "c", opcodeGetTarget, opcodeGetTargetExplicit); extensions.registerInstruction("face", "llX", opcodeFace, opcodeFaceExplicit); } } namespace Animation { void registerExtensions (Extensions& extensions) { extensions.registerInstruction ("skipanim", "", opcodeSkipAnim, opcodeSkipAnimExplicit); extensions.registerInstruction ("playgroup", "c/l", opcodePlayAnim, opcodePlayAnimExplicit); extensions.registerInstruction ("loopgroup", "cl/l", opcodeLoopAnim, opcodeLoopAnimExplicit); } } namespace Cell { void registerExtensions (Extensions& extensions) { extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged); extensions.registerInstruction ("coc", "S", opcodeCOC); extensions.registerInstruction ("centeroncell", "S", opcodeCOC); extensions.registerInstruction ("coe", "ll", opcodeCOE); extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE); extensions.registerInstruction ("setwaterlevel", "f", opcodeSetWaterLevel); extensions.registerInstruction ("modwaterlevel", "f", opcodeModWaterLevel); extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior); extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell); extensions.registerFunction ("getwaterlevel", 'f', "", opcodeGetWaterLevel); } } namespace Console { void registerExtensions (Extensions& extensions) { } } namespace Container { void registerExtensions (Extensions& extensions) { extensions.registerInstruction ("additem", "clX", opcodeAddItem, opcodeAddItemExplicit); extensions.registerFunction ("getitemcount", 'l', "c", opcodeGetItemCount, opcodeGetItemCountExplicit); extensions.registerInstruction ("removeitem", "clX", opcodeRemoveItem, opcodeRemoveItemExplicit); extensions.registerInstruction ("equip", "cX", opcodeEquip, opcodeEquipExplicit); extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit); extensions.registerFunction ("hassoulgem", 'l', "c", opcodeHasSoulGem, opcodeHasSoulGemExplicit); extensions.registerFunction ("getweapontype", 'l', "", opcodeGetWeaponType, opcodeGetWeaponTypeExplicit); } } namespace Control { void registerExtensions (Extensions& extensions) { std::string enable ("enable"); std::string disable ("disable"); for (int i=0; i& code) const { mScriptParser.getCode (code); } const Locals& FileParser::getLocals() const { return mLocals; } bool FileParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (mState==NameState) { mName = name; mState = BeginCompleteState; return true; } if (mState==EndNameState) { // optional repeated name after end statement if (mName!=name) reportWarning ("Names for script " + mName + " do not match", loc); mState = EndCompleteState; return false; // we are stopping here, because there might be more garbage on the end line, // that we must ignore. // /// \todo allow this workaround to be disabled for newer scripts } if (mState==BeginCompleteState) { reportWarning ("Stray string (" + name + ") after begin statement", loc); return true; } return Parser::parseName (name, loc, scanner); } bool FileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (mState==BeginState && keyword==Scanner::K_begin) { mState = NameState; return true; } if (mState==NameState) { // keywords can be used as script names too. Thank you Morrowind for another // syntactic perversity :( mName = loc.mLiteral; mState = BeginCompleteState; return true; } if (mState==EndNameState) { // optional repeated name after end statement if (mName!=loc.mLiteral) reportWarning ("Names for script " + mName + " do not match", loc); mState = EndCompleteState; return false; // we are stopping here, because there might be more garbage on the end line, // that we must ignore. // /// \todo allow this workaround to be disabled for newer scripts } return Parser::parseKeyword (keyword, loc, scanner); } bool FileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_newline) { if (mState==BeginState) { // ignore empty lines return true; } if (mState==BeginCompleteState) { // parse the script body mScriptParser.reset(); scanner.scan (mScriptParser); mState = EndNameState; return true; } if (mState==EndCompleteState || mState==EndNameState) { // we are done here -> ignore the rest of the script return false; } } return Parser::parseSpecial (code, loc, scanner); } void FileParser::parseEOF (Scanner& scanner) { if (mState!=EndNameState && mState!=EndCompleteState) Parser::parseEOF (scanner); } void FileParser::reset() { mState = BeginState; mName.clear(); mScriptParser.reset(); Parser::reset(); } } openmw-openmw-0.38.0/components/compiler/fileparser.hpp000066400000000000000000000034151264522266000232660ustar00rootroot00000000000000#ifndef COMPILER_FILEPARSER_H_INCLUDED #define COMPILER_FILEPARSER_H_INCLUDED #include "parser.hpp" #include "scriptparser.hpp" #include "locals.hpp" #include "literals.hpp" namespace Compiler { // Top-level parser, to be used for global scripts, local scripts and targeted scripts class FileParser : public Parser { enum State { BeginState, NameState, BeginCompleteState, EndNameState, EndCompleteState }; ScriptParser mScriptParser; State mState; std::string mName; Locals mLocals; public: FileParser (ErrorHandler& errorHandler, Context& context); std::string getName() const; ///< Return script name. void getCode (std::vector& code) const; ///< store generated code in \a code. const Locals& getLocals() const; ///< get local variable declarations. virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? virtual void parseEOF (Scanner& scanner); ///< Handle EOF token. void reset(); ///< Reset parser to clean state. }; } #endif openmw-openmw-0.38.0/components/compiler/generator.cpp000066400000000000000000000570061264522266000231200ustar00rootroot00000000000000#include "generator.hpp" #include #include #include #include #include "literals.hpp" namespace { void opPushInt (Compiler::Generator::CodeContainer& code, int value) { code.push_back (Compiler::Generator::segment0 (0, value)); } void opFetchIntLiteral (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (4)); } void opFetchFloatLiteral (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (5)); } void opIntToFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (3)); } void opFloatToInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (6)); } void opStoreLocalShort (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (0)); } void opStoreLocalLong (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (1)); } void opStoreLocalFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (2)); } void opNegateInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (7)); } void opNegateFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (8)); } void opAddInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (9)); } void opAddFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (10)); } void opSubInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (11)); } void opSubFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (12)); } void opMulInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (13)); } void opMulFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (14)); } void opDivInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (15)); } void opDivFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (16)); } void opIntToFloat1 (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (17)); } void opSquareRoot (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (19)); } void opReturn (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (20)); } void opMessageBox (Compiler::Generator::CodeContainer& code, int buttons) { code.push_back (Compiler::Generator::segment3 (0, buttons)); } void opReport (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (58)); } void opFetchLocalShort (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (21)); } void opFetchLocalLong (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (22)); } void opFetchLocalFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (23)); } void opJumpForward (Compiler::Generator::CodeContainer& code, int offset) { code.push_back (Compiler::Generator::segment0 (1, offset)); } void opJumpBackward (Compiler::Generator::CodeContainer& code, int offset) { code.push_back (Compiler::Generator::segment0 (2, offset)); } /* Currently unused void opSkipOnZero (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (24)); } */ void opSkipOnNonZero (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (25)); } void opEqualInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (26)); } void opNonEqualInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (27)); } void opLessThanInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (28)); } void opLessOrEqualInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (29)); } void opGreaterThanInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (30)); } void opGreaterOrEqualInt (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (31)); } void opEqualFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (32)); } void opNonEqualFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (33)); } void opLessThanFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (34)); } void opLessOrEqualFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (35)); } void opGreaterThanFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (36)); } void opGreaterOrEqualFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (37)); } void opMenuMode (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (38)); } void opStoreGlobalShort (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (39)); } void opStoreGlobalLong (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (40)); } void opStoreGlobalFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (41)); } void opFetchGlobalShort (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (42)); } void opFetchGlobalLong (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (43)); } void opFetchGlobalFloat (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (44)); } void opStoreMemberShort (Compiler::Generator::CodeContainer& code, bool global) { code.push_back (Compiler::Generator::segment5 (global ? 65 : 59)); } void opStoreMemberLong (Compiler::Generator::CodeContainer& code, bool global) { code.push_back (Compiler::Generator::segment5 (global ? 66 : 60)); } void opStoreMemberFloat (Compiler::Generator::CodeContainer& code, bool global) { code.push_back (Compiler::Generator::segment5 (global ? 67 : 61)); } void opFetchMemberShort (Compiler::Generator::CodeContainer& code, bool global) { code.push_back (Compiler::Generator::segment5 (global ? 68 : 62)); } void opFetchMemberLong (Compiler::Generator::CodeContainer& code, bool global) { code.push_back (Compiler::Generator::segment5 (global ? 69 : 63)); } void opFetchMemberFloat (Compiler::Generator::CodeContainer& code, bool global) { code.push_back (Compiler::Generator::segment5 (global ? 70 : 64)); } void opRandom (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (45)); } void opScriptRunning (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (46)); } void opStartScript (Compiler::Generator::CodeContainer& code, bool targeted) { code.push_back (Compiler::Generator::segment5 (targeted ? 71 : 47)); } void opStopScript (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (48)); } void opGetDistance (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (49)); } void opGetSecondsPassed (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (50)); } void opEnable (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (51)); } void opDisable (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (52)); } void opGetDisabled (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (53)); } void opEnableExplicit (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (54)); } void opDisableExplicit (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (55)); } void opGetDisabledExplicit (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (56)); } void opGetDistanceExplicit (Compiler::Generator::CodeContainer& code) { code.push_back (Compiler::Generator::segment5 (57)); } } namespace Compiler { namespace Generator { void pushInt (CodeContainer& code, Literals& literals, int value) { int index = literals.addInteger (value); opPushInt (code, index); opFetchIntLiteral (code); } void pushFloat (CodeContainer& code, Literals& literals, float value) { int index = literals.addFloat (value); opPushInt (code, index); opFetchFloatLiteral (code); } void pushString (CodeContainer& code, Literals& literals, const std::string& value) { int index = literals.addString (value); opPushInt (code, index); } void assignToLocal (CodeContainer& code, char localType, int localIndex, const CodeContainer& value, char valueType) { opPushInt (code, localIndex); std::copy (value.begin(), value.end(), std::back_inserter (code)); if (localType!=valueType) { if (localType=='f' && valueType=='l') { opIntToFloat (code); } else if ((localType=='l' || localType=='s') && valueType=='f') { opFloatToInt (code); } } switch (localType) { case 'f': opStoreLocalFloat (code); break; case 's': opStoreLocalShort (code); break; case 'l': opStoreLocalLong (code); break; default: assert (0); } } void negate (CodeContainer& code, char valueType) { switch (valueType) { case 'l': opNegateInt (code); break; case 'f': opNegateFloat (code); break; default: assert (0); } } void add (CodeContainer& code, char valueType1, char valueType2) { if (valueType1=='l' && valueType2=='l') { opAddInt (code); } else { if (valueType1=='l') opIntToFloat1 (code); if (valueType2=='l') opIntToFloat (code); opAddFloat (code); } } void sub (CodeContainer& code, char valueType1, char valueType2) { if (valueType1=='l' && valueType2=='l') { opSubInt (code); } else { if (valueType1=='l') opIntToFloat1 (code); if (valueType2=='l') opIntToFloat (code); opSubFloat (code); } } void mul (CodeContainer& code, char valueType1, char valueType2) { if (valueType1=='l' && valueType2=='l') { opMulInt (code); } else { if (valueType1=='l') opIntToFloat1 (code); if (valueType2=='l') opIntToFloat (code); opMulFloat (code); } } void div (CodeContainer& code, char valueType1, char valueType2) { if (valueType1=='l' && valueType2=='l') { opDivInt (code); } else { if (valueType1=='l') opIntToFloat1 (code); if (valueType2=='l') opIntToFloat (code); opDivFloat (code); } } void convert (CodeContainer& code, char fromType, char toType) { if (fromType!=toType) { if (fromType=='f' && toType=='l') opFloatToInt (code); else if (fromType=='l' && toType=='f') opIntToFloat (code); else throw std::logic_error ("illegal type conversion"); } } void squareRoot (CodeContainer& code) { opSquareRoot (code); } void exit (CodeContainer& code) { opReturn (code); } void message (CodeContainer& code, Literals& literals, const std::string& message, int buttons) { assert (buttons>=0); if (buttons>=256) throw std::runtime_error ("A message box can't have more than 255 buttons"); int index = literals.addString (message); opPushInt (code, index); opMessageBox (code, buttons); } void report (CodeContainer& code, Literals& literals, const std::string& message) { int index = literals.addString (message); opPushInt (code, index); opReport (code); } void fetchLocal (CodeContainer& code, char localType, int localIndex) { opPushInt (code, localIndex); switch (localType) { case 'f': opFetchLocalFloat (code); break; case 's': opFetchLocalShort (code); break; case 'l': opFetchLocalLong (code); break; default: assert (0); } } void jump (CodeContainer& code, int offset) { if (offset>0) opJumpForward (code, offset); else if (offset<0) opJumpBackward (code, -offset); else throw std::logic_error ("infinite loop"); } void jumpOnZero (CodeContainer& code, int offset) { opSkipOnNonZero (code); if (offset<0) --offset; // compensate for skip instruction jump (code, offset); } void compare (CodeContainer& code, char op, char valueType1, char valueType2) { if (valueType1=='l' && valueType2=='l') { switch (op) { case 'e': opEqualInt (code); break; case 'n': opNonEqualInt (code); break; case 'l': opLessThanInt (code); break; case 'L': opLessOrEqualInt (code); break; case 'g': opGreaterThanInt (code); break; case 'G': opGreaterOrEqualInt (code); break; default: assert (0); } } else { if (valueType1=='l') opIntToFloat1 (code); if (valueType2=='l') opIntToFloat (code); switch (op) { case 'e': opEqualFloat (code); break; case 'n': opNonEqualFloat (code); break; case 'l': opLessThanFloat (code); break; case 'L': opLessOrEqualFloat (code); break; case 'g': opGreaterThanFloat (code); break; case 'G': opGreaterOrEqualFloat (code); break; default: assert (0); } } } void menuMode (CodeContainer& code) { opMenuMode (code); } void assignToGlobal (CodeContainer& code, Literals& literals, char localType, const std::string& name, const CodeContainer& value, char valueType) { int index = literals.addString (name); opPushInt (code, index); std::copy (value.begin(), value.end(), std::back_inserter (code)); if (localType!=valueType) { if (localType=='f' && (valueType=='l' || valueType=='s')) { opIntToFloat (code); } else if ((localType=='l' || localType=='s') && valueType=='f') { opFloatToInt (code); } } switch (localType) { case 'f': opStoreGlobalFloat (code); break; case 's': opStoreGlobalShort (code); break; case 'l': opStoreGlobalLong (code); break; default: assert (0); } } void fetchGlobal (CodeContainer& code, Literals& literals, char localType, const std::string& name) { int index = literals.addString (name); opPushInt (code, index); switch (localType) { case 'f': opFetchGlobalFloat (code); break; case 's': opFetchGlobalShort (code); break; case 'l': opFetchGlobalLong (code); break; default: assert (0); } } void assignToMember (CodeContainer& code, Literals& literals, char localType, const std::string& name, const std::string& id, const CodeContainer& value, char valueType, bool global) { int index = literals.addString (name); opPushInt (code, index); index = literals.addString (id); opPushInt (code, index); std::copy (value.begin(), value.end(), std::back_inserter (code)); if (localType!=valueType) { if (localType=='f' && (valueType=='l' || valueType=='s')) { opIntToFloat (code); } else if ((localType=='l' || localType=='s') && valueType=='f') { opFloatToInt (code); } } switch (localType) { case 'f': opStoreMemberFloat (code, global); break; case 's': opStoreMemberShort (code, global); break; case 'l': opStoreMemberLong (code, global); break; default: assert (0); } } void fetchMember (CodeContainer& code, Literals& literals, char localType, const std::string& name, const std::string& id, bool global) { int index = literals.addString (name); opPushInt (code, index); index = literals.addString (id); opPushInt (code, index); switch (localType) { case 'f': opFetchMemberFloat (code, global); break; case 's': opFetchMemberShort (code, global); break; case 'l': opFetchMemberLong (code, global); break; default: assert (0); } } void random (CodeContainer& code) { opRandom (code); } void scriptRunning (CodeContainer& code) { opScriptRunning (code); } void startScript (CodeContainer& code, Literals& literals, const std::string& id) { if (id.empty()) opStartScript (code, false); else { int index = literals.addString (id); opPushInt (code, index); opStartScript (code, true); } } void stopScript (CodeContainer& code) { opStopScript (code); } void getDistance (CodeContainer& code, Literals& literals, const std::string& id) { if (id.empty()) { opGetDistance (code); } else { int index = literals.addString (id); opPushInt (code, index); opGetDistanceExplicit (code); } } void getSecondsPassed (CodeContainer& code) { opGetSecondsPassed (code); } void getDisabled (CodeContainer& code, Literals& literals, const std::string& id) { if (id.empty()) { opGetDisabled (code); } else { int index = literals.addString (id); opPushInt (code, index); opGetDisabledExplicit (code); } } void enable (CodeContainer& code, Literals& literals, const std::string& id) { if (id.empty()) { opEnable (code); } else { int index = literals.addString (id); opPushInt (code, index); opEnableExplicit (code); } } void disable (CodeContainer& code, Literals& literals, const std::string& id) { if (id.empty()) { opDisable (code); } else { int index = literals.addString (id); opPushInt (code, index); opDisableExplicit (code); } } } } openmw-openmw-0.38.0/components/compiler/generator.hpp000066400000000000000000000105621264522266000231210ustar00rootroot00000000000000#ifndef COMPILER_GENERATOR_H_INCLUDED #define COMPILER_GENERATOR_H_INCLUDED #include #include #include #include namespace Compiler { class Literals; namespace Generator { typedef std::vector CodeContainer; inline Interpreter::Type_Code segment0 (unsigned int c, unsigned int arg0) { assert (c<64); return (c<<24) | (arg0 & 0xffffff); } inline Interpreter::Type_Code segment1 (unsigned int c, unsigned int arg0, unsigned int arg1) { assert (c<64); return 0x40000000 | (c<<24) | ((arg0 & 0xfff)<<12) | (arg1 & 0xfff); } inline Interpreter::Type_Code segment2 (unsigned int c, unsigned int arg0) { assert (c<1024); return 0x80000000 | (c<<20) | (arg0 & 0xfffff); } inline Interpreter::Type_Code segment3 (unsigned int c, unsigned int arg0) { assert (c<262144); return 0xc0000000 | (c<<8) | (arg0 & 0xff); } inline Interpreter::Type_Code segment4 (unsigned int c, unsigned int arg0, unsigned int arg1) { assert (c<1024); return 0xc4000000 | (c<<16) | ((arg0 & 0xff)<<8) | (arg1 & 0xff); } inline Interpreter::Type_Code segment5 (unsigned int c) { assert (c<67108864); return 0xc8000000 | c; } void pushInt (CodeContainer& code, Literals& literals, int value); void pushFloat (CodeContainer& code, Literals& literals, float value); void pushString (CodeContainer& code, Literals& literals, const std::string& value); void assignToLocal (CodeContainer& code, char localType, int localIndex, const CodeContainer& value, char valueType); void negate (CodeContainer& code, char valueType); void add (CodeContainer& code, char valueType1, char valueType2); void sub (CodeContainer& code, char valueType1, char valueType2); void mul (CodeContainer& code, char valueType1, char valueType2); void div (CodeContainer& code, char valueType1, char valueType2); void convert (CodeContainer& code, char fromType, char toType); void squareRoot (CodeContainer& code); void exit (CodeContainer& code); void message (CodeContainer& code, Literals& literals, const std::string& message, int buttons); void report (CodeContainer& code, Literals& literals, const std::string& message); void fetchLocal (CodeContainer& code, char localType, int localIndex); void jump (CodeContainer& code, int offset); void jumpOnZero (CodeContainer& code, int offset); void compare (CodeContainer& code, char op, char valueType1, char valueType2); void menuMode (CodeContainer& code); void assignToGlobal (CodeContainer& code, Literals& literals, char localType, const std::string& name, const CodeContainer& value, char valueType); void fetchGlobal (CodeContainer& code, Literals& literals, char localType, const std::string& name); void assignToMember (CodeContainer& code, Literals& literals, char memberType, const std::string& name, const std::string& id, const CodeContainer& value, char valueType, bool global); ///< \param global Member of a global script instead of a script of a reference. void fetchMember (CodeContainer& code, Literals& literals, char memberType, const std::string& name, const std::string& id, bool global); ///< \param global Member of a global script instead of a script of a reference. void random (CodeContainer& code); void scriptRunning (CodeContainer& code); void startScript (CodeContainer& code, Literals& literals, const std::string& id); void stopScript (CodeContainer& code); void getDistance (CodeContainer& code, Literals& literals, const std::string& id); void getSecondsPassed (CodeContainer& code); void getDisabled (CodeContainer& code, Literals& literals, const std::string& id); void enable (CodeContainer& code, Literals& literals, const std::string& id); void disable (CodeContainer& code, Literals& literals, const std::string& id); } } #endif openmw-openmw-0.38.0/components/compiler/junkparser.cpp000066400000000000000000000022761264522266000233150ustar00rootroot00000000000000#include "junkparser.hpp" #include "scanner.hpp" Compiler::JunkParser::JunkParser (ErrorHandler& errorHandler, const Context& context, int ignoreKeyword) : Parser (errorHandler, context), mIgnoreKeyword (ignoreKeyword) {} bool Compiler::JunkParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) { scanner.putbackInt (value, loc); return false; } bool Compiler::JunkParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) { scanner.putbackFloat (value, loc); return false; } bool Compiler::JunkParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { scanner.putbackName (name, loc); return false; } bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (keyword==mIgnoreKeyword) reportWarning ("found junk (ignoring it)", loc); else scanner.putbackKeyword (keyword, loc); return false; } bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_member) reportWarning ("found junk (ignoring it)", loc); else scanner.putbackSpecial (code, loc); return false; } openmw-openmw-0.38.0/components/compiler/junkparser.hpp000066400000000000000000000024401264522266000233130ustar00rootroot00000000000000#ifndef COMPILER_JUNKPARSER_H_INCLUDED #define COMPILER_JUNKPARSER_H_INCLUDED #include "parser.hpp" namespace Compiler { /// \brief Parse an optional single junk token class JunkParser : public Parser { int mIgnoreKeyword; public: JunkParser (ErrorHandler& errorHandler, const Context& context, int ignoreKeyword = -1); virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. /// \return fetch another token? virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); ///< Handle a float token. /// \return fetch another token? virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? }; } #endif openmw-openmw-0.38.0/components/compiler/lineparser.cpp000066400000000000000000000443021264522266000232710ustar00rootroot00000000000000#include "lineparser.hpp" #include #include #include "scanner.hpp" #include "context.hpp" #include "errorhandler.hpp" #include "skipparser.hpp" #include "locals.hpp" #include "generator.hpp" #include "extensions.hpp" #include "declarationparser.hpp" #include "exception.hpp" namespace Compiler { void LineParser::parseExpression (Scanner& scanner, const TokenLoc& loc) { mExprParser.reset(); if (!mExplicit.empty()) { mExprParser.parseName (mExplicit, loc, scanner); if (mState==MemberState) mExprParser.parseSpecial (Scanner::S_member, loc, scanner); else mExprParser.parseSpecial (Scanner::S_ref, loc, scanner); } scanner.scan (mExprParser); char type = mExprParser.append (mCode); mState = EndState; switch (type) { case 'l': Generator::report (mCode, mLiterals, "%g"); break; case 'f': Generator::report (mCode, mLiterals, "%f"); break; default: throw std::runtime_error ("unknown expression result type"); } } LineParser::LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, std::vector& code, bool allowExpression) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code), mState (BeginState), mReferenceMember(false), mButtons(0), mType(0), mExprParser (errorHandler, context, locals, literals), mAllowExpression (allowExpression) {} bool LineParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) { if (mAllowExpression && mState==BeginState) { scanner.putbackInt (value, loc); parseExpression (scanner, loc); return true; } return Parser::parseInt (value, loc, scanner); } bool LineParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) { if (mAllowExpression && mState==BeginState) { scanner.putbackFloat (value, loc); parseExpression (scanner, loc); return true; } return Parser::parseFloat (value, loc, scanner); } bool LineParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (mState==PotentialEndState) { getErrorHandler().warning ("stray string argument (ignoring it)", loc); mState = EndState; return true; } if (mState==SetState) { std::string name2 = Misc::StringUtils::lowerCase (name); mName = name2; // local variable? char type = mLocals.getType (name2); if (type!=' ') { mType = type; mState = SetLocalVarState; return true; } type = getContext().getGlobalType (name2); if (type!=' ') { mType = type; mState = SetGlobalVarState; return true; } mState = SetPotentialMemberVarState; return true; } if (mState==SetMemberVarState) { mMemberName = Misc::StringUtils::lowerCase (name); std::pair type = getContext().getMemberType (mMemberName, mName); if (type.first!=' ') { mState = SetMemberVarState2; mType = type.first; mReferenceMember = type.second; return true; } getErrorHandler().error ("unknown variable", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; } if (mState==MessageState || mState==MessageCommaState) { std::string arguments; for (std::size_t i=0; iisInstruction (keyword, argumentType, hasExplicit)) { // pretend this is not a keyword std::string name = loc.mLiteral; if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"') name = name.substr (1, name.size()-2); return parseName (name, loc, scanner); } } } if (mState==SetMemberVarState) { mMemberName = loc.mLiteral; std::pair type = getContext().getMemberType (mMemberName, mName); if (type.first!=' ') { mState = SetMemberVarState2; mType = type.first; mReferenceMember = type.second; return true; } } if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to) { getErrorHandler().warning ("unknown variable (ignoring set instruction)", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; } if (mState==SetState) { // allow keywords to be used as variable names when assigning a value to a variable. return parseName (loc.mLiteral, loc, scanner); } if (mState==BeginState || mState==ExplicitState) { switch (keyword) { case Scanner::K_enable: Generator::enable (mCode, mLiterals, mExplicit); mState = PotentialEndState; return true; case Scanner::K_disable: Generator::disable (mCode, mLiterals, mExplicit); mState = PotentialEndState; return true; case Scanner::K_startscript: mExprParser.parseArguments ("c", scanner, mCode); Generator::startScript (mCode, mLiterals, mExplicit); mState = EndState; return true; case Scanner::K_stopscript: mExprParser.parseArguments ("c", scanner, mCode); Generator::stopScript (mCode); mState = EndState; return true; } // check for custom extensions if (const Extensions *extensions = getContext().getExtensions()) { std::string argumentType; bool hasExplicit = mState==ExplicitState; if (extensions->isInstruction (keyword, argumentType, hasExplicit)) { if (!hasExplicit && mState==ExplicitState) { getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); mExplicit.clear(); } try { // workaround for broken positioncell instructions. /// \todo add option to disable this std::auto_ptr errorDowngrade (0); if (Misc::StringUtils::lowerCase (loc.mLiteral)=="positioncell") errorDowngrade.reset (new ErrorDowngrade (getErrorHandler())); std::vector code; int optionals = mExprParser.parseArguments (argumentType, scanner, code, keyword); mCode.insert (mCode.end(), code.begin(), code.end()); extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals); } catch (const SourceException&) { // Ignore argument exceptions for positioncell. /// \todo add option to disable this if (Misc::StringUtils::lowerCase (loc.mLiteral)=="positioncell") { SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; } throw; } mState = EndState; return true; } } if (mAllowExpression) { if (keyword==Scanner::K_getdisabled || keyword==Scanner::K_getdistance) { scanner.putbackKeyword (keyword, loc); parseExpression (scanner, loc); mState = EndState; return true; } if (const Extensions *extensions = getContext().getExtensions()) { char returnType; std::string argumentType; bool hasExplicit = !mExplicit.empty(); if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit)) { if (!hasExplicit && !mExplicit.empty()) { getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); mExplicit.clear(); } scanner.putbackKeyword (keyword, loc); parseExpression (scanner, loc); mState = EndState; return true; } } } } if (mState==ExplicitState) { // drop stray explicit reference getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); mState = BeginState; mExplicit.clear(); } if (mState==BeginState) { switch (keyword) { case Scanner::K_short: case Scanner::K_long: case Scanner::K_float: { if (!getContext().canDeclareLocals()) { getErrorHandler().error ( "local variables can't be declared in this context", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return true; } DeclarationParser declaration (getErrorHandler(), getContext(), mLocals); if (declaration.parseKeyword (keyword, loc, scanner)) scanner.scan (declaration); return false; } case Scanner::K_set: mState = SetState; return true; case Scanner::K_messagebox: mState = MessageState; scanner.enableStrictKeywords(); return true; case Scanner::K_return: Generator::exit (mCode); mState = EndState; return true; case Scanner::K_stopscript: mExprParser.parseArguments ("c", scanner, mCode); Generator::stopScript (mCode); mState = EndState; return true; case Scanner::K_else: getErrorHandler().warning ("stray else (ignoring it)", loc); mState = EndState; return true; case Scanner::K_endif: getErrorHandler().warning ("stray endif (ignoring it)", loc); mState = EndState; return true; case Scanner::K_begin: getErrorHandler().warning ("stray begin (ignoring it)", loc); mState = EndState; return true; } } else if (mState==SetLocalVarState && keyword==Scanner::K_to) { mExprParser.reset(); scanner.scan (mExprParser); std::vector code; char type = mExprParser.append (code); Generator::assignToLocal (mCode, mLocals.getType (mName), mLocals.getIndex (mName), code, type); mState = EndState; return true; } else if (mState==SetGlobalVarState && keyword==Scanner::K_to) { mExprParser.reset(); scanner.scan (mExprParser); std::vector code; char type = mExprParser.append (code); Generator::assignToGlobal (mCode, mLiterals, mType, mName, code, type); mState = EndState; return true; } else if (mState==SetMemberVarState2 && keyword==Scanner::K_to) { mExprParser.reset(); scanner.scan (mExprParser); std::vector code; char type = mExprParser.append (code); Generator::assignToMember (mCode, mLiterals, mType, mMemberName, mName, code, type, !mReferenceMember); mState = EndState; return true; } if (mAllowExpression) { if (keyword==Scanner::K_getsquareroot || keyword==Scanner::K_menumode || keyword==Scanner::K_random || keyword==Scanner::K_scriptrunning || keyword==Scanner::K_getsecondspassed) { scanner.putbackKeyword (keyword, loc); parseExpression (scanner, loc); mState = EndState; return true; } } return Parser::parseKeyword (keyword, loc, scanner); } bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (mState==EndState && code==Scanner::S_open) { getErrorHandler().warning ("stray '[' or '(' at the end of the line (ignoring it)", loc); return true; } if (code==Scanner::S_newline && (mState==EndState || mState==BeginState || mState==PotentialEndState)) return false; if (code==Scanner::S_comma && mState==MessageState) { mState = MessageCommaState; return true; } if (code==Scanner::S_ref && mState==PotentialExplicitState) { mState = ExplicitState; return true; } if (code==Scanner::S_member && mState==PotentialExplicitState) { mState = MemberState; parseExpression (scanner, loc); mState = EndState; return true; } if (code==Scanner::S_newline && mState==MessageButtonState) { Generator::message (mCode, mLiterals, mName, mButtons); return false; } if (code==Scanner::S_comma && mState==MessageButtonState) { mState = MessageButtonCommaState; return true; } if (code==Scanner::S_member && mState==SetPotentialMemberVarState) { mState = SetMemberVarState; return true; } if (mAllowExpression && mState==BeginState && (code==Scanner::S_open || code==Scanner::S_minus || code==Scanner::S_plus)) { scanner.putbackSpecial (code, loc); parseExpression (scanner, loc); mState = EndState; return true; } return Parser::parseSpecial (code, loc, scanner); } void LineParser::reset() { mState = BeginState; mName.clear(); mExplicit.clear(); } } openmw-openmw-0.38.0/components/compiler/lineparser.hpp000066400000000000000000000052331264522266000232760ustar00rootroot00000000000000#ifndef COMPILER_LINEPARSER_H_INCLUDED #define COMPILER_LINEPARSER_H_INCLUDED #include #include #include "parser.hpp" #include "exprparser.hpp" namespace Compiler { class Locals; class Literals; /// \brief Line parser, to be used in console scripts and as part of ScriptParser class LineParser : public Parser { enum State { BeginState, SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState, SetMemberVarState, SetMemberVarState2, MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState, EndState, PotentialEndState /* may have a stray string argument */, PotentialExplicitState, ExplicitState, MemberState }; Locals& mLocals; Literals& mLiterals; std::vector& mCode; State mState; std::string mName; std::string mMemberName; bool mReferenceMember; int mButtons; std::string mExplicit; char mType; ExprParser mExprParser; bool mAllowExpression; void parseExpression (Scanner& scanner, const TokenLoc& loc); public: LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, std::vector& code, bool allowExpression = false); ///< \param allowExpression Allow lines consisting of a naked expression /// (result is send to the messagebox interface) virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. /// \return fetch another token? virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); ///< Handle a float token. /// \return fetch another token? virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? void reset(); ///< Reset parser to clean state. }; } #endif openmw-openmw-0.38.0/components/compiler/literals.cpp000066400000000000000000000047711264522266000227520ustar00rootroot00000000000000#include "literals.hpp" #include namespace Compiler { int Literals::getIntegerSize() const { return mIntegers.size() * sizeof (Interpreter::Type_Integer); } int Literals::getFloatSize() const { return mFloats.size() * sizeof (Interpreter::Type_Float); } int Literals::getStringSize() const { int size = 0; for (std::vector::const_iterator iter (mStrings.begin()); iter!=mStrings.end(); ++iter) size += static_cast (iter->size()) + 1; if (size % 4) // padding size += 4 - size % 4; return size; } void Literals::append (std::vector& code) const { for (std::vector::const_iterator iter (mIntegers.begin()); iter!=mIntegers.end(); ++iter) code.push_back (*reinterpret_cast (&*iter)); for (std::vector::const_iterator iter (mFloats.begin()); iter!=mFloats.end(); ++iter) code.push_back (*reinterpret_cast (&*iter)); int stringBlockSize = getStringSize(); int size = static_cast (code.size()); code.resize (size+stringBlockSize/4); int offset = 0; for (std::vector::const_iterator iter (mStrings.begin()); iter!=mStrings.end(); ++iter) { int stringSize = iter->size()+1; std::copy (iter->c_str(), iter->c_str()+stringSize, reinterpret_cast (&code[size]) + offset); offset += stringSize; } } int Literals::addInteger (Interpreter::Type_Integer value) { int index = static_cast (mIntegers.size()); mIntegers.push_back (value); return index; } int Literals::addFloat (Interpreter::Type_Float value) { int index = static_cast (mFloats.size()); mFloats.push_back (value); return index; } int Literals::addString (const std::string& value) { int index = static_cast (mStrings.size()); mStrings.push_back (value); return index; } void Literals::clear() { mIntegers.clear(); mFloats.clear(); mStrings.clear(); } } openmw-openmw-0.38.0/components/compiler/literals.hpp000066400000000000000000000025701264522266000227520ustar00rootroot00000000000000#ifndef COMPILER_LITERALS_H_INCLUDED #define COMPILER_LITERALS_H_INCLUDED #include #include #include namespace Compiler { /// \brief Literal values. class Literals { std::vector mIntegers; std::vector mFloats; std::vector mStrings; public: int getIntegerSize() const; ///< Return size of integer block (in bytes). int getFloatSize() const; ///< Return size of float block (in bytes). int getStringSize() const; ///< Return size of string block (in bytes). void append (std::vector& code) const; ///< Apepnd literal blocks to code. /// \note code blocks will be padded for 32-bit alignment. int addInteger (Interpreter::Type_Integer value); ///< add integer liternal and return index. int addFloat (Interpreter::Type_Float value); ///< add float literal and return value. int addString (const std::string& value); ///< add string literal and return value. void clear(); ///< remove all literals. }; } #endif openmw-openmw-0.38.0/components/compiler/locals.cpp000066400000000000000000000051421264522266000224010ustar00rootroot00000000000000#include "locals.hpp" #include #include #include #include #include #include namespace Compiler { const std::vector& Locals::get (char type) const { switch (type) { case 's': return mShorts; case 'l': return mLongs; case 'f': return mFloats; } throw std::logic_error ("unknown variable type"); } int Locals::searchIndex (char type, const std::string& name) const { const std::vector& collection = get (type); std::vector::const_iterator iter = std::find (collection.begin(), collection.end(), name); if (iter==collection.end()) return -1; return iter-collection.begin(); } bool Locals::search (char type, const std::string& name) const { return searchIndex (type, name)!=-1; } std::vector& Locals::get (char type) { switch (type) { case 's': return mShorts; case 'l': return mLongs; case 'f': return mFloats; } throw std::logic_error ("unknown variable type"); } char Locals::getType (const std::string& name) const { if (search ('s', name)) return 's'; if (search ('l', name)) return 'l'; if (search ('f', name)) return 'f'; return ' '; } int Locals::getIndex (const std::string& name) const { int index = searchIndex ('s', name); if (index!=-1) return index; index = searchIndex ('l', name); if (index!=-1) return index; return searchIndex ('f', name); } void Locals::write (std::ostream& localFile) const { localFile << get ('s').size() << ' ' << get ('l').size() << ' ' << get ('f').size() << std::endl; std::copy (get ('s').begin(), get ('s').end(), std::ostream_iterator (localFile, " ")); std::copy (get ('l').begin(), get ('l').end(), std::ostream_iterator (localFile, " ")); std::copy (get ('f').begin(), get ('f').end(), std::ostream_iterator (localFile, " ")); } void Locals::declare (char type, const std::string& name) { get (type).push_back (Misc::StringUtils::lowerCase (name)); } void Locals::clear() { get ('s').clear(); get ('l').clear(); get ('f').clear(); } } openmw-openmw-0.38.0/components/compiler/locals.hpp000066400000000000000000000024401264522266000224040ustar00rootroot00000000000000#ifndef COMPILER_LOCALS_H_INCLUDED #define COMPILER_LOCALS_H_INCLUDED #include #include #include namespace Compiler { /// \brief Local variable declarations class Locals { std::vector mShorts; std::vector mLongs; std::vector mFloats; std::vector& get (char type); public: char getType (const std::string& name) const; ///< 's': short, 'l': long, 'f': float, ' ': does not exist. int getIndex (const std::string& name) const; ///< return index for local variable \a name (-1: does not exist). bool search (char type, const std::string& name) const; /// Return index for local variable \a name of type \a type (-1: variable does not /// exit). int searchIndex (char type, const std::string& name) const; const std::vector& get (char type) const; void write (std::ostream& localFile) const; ///< write declarations to file. void declare (char type, const std::string& name); ///< declares a variable. void clear(); ///< remove all declarations. }; } #endif openmw-openmw-0.38.0/components/compiler/nullerrorhandler.cpp000066400000000000000000000003351264522266000245050ustar00rootroot00000000000000#include "nullerrorhandler.hpp" void Compiler::NullErrorHandler::report (const std::string& message, const TokenLoc& loc, Type type) {} void Compiler::NullErrorHandler::report (const std::string& message, Type type) {} openmw-openmw-0.38.0/components/compiler/nullerrorhandler.hpp000066400000000000000000000010351264522266000245100ustar00rootroot00000000000000#ifndef COMPILER_NULLERRORHANDLER_H_INCLUDED #define COMPILER_NULLERRORHANDLER_H_INCLUDED #include "errorhandler.hpp" namespace Compiler { /// \brief Error handler implementation: Ignore all error messages class NullErrorHandler : public ErrorHandler { virtual void report (const std::string& message, const TokenLoc& loc, Type type); ///< Report error to the user. virtual void report (const std::string& message, Type type); ///< Report a file related error }; } #endif openmw-openmw-0.38.0/components/compiler/opcodes.cpp000066400000000000000000000004451264522266000225610ustar00rootroot00000000000000#include "opcodes.hpp" namespace Compiler { namespace Control { const char *controls[numberOfControls] = { "playercontrols", "playerfighting", "playerjumping", "playerlooking", "playermagic", "playerviewswitch", "vanitymode" }; } } openmw-openmw-0.38.0/components/compiler/opcodes.hpp000066400000000000000000000542251264522266000225730ustar00rootroot00000000000000#ifndef COMPILER_OPCODES_H #define COMPILER_OPCODES_H namespace Compiler { namespace Ai { const int opcodeAiTravel = 0x20000; const int opcodeAiTravelExplicit = 0x20001; const int opcodeAiEscort = 0x20002; const int opcodeAiEscortExplicit = 0x20003; const int opcodeGetAiPackageDone = 0x200007c; const int opcodeGetAiPackageDoneExplicit = 0x200007d; const int opcodeGetCurrentAiPackage = 0x20001ef; const int opcodeGetCurrentAiPackageExplicit = 0x20001f0; const int opcodeGetDetected = 0x20001f1; const int opcodeGetDetectedExplicit = 0x20001f2; const int opcodeAiWander = 0x20010; const int opcodeAiWanderExplicit = 0x20011; const int opcodeAIActivate = 0x2001e; const int opcodeAIActivateExplicit = 0x2001f; const int opcodeAiEscortCell = 0x20020; const int opcodeAiEscortCellExplicit = 0x20021; const int opcodeAiFollow = 0x20022; const int opcodeAiFollowExplicit = 0x20023; const int opcodeAiFollowCell = 0x20024; const int opcodeAiFollowCellExplicit = 0x20025; const int opcodeSetHello = 0x200015c; const int opcodeSetHelloExplicit = 0x200015d; const int opcodeSetFight = 0x200015e; const int opcodeSetFightExplicit = 0x200015f; const int opcodeSetFlee = 0x2000160; const int opcodeSetFleeExplicit = 0x2000161; const int opcodeSetAlarm = 0x2000162; const int opcodeSetAlarmExplicit = 0x2000163; const int opcodeModHello = 0x20001b7; const int opcodeModHelloExplicit = 0x20001b8; const int opcodeModFight = 0x20001b9; const int opcodeModFightExplicit = 0x20001ba; const int opcodeModFlee = 0x20001bb; const int opcodeModFleeExplicit = 0x20001bc; const int opcodeModAlarm = 0x20001bd; const int opcodeModAlarmExplicit = 0x20001be; const int opcodeGetHello = 0x20001bf; const int opcodeGetHelloExplicit = 0x20001c0; const int opcodeGetFight = 0x20001c1; const int opcodeGetFightExplicit = 0x20001c2; const int opcodeGetFlee = 0x20001c3; const int opcodeGetFleeExplicit = 0x20001c4; const int opcodeGetAlarm = 0x20001c5; const int opcodeGetAlarmExplicit = 0x20001c6; const int opcodeGetLineOfSight = 0x2000222; const int opcodeGetLineOfSightExplicit = 0x2000223; const int opcodeToggleAI = 0x2000224; const int opcodeToggleAIExplicit = 0x2000225; const int opcodeGetTarget = 0x2000238; const int opcodeGetTargetExplicit = 0x2000239; const int opcodeStartCombat = 0x200023a; const int opcodeStartCombatExplicit = 0x200023b; const int opcodeStopCombat = 0x200023c; const int opcodeStopCombatExplicit = 0x200023d; const int opcodeFace = 0x200024c; const int opcodeFaceExplicit = 0x200024d; } namespace Animation { const int opcodeSkipAnim = 0x2000138; const int opcodeSkipAnimExplicit = 0x2000139; const int opcodePlayAnim = 0x20006; const int opcodePlayAnimExplicit = 0x20007; const int opcodeLoopAnim = 0x20008; const int opcodeLoopAnimExplicit = 0x20009; } namespace Cell { const int opcodeCellChanged = 0x2000000; const int opcodeCOC = 0x2000026; const int opcodeCOE = 0x2000226; const int opcodeGetInterior = 0x2000131; const int opcodeGetPCCell = 0x2000136; const int opcodeGetWaterLevel = 0x2000141; const int opcodeSetWaterLevel = 0x2000142; const int opcodeModWaterLevel = 0x2000143; } namespace Console { } namespace Container { const int opcodeAddItem = 0x2000076; const int opcodeAddItemExplicit = 0x2000077; const int opcodeGetItemCount = 0x2000078; const int opcodeGetItemCountExplicit = 0x2000079; const int opcodeRemoveItem = 0x200007a; const int opcodeRemoveItemExplicit = 0x200007b; const int opcodeEquip = 0x20001b3; const int opcodeEquipExplicit = 0x20001b4; const int opcodeGetArmorType = 0x20001d1; const int opcodeGetArmorTypeExplicit = 0x20001d2; const int opcodeHasItemEquipped = 0x20001d5; const int opcodeHasItemEquippedExplicit = 0x20001d6; const int opcodeHasSoulGem = 0x20001de; const int opcodeHasSoulGemExplicit = 0x20001df; const int opcodeGetWeaponType = 0x20001e0; const int opcodeGetWeaponTypeExplicit = 0x20001e1; } namespace Control { const int numberOfControls = 7; extern const char *controls[numberOfControls]; const int opcodeEnable = 0x200007e; const int opcodeDisable = 0x2000085; const int opcodeToggleCollision = 0x2000130; const int opcodeClearForceRun = 0x2000154; const int opcodeClearForceRunExplicit = 0x2000155; const int opcodeForceRun = 0x2000156; const int opcodeForceRunExplicit = 0x2000157; const int opcodeClearForceJump = 0x2000258; const int opcodeClearForceJumpExplicit = 0x2000259; const int opcodeForceJump = 0x200025a; const int opcodeForceJumpExplicit = 0x200025b; const int opcodeClearForceMoveJump = 0x200025c; const int opcodeClearForceMoveJumpExplicit = 0x200025d; const int opcodeForceMoveJump = 0x200025e; const int opcodeForceMoveJumpExplicit = 0x200025f; const int opcodeClearForceSneak = 0x2000158; const int opcodeClearForceSneakExplicit = 0x2000159; const int opcodeForceSneak = 0x200015a; const int opcodeForceSneakExplicit = 0x200015b; const int opcodeGetDisabled = 0x2000175; const int opcodeGetPcRunning = 0x20001c9; const int opcodeGetPcSneaking = 0x20001ca; const int opcodeGetForceRun = 0x20001cb; const int opcodeGetForceSneak = 0x20001cc; const int opcodeGetForceRunExplicit = 0x20001cd; const int opcodeGetForceSneakExplicit = 0x20001ce; const int opcodeGetForceJump = 0x2000260; const int opcodeGetForceMoveJump = 0x2000262; const int opcodeGetForceJumpExplicit = 0x2000261; const int opcodeGetForceMoveJumpExplicit = 0x2000263; } namespace Dialogue { const int opcodeJournal = 0x2000133; const int opcodeSetJournalIndex = 0x2000134; const int opcodeGetJournalIndex = 0x2000135; const int opcodeAddTopic = 0x200013a; const int opcodeChoice = 0x2000a; const int opcodeForceGreeting = 0x200014f; const int opcodeForceGreetingExplicit = 0x2000150; const int opcodeGoodbye = 0x2000152; const int opcodeSetReputation = 0x20001ad; const int opcodeModReputation = 0x20001ae; const int opcodeSetReputationExplicit = 0x20001af; const int opcodeModReputationExplicit = 0x20001b0; const int opcodeGetReputation = 0x20001b1; const int opcodeGetReputationExplicit = 0x20001b2; const int opcodeSameFaction = 0x20001b5; const int opcodeSameFactionExplicit = 0x20001b6; const int opcodeModFactionReaction = 0x2000242; const int opcodeSetFactionReaction = 0x20002ff; const int opcodeGetFactionReaction = 0x2000243; const int opcodeClearInfoActor = 0x2000245; const int opcodeClearInfoActorExplicit = 0x2000246; } namespace Gui { const int opcodeEnableBirthMenu = 0x200000e; const int opcodeEnableClassMenu = 0x200000f; const int opcodeEnableNameMenu = 0x2000010; const int opcodeEnableRaceMenu = 0x2000011; const int opcodeEnableStatsReviewMenu = 0x2000012; const int opcodeEnableInventoryMenu = 0x2000013; const int opcodeEnableMagicMenu = 0x2000014; const int opcodeEnableMapMenu = 0x2000015; const int opcodeEnableStatsMenu = 0x2000016; const int opcodeEnableRest = 0x2000017; const int opcodeEnableLevelupMenu = 0x2000300; const int opcodeShowRestMenu = 0x2000018; const int opcodeShowRestMenuExplicit = 0x2000234; const int opcodeGetButtonPressed = 0x2000137; const int opcodeToggleFogOfWar = 0x2000145; const int opcodeToggleFullHelp = 0x2000151; const int opcodeShowMap = 0x20001a0; const int opcodeFillMap = 0x20001a1; const int opcodeMenuTest = 0x2002c; const int opcodeToggleMenus = 0x200024b; } namespace Misc { const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; const int opcodeActivateExplicit = 0x2000244; const int opcodeLock = 0x20004; const int opcodeLockExplicit = 0x20005; const int opcodeUnlock = 0x200008c; const int opcodeUnlockExplicit = 0x200008d; const int opcodeToggleCollisionDebug = 0x2000132; const int opcodeToggleCollisionBoxes = 0x20001ac; const int opcodeToggleWireframe = 0x200013b; const int opcodeFadeIn = 0x200013c; const int opcodeFadeOut = 0x200013d; const int opcodeFadeTo = 0x200013e; const int opcodeToggleWater = 0x2000144; const int opcodeToggleWorld = 0x20002f5; const int opcodeTogglePathgrid = 0x2000146; const int opcodeDontSaveObject = 0x2000153; const int opcodePcForce1stPerson = 0x20002f6; const int opcodePcForce3rdPerson = 0x20002f7; const int opcodePcGet3rdPerson = 0x20002f8; const int opcodeToggleVanityMode = 0x2000174; const int opcodeGetPcSleep = 0x200019f; const int opcodeGetPcJumping = 0x2000233; const int opcodeWakeUpPc = 0x20001a2; const int opcodeGetLocked = 0x20001c7; const int opcodeGetLockedExplicit = 0x20001c8; const int opcodeGetEffect = 0x20001cf; const int opcodeGetEffectExplicit = 0x20001d0; const int opcodeBetaComment = 0x2002d; const int opcodeBetaCommentExplicit = 0x2002e; const int opcodeAddSoulGem = 0x20001f3; const int opcodeAddSoulGemExplicit = 0x20001f4; const int opcodeRemoveSoulGem = 0x20027; const int opcodeRemoveSoulGemExplicit = 0x20028; const int opcodeDrop = 0x20001f8; const int opcodeDropExplicit = 0x20001f9; const int opcodeDropSoulGem = 0x20001fa; const int opcodeDropSoulGemExplicit = 0x20001fb; const int opcodeGetAttacked = 0x20001d3; const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetWeaponDrawn = 0x20001d7; const int opcodeGetWeaponDrawnExplicit = 0x20001d8; const int opcodeGetSpellReadied = 0x2000231; const int opcodeGetSpellReadiedExplicit = 0x2000232; const int opcodeGetSpellEffects = 0x20001db; const int opcodeGetSpellEffectsExplicit = 0x20001dc; const int opcodeGetCurrentTime = 0x20001dd; const int opcodeSetDelete = 0x20001e5; const int opcodeSetDeleteExplicit = 0x20001e6; const int opcodeGetSquareRoot = 0x20001e7; const int opcodeFall = 0x200020a; const int opcodeFallExplicit = 0x200020b; const int opcodeGetStandingPc = 0x200020c; const int opcodeGetStandingPcExplicit = 0x200020d; const int opcodeGetStandingActor = 0x200020e; const int opcodeGetStandingActorExplicit = 0x200020f; const int opcodeGetCollidingPc = 0x2000250; const int opcodeGetCollidingPcExplicit = 0x2000251; const int opcodeGetCollidingActor = 0x2000252; const int opcodeGetCollidingActorExplicit = 0x2000253; const int opcodeHurtStandingActor = 0x2000254; const int opcodeHurtStandingActorExplicit = 0x2000255; const int opcodeHurtCollidingActor = 0x2000256; const int opcodeHurtCollidingActorExplicit = 0x2000257; const int opcodeGetWindSpeed = 0x2000212; const int opcodePlayBink = 0x20001f7; const int opcodeGoToJail = 0x2000235; const int opcodePayFine = 0x2000236; const int opcodePayFineThief = 0x2000237; const int opcodeHitOnMe = 0x2000213; const int opcodeHitOnMeExplicit = 0x2000214; const int opcodeHitAttemptOnMe = 0x20002f9; const int opcodeHitAttemptOnMeExplicit = 0x20002fa; const int opcodeDisableTeleporting = 0x2000215; const int opcodeEnableTeleporting = 0x2000216; const int opcodeShowVars = 0x200021d; const int opcodeShowVarsExplicit = 0x200021e; const int opcodeToggleGodMode = 0x200021f; const int opcodeToggleScripts = 0x2000301; const int opcodeDisableLevitation = 0x2000220; const int opcodeEnableLevitation = 0x2000221; const int opcodeCast = 0x2000227; const int opcodeCastExplicit = 0x2000228; const int opcodeExplodeSpell = 0x2000229; const int opcodeExplodeSpellExplicit = 0x200022a; const int opcodeGetPcInJail = 0x200023e; const int opcodeGetPcTraveling = 0x200023f; const int opcodeAddToLevCreature = 0x20002fb; const int opcodeRemoveFromLevCreature = 0x20002fc; const int opcodeAddToLevItem = 0x20002fd; const int opcodeRemoveFromLevItem = 0x20002fe; } namespace Sky { const int opcodeToggleSky = 0x2000021; const int opcodeTurnMoonWhite = 0x2000022; const int opcodeTurnMoonRed = 0x2000023; const int opcodeGetMasserPhase = 0x2000024; const int opcodeGetSecundaPhase = 0x2000025; const int opcodeGetCurrentWeather = 0x200013f; const int opcodeChangeWeather = 0x2000140; const int opcodeModRegion = 0x20026; } namespace Sound { const int opcodeSay = 0x2000001; const int opcodeSayDone = 0x2000002; const int opcodeStreamMusic = 0x2000003; const int opcodePlaySound = 0x2000004; const int opcodePlaySoundVP = 0x2000005; const int opcodePlaySound3D = 0x2000006; const int opcodePlaySound3DVP = 0x2000007; const int opcodePlayLoopSound3D = 0x2000008; const int opcodePlayLoopSound3DVP = 0x2000009; const int opcodeStopSound = 0x200000a; const int opcodeGetSoundPlaying = 0x200000b; const int opcodeSayExplicit = 0x2000019; const int opcodeSayDoneExplicit = 0x200001a; const int opcodePlaySound3DExplicit = 0x200001b; const int opcodePlaySound3DVPExplicit = 0x200001c; const int opcodePlayLoopSound3DExplicit = 0x200001d; const int opcodePlayLoopSound3DVPExplicit = 0x200001e; const int opcodeStopSoundExplicit = 0x200001f; const int opcodeGetSoundPlayingExplicit = 0x2000020; } namespace Stats { const int numberOfAttributes = 8; const int numberOfDynamics = 3; const int numberOfSkills = 27; const int numberOfMagicEffects = 24; const int opcodeGetAttribute = 0x2000027; const int opcodeGetAttributeExplicit = 0x200002f; const int opcodeSetAttribute = 0x2000037; const int opcodeSetAttributeExplicit = 0x200003f; const int opcodeModAttribute = 0x2000047; const int opcodeModAttributeExplicit = 0x200004f; const int opcodeGetDynamic = 0x2000057; const int opcodeGetDynamicExplicit = 0x200005a; const int opcodeSetDynamic = 0x200005d; const int opcodeSetDynamicExplicit = 0x2000060; const int opcodeModDynamic = 0x2000063; const int opcodeModDynamicExplicit = 0x2000066; const int opcodeModCurrentDynamic = 0x2000069; const int opcodeModCurrentDynamicExplicit = 0x200006c; const int opcodeGetDynamicGetRatio = 0x200006f; const int opcodeGetDynamicGetRatioExplicit = 0x2000072; const int opcodeGetSkill = 0x200008e; const int opcodeGetSkillExplicit = 0x20000a9; const int opcodeSetSkill = 0x20000c4; const int opcodeSetSkillExplicit = 0x20000df; const int opcodeModSkill = 0x20000fa; const int opcodeModSkillExplicit = 0x2000115; const int opcodeGetMagicEffect = 0x2000264; const int opcodeGetMagicEffectExplicit = 0x200027c; const int opcodeSetMagicEffect = 0x2000294; const int opcodeSetMagicEffectExplicit = 0x20002ac; const int opcodeModMagicEffect = 0x20002c4; const int opcodeModMagicEffectExplicit = 0x20002dc; const int opcodeGetPCCrimeLevel = 0x20001ec; const int opcodeSetPCCrimeLevel = 0x20001ed; const int opcodeModPCCrimeLevel = 0x20001ee; const int opcodeAddSpell = 0x2000147; const int opcodeAddSpellExplicit = 0x2000148; const int opcodeRemoveSpell = 0x2000149; const int opcodeRemoveSpellExplicit = 0x200014a; const int opcodeGetSpell = 0x200014b; const int opcodeGetSpellExplicit = 0x200014c; const int opcodePCRaiseRank = 0x2000b; const int opcodePCLowerRank = 0x2000c; const int opcodePCJoinFaction = 0x2000d; const int opcodePCRaiseRankExplicit = 0x20029; const int opcodePCLowerRankExplicit = 0x2002a; const int opcodePCJoinFactionExplicit = 0x2002b; const int opcodeGetPCRank = 0x2000e; const int opcodeGetPCRankExplicit = 0x2000f; const int opcodeModDisposition = 0x200014d; const int opcodeModDispositionExplicit = 0x200014e; const int opcodeSetDisposition = 0x20001a4; const int opcodeSetDispositionExplicit = 0x20001a5; const int opcodeGetDisposition = 0x20001a6; const int opcodeGetDispositionExplicit = 0x20001a7; const int opcodeGetLevel = 0x200018c; const int opcodeGetLevelExplicit = 0x200018d; const int opcodeSetLevel = 0x200018e; const int opcodeSetLevelExplicit = 0x200018f; const int opcodeGetDeadCount = 0x20001a3; const int opcodeGetPCFacRep = 0x20012; const int opcodeGetPCFacRepExplicit = 0x20013; const int opcodeSetPCFacRep = 0x20014; const int opcodeSetPCFacRepExplicit = 0x20015; const int opcodeModPCFacRep = 0x20016; const int opcodeModPCFacRepExplicit = 0x20017; const int opcodeGetCommonDisease = 0x20001a8; const int opcodeGetCommonDiseaseExplicit = 0x20001a9; const int opcodeGetBlightDisease = 0x20001aa; const int opcodeGetBlightDiseaseExplicit = 0x20001ab; const int opcodeGetRace = 0x20001d9; const int opcodeGetRaceExplicit = 0x20001da; const int opcodePcExpelled = 0x20018; const int opcodePcExpelledExplicit = 0x20019; const int opcodePcExpell = 0x2001a; const int opcodePcExpellExplicit = 0x2001b; const int opcodePcClearExpelled = 0x2001c; const int opcodePcClearExpelledExplicit = 0x2001d; const int opcodeRaiseRank = 0x20001e8; const int opcodeRaiseRankExplicit = 0x20001e9; const int opcodeLowerRank = 0x20001ea; const int opcodeLowerRankExplicit = 0x20001eb; const int opcodeOnDeath = 0x20001fc; const int opcodeOnDeathExplicit = 0x2000205; const int opcodeOnMurder = 0x2000249; const int opcodeOnMurderExplicit = 0x200024a; const int opcodeOnKnockout = 0x2000240; const int opcodeOnKnockoutExplicit = 0x2000241; const int opcodeBecomeWerewolf = 0x2000217; const int opcodeBecomeWerewolfExplicit = 0x2000218; const int opcodeUndoWerewolf = 0x2000219; const int opcodeUndoWerewolfExplicit = 0x200021a; const int opcodeSetWerewolfAcrobatics = 0x200021b; const int opcodeSetWerewolfAcrobaticsExplicit = 0x200021c; const int opcodeIsWerewolf = 0x20001fd; const int opcodeIsWerewolfExplicit = 0x20001fe; const int opcodeGetWerewolfKills = 0x20001e2; const int opcodeRemoveSpellEffects = 0x200022b; const int opcodeRemoveSpellEffectsExplicit = 0x200022c; const int opcodeRemoveEffects = 0x200022d; const int opcodeRemoveEffectsExplicit = 0x200022e; const int opcodeResurrect = 0x200022f; const int opcodeResurrectExplicit = 0x2000230; const int opcodeGetStat = 0x200024e; const int opcodeGetStatExplicit = 0x200024f; } namespace Transformation { const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; const int opcodeSetAngleExplicit = 0x2000167; const int opcodeGetScale = 0x2000168; const int opcodeGetScaleExplicit = 0x2000169; const int opcodeGetAngle = 0x200016a; const int opcodeGetAngleExplicit = 0x200016b; const int opcodeGetPos = 0x2000190; const int opcodeGetPosExplicit = 0x2000191; const int opcodeSetPos = 0x2000192; const int opcodeSetPosExplicit = 0x2000193; const int opcodeGetStartingPos = 0x2000194; const int opcodeGetStartingPosExplicit = 0x2000195; const int opcodeGetStartingAngle = 0x2000210; const int opcodeGetStartingAngleExplicit = 0x2000211; const int opcodePosition = 0x2000196; const int opcodePositionExplicit = 0x2000197; const int opcodePositionCell = 0x2000198; const int opcodePositionCellExplicit = 0x2000199; const int opcodePlaceItemCell = 0x200019a; const int opcodePlaceItem = 0x200019b; const int opcodePlaceAtPc = 0x200019c; const int opcodePlaceAtMe = 0x200019d; const int opcodePlaceAtMeExplicit = 0x200019e; const int opcodeModScale = 0x20001e3; const int opcodeModScaleExplicit = 0x20001e4; const int opcodeRotate = 0x20001ff; const int opcodeRotateExplicit = 0x2000200; const int opcodeRotateWorld = 0x2000201; const int opcodeRotateWorldExplicit = 0x2000202; const int opcodeSetAtStart = 0x2000203; const int opcodeSetAtStartExplicit = 0x2000204; const int opcodeMove = 0x2000206; const int opcodeMoveExplicit = 0x2000207; const int opcodeMoveWorld = 0x2000208; const int opcodeMoveWorldExplicit = 0x2000209; const int opcodeResetActors = 0x20002f4; } namespace User { const int opcodeUser1 = 0x200016c; const int opcodeUser2 = 0x200016d; const int opcodeUser3 = 0x200016e; const int opcodeUser3Explicit = 0x200016f; const int opcodeUser4 = 0x2000170; const int opcodeUser4Explicit = 0x2000171; } } #endif openmw-openmw-0.38.0/components/compiler/output.cpp000066400000000000000000000032141264522266000224620ustar00rootroot00000000000000#include "output.hpp" #include #include #include #include "locals.hpp" namespace Compiler { Output::Output (Locals& locals) : mLocals (locals) {} void Output::getCode (std::vector& code) const { code.clear(); // header code.push_back (static_cast (mCode.size())); assert (mLiterals.getIntegerSize()%4==0); code.push_back (static_cast (mLiterals.getIntegerSize()/4)); assert (mLiterals.getFloatSize()%4==0); code.push_back (static_cast (mLiterals.getFloatSize()/4)); assert (mLiterals.getStringSize()%4==0); code.push_back (static_cast (mLiterals.getStringSize()/4)); // code std::copy (mCode.begin(), mCode.end(), std::back_inserter (code)); // literals mLiterals.append (code); } const Literals& Output::getLiterals() const { return mLiterals; } const std::vector& Output::getCode() const { return mCode; } const Locals& Output::getLocals() const { return mLocals; } Literals& Output::getLiterals() { return mLiterals; } std::vector& Output::getCode() { return mCode; } Locals& Output::getLocals() { return mLocals; } void Output::clear() { mLiterals.clear(); mCode.clear(); mLocals.clear(); } } openmw-openmw-0.38.0/components/compiler/output.hpp000066400000000000000000000016741264522266000224770ustar00rootroot00000000000000#ifndef COMPILER_OUTPUT_H_INCLUDED #define COMPILER_OUTPUT_H_INCLUDED #include "literals.hpp" #include #include namespace Compiler { class Locals; class Output { Literals mLiterals; std::vector mCode; Locals& mLocals; public: Output (Locals& locals); void getCode (std::vector& code) const; ///< store generated code in \a code. const Literals& getLiterals() const; const Locals& getLocals() const; const std::vector& getCode() const; Literals& getLiterals(); std::vector& getCode(); Locals& getLocals(); void clear(); }; } #endif openmw-openmw-0.38.0/components/compiler/parser.cpp000066400000000000000000000077041264522266000224260ustar00rootroot00000000000000#include "parser.hpp" #include #include #include #include "errorhandler.hpp" #include "exception.hpp" #include "scanner.hpp" #include namespace Compiler { // Report the error and throw an exception. void Parser::reportSeriousError (const std::string& message, const TokenLoc& loc) { mErrorHandler.error (message, loc); throw SourceException(); } // Report the warning without throwing an exception. void Parser::reportWarning (const std::string& message, const TokenLoc& loc) { mErrorHandler.warning (message, loc); } // Report an unexpected EOF condition. void Parser::reportEOF() { mErrorHandler.endOfFile(); throw EOFException(); } // Return error handler ErrorHandler& Parser::getErrorHandler() { return mErrorHandler; } // Return context const Context& Parser::getContext() const { return mContext; } std::string Parser::toLower (const std::string& name) { std::string lowerCase = Misc::StringUtils::lowerCase(name); return lowerCase; } Parser::Parser (ErrorHandler& errorHandler, const Context& context) : mErrorHandler (errorHandler), mContext (context), mOptional (false), mEmpty (true) {} // destructor Parser::~Parser() {} // Handle an int token. // \return fetch another token? // // - Default-implementation: Report an error. bool Parser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) { if (!(mOptional && mEmpty)) reportSeriousError ("Unexpected numeric value", loc); else scanner.putbackInt (value, loc); return false; } // Handle a float token. // \return fetch another token? // // - Default-implementation: Report an error. bool Parser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) { if (!(mOptional && mEmpty)) reportSeriousError ("Unexpected floating point value", loc); else scanner.putbackFloat (value, loc); return false; } // Handle a name token. // \return fetch another token? // // - Default-implementation: Report an error. bool Parser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (!(mOptional && mEmpty)) reportSeriousError ("Unexpected name", loc); else scanner.putbackName (name, loc); return false; } // Handle a keyword token. // \return fetch another token? // // - Default-implementation: Report an error. bool Parser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (!(mOptional && mEmpty)) reportSeriousError ("Unexpected keyword", loc); else scanner.putbackKeyword (keyword, loc); return false; } // Handle a special character token. // \return fetch another token? // // - Default-implementation: Report an error. bool Parser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (!(mOptional && mEmpty)) reportSeriousError ("Unexpected special token", loc); else scanner.putbackSpecial (code, loc); return false; } bool Parser::parseComment (const std::string& comment, const TokenLoc& loc, Scanner& scanner) { return true; } // Handle an EOF token. // // - Default-implementation: Report an error. void Parser::parseEOF (Scanner& scanner) { reportEOF(); } void Parser::reset() { mOptional = false; mEmpty = true; } void Parser::setOptional (bool optional) { mOptional = optional; } void Parser::start() { mEmpty = false; } bool Parser::isEmpty() const { return mEmpty; } } openmw-openmw-0.38.0/components/compiler/parser.hpp000066400000000000000000000067141264522266000224330ustar00rootroot00000000000000#ifndef COMPILER_PARSER_H_INCLUDED #define COMPILER_PARSER_H_INCLUDED #include namespace Compiler { class Scanner; struct TokenLoc; class ErrorHandler; class Context; /// \brief Parser base class /// /// This class defines a callback-parser. class Parser { ErrorHandler& mErrorHandler; const Context& mContext; bool mOptional; bool mEmpty; protected: void reportSeriousError (const std::string& message, const TokenLoc& loc); ///< Report the error and throw a exception. void reportWarning (const std::string& message, const TokenLoc& loc); ///< Report the warning without throwing an exception. void reportEOF(); ///< Report an unexpected EOF condition. ErrorHandler& getErrorHandler(); ///< Return error handler const Context& getContext() const; ///< Return context static std::string toLower (const std::string& name); public: Parser (ErrorHandler& errorHandler, const Context& context); ///< constructor virtual ~Parser(); ///< destructor virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. /// \return fetch another token? /// /// - Default-implementation: Report an error. virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); ///< Handle a float token. /// \return fetch another token? /// /// - Default-implementation: Report an error. virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? /// /// - Default-implementation: Report an error. virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? /// /// - Default-implementation: Report an error. virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? /// /// - Default-implementation: Report an error. virtual bool parseComment (const std::string& comment, const TokenLoc& loc, Scanner& scanner); ///< Handle comment token. /// \return fetch another token? /// /// - Default-implementation: ignored (and return true). virtual void parseEOF (Scanner& scanner); ///< Handle EOF token. /// /// - Default-implementation: Report an error. virtual void reset(); ///< Reset parser to clean state. void setOptional (bool optional); ///< Optional mode: If nothign has been parsed yet and an unexpected token is delivered, stop /// parsing without raising an exception (after a reset the parser is in non-optional mode). void start(); ///< Mark parser as non-empty (at least one token has been parser). bool isEmpty() const; ///< Has anything been parsed? }; } #endif openmw-openmw-0.38.0/components/compiler/quickfileparser.cpp000066400000000000000000000024551264522266000243210ustar00rootroot00000000000000#include "quickfileparser.hpp" #include "skipparser.hpp" #include "scanner.hpp" Compiler::QuickFileParser::QuickFileParser (ErrorHandler& errorHandler, const Context& context, Locals& locals) : Parser (errorHandler, context), mDeclarationParser (errorHandler, context, locals) {} bool Compiler::QuickFileParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return true; } bool Compiler::QuickFileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (keyword==Scanner::K_end) return false; if (keyword==Scanner::K_short || keyword==Scanner::K_long || keyword==Scanner::K_float) { mDeclarationParser.reset(); scanner.putbackKeyword (keyword, loc); scanner.scan (mDeclarationParser); return true; } SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return true; } bool Compiler::QuickFileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code!=Scanner::S_newline) { SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); } return true; } void Compiler::QuickFileParser::parseEOF (Scanner& scanner) { } openmw-openmw-0.38.0/components/compiler/quickfileparser.hpp000066400000000000000000000022061264522266000243200ustar00rootroot00000000000000#ifndef COMPILER_QUICKFILEPARSER_H_INCLUDED #define COMPILER_QUICKFILEPARSER_H_INCLUDED #include "parser.hpp" #include "declarationparser.hpp" namespace Compiler { class Locals; /// \brief File parser variant that ignores everything but variable declarations class QuickFileParser : public Parser { DeclarationParser mDeclarationParser; public: QuickFileParser (ErrorHandler& errorHandler, const Context& context, Locals& locals); virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? virtual void parseEOF (Scanner& scanner); ///< Handle EOF token. }; } #endif openmw-openmw-0.38.0/components/compiler/scanner.cpp000066400000000000000000000371271264522266000225650ustar00rootroot00000000000000#include "scanner.hpp" #include #include #include #include #include #include "exception.hpp" #include "errorhandler.hpp" #include "parser.hpp" #include "extensions.hpp" #include namespace Compiler { bool Scanner::get (char& c) { mStream.get (c); if (!mStream.good()) return false; mPrevLoc =mLoc; if (c=='\n') { mStrictKeywords = false; mLoc.mColumn = 0; ++mLoc.mLine; mLoc.mLiteral.clear(); } else { ++mLoc.mColumn; mLoc.mLiteral += c; } return true; } void Scanner::putback (char c) { mStream.putback (c); mLoc = mPrevLoc; } bool Scanner::scanToken (Parser& parser) { switch (mPutback) { case Putback_Special: mPutback = Putback_None; return parser.parseSpecial (mPutbackCode, mPutbackLoc, *this); case Putback_Integer: mPutback = Putback_None; return parser.parseInt (mPutbackInteger, mPutbackLoc, *this); case Putback_Float: mPutback = Putback_None; return parser.parseFloat (mPutbackFloat, mPutbackLoc, *this); case Putback_Name: mPutback = Putback_None; return parser.parseName (mPutbackName, mPutbackLoc, *this); case Putback_Keyword: mPutback = Putback_None; return parser.parseKeyword (mPutbackCode, mPutbackLoc, *this); case Putback_None: break; } char c; if (!get (c)) { parser.parseEOF (*this); return false; } else if (c==';') { std::string comment; comment += c; while (get (c)) { if (c=='\n') { putback (c); break; } else comment += c; } TokenLoc loc (mLoc); mLoc.mLiteral.clear(); return parser.parseComment (comment, loc, *this); } else if (isWhitespace (c)) { mLoc.mLiteral.clear(); return true; } else if (c==':') { // treat : as a whitespace :( mLoc.mLiteral.clear(); return true; } else if (std::isalpha (c) || c=='_' || c=='"') { bool cont = false; if (scanName (c, parser, cont)) { mLoc.mLiteral.clear(); return cont; } } else if (std::isdigit (c)) { bool cont = false; if (scanInt (c, parser, cont)) { mLoc.mLiteral.clear(); return cont; } } else if (c==13) // linux compatibility hack { return true; } else { bool cont = false; if (scanSpecial (c, parser, cont)) { mLoc.mLiteral.clear(); return cont; } } TokenLoc loc (mLoc); mLoc.mLiteral.clear(); mErrorHandler.error ("syntax error", loc); throw SourceException(); } bool Scanner::scanInt (char c, Parser& parser, bool& cont) { assert(c != '\0'); std::string value; value += c; bool error = false; while (get (c)) { if (std::isdigit (c)) { value += c; } else if (c!='-' && isStringCharacter (c)) { error = true; value += c; } else if (c=='.') { if (error) { putback (c); break; } return scanFloat (value, parser, cont); } else { putback (c); break; } } if (error) { /// workaround that allows names to begin with digits /// \todo disable TokenLoc loc (mLoc); mLoc.mLiteral.clear(); cont = parser.parseName (value, loc, *this); return true; // return false; } TokenLoc loc (mLoc); mLoc.mLiteral.clear(); std::istringstream stream (value); int intValue = 0; stream >> intValue; cont = parser.parseInt (intValue, loc, *this); return true; } bool Scanner::scanFloat (const std::string& intValue, Parser& parser, bool& cont) { std::string value = intValue + "."; char c; bool empty = intValue.empty() || intValue=="-"; bool error = false; while (get (c)) { if (std::isdigit (c)) { value += c; empty = false; } else if (std::isalpha (c) || c=='_') error = true; else { putback (c); break; } } if (empty || error) return false; TokenLoc loc (mLoc); mLoc.mLiteral.clear(); std::istringstream stream (value); float floatValue = 0; stream >> floatValue; cont = parser.parseFloat (floatValue, loc, *this); return true; } static const char *keywords[] = { "begin", "end", "short", "long", "float", "if", "endif", "else", "elseif", "while", "endwhile", "return", "messagebox", "set", "to", "getsquareroot", "menumode", "random", "startscript", "stopscript", "scriptrunning", "getdistance", "getsecondspassed", "enable", "disable", "getdisabled", 0 }; bool Scanner::scanName (char c, Parser& parser, bool& cont) { std::string name; name += c; if (!scanName (name)) return false; TokenLoc loc (mLoc); mLoc.mLiteral.clear(); if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"') { name = name.substr (1, name.size()-2); // allow keywords enclosed in "" /// \todo optionally disable if (mStrictKeywords) { cont = parser.parseName (name, loc, *this); return true; } } int i = 0; std::string lowerCase = Misc::StringUtils::lowerCase(name); for (; keywords[i]; ++i) if (lowerCase==keywords[i]) break; if (keywords[i]) { cont = parser.parseKeyword (i, loc, *this); return true; } if (mExtensions) { if (int keyword = mExtensions->searchKeyword (lowerCase)) { cont = parser.parseKeyword (keyword, loc, *this); return true; } } cont = parser.parseName (name, loc, *this); return true; } bool Scanner::scanName (std::string& name) { char c; bool error = false; while (get (c)) { if (!name.empty() && name[0]=='"') { if (c=='"') { name += c; break; } // ignoring escape sequences for now, because they are messing up stupid Windows path names. // else if (c=='\\') // { // if (!get (c)) // { // error = true; // mErrorHandler.error ("incomplete escape sequence", mLoc); // break; // } // } else if (c=='\n') { error = true; mErrorHandler.error ("incomplete string or name", mLoc); break; } } else if (!(c=='"' && name.empty())) { if (!isStringCharacter (c)) { putback (c); break; } } name += c; } return !error; } bool Scanner::scanSpecial (char c, Parser& parser, bool& cont) { int special = -1; if (c=='\n') special = S_newline; else if (c=='(' || c=='[') /// \todo option to disable the use of [ as alias for ( special = S_open; else if (c==')' || c==']') /// \todo option to disable the use of ] as alias for ) special = S_close; else if (c=='.') { // check, if this starts a float literal if (get (c)) { putback (c); if (std::isdigit (c)) return scanFloat ("", parser, cont); } special = S_member; } else if (c=='=') { if (get (c)) { /// \todo hack to allow a space in comparison operators (add option to disable) if (c==' ' && !get (c)) special = S_cmpEQ; else if (c=='=') special = S_cmpEQ; else { special = S_cmpEQ; putback (c); // return false; /// Allow = as synonym for ==. \todo optionally disable for post-1.0 scripting improvements. } } else { putback (c); return false; } } else if (c=='!') { if (get (c)) { /// \todo hack to allow a space in comparison operators (add option to disable) if (c==' ' && !get (c)) return false; if (c=='=') special = S_cmpNE; else { putback (c); return false; } } else return false; } else if (c=='-') { if (get (c)) { if (c=='>') special = S_ref; else { putback (c); special = S_minus; } } else special = S_minus; } else if (static_cast (c)==0xe2) { /// Workaround for some translator who apparently can't keep his minus in order /// \todo disable for later script formats if (get (c) && static_cast (c)==0x80 && get (c) && static_cast (c)==0x93) { if (get (c)) { if (c=='>') special = S_ref; else { putback (c); special = S_minus; } } else special = S_minus; } else { mErrorHandler.error ("Invalid character", mLoc); return false; } } else if (c=='<') { if (get (c)) { /// \todo hack to allow a space in comparison operators (add option to disable) if (c==' ' && !get (c)) special = S_cmpLT; else if (c=='=') { special = S_cmpLE; if (get (c) && c!='=') // <== is a allowed as an alternative to <= :( putback (c); } else { putback (c); special = S_cmpLT; } } else special = S_cmpLT; } else if (c=='>') { if (get (c)) { /// \todo hack to allow a space in comparison operators (add option to disable) if (c==' ' && !get (c)) special = S_cmpGT; else if (c=='=') { special = S_cmpGE; if (get (c) && c!='=') // >== is a allowed as an alternative to >= :( putback (c); } else { putback (c); special = S_cmpGT; } } else special = S_cmpGT; } else if (c==',') special = S_comma; else if (c=='+') special = S_plus; else if (c=='*') special = S_mult; else if (c=='/') special = S_div; else return false; if (special==S_newline) mLoc.mLiteral = ""; TokenLoc loc (mLoc); mLoc.mLiteral.clear(); cont = parser.parseSpecial (special, loc, *this); return true; } bool Scanner::isStringCharacter (char c, bool lookAhead) { return std::isalpha (c) || std::isdigit (c) || c=='_' || /// \todo disable this when doing more stricter compiling c=='`' || c=='\'' || /// \todo disable this when doing more stricter compiling. Also, find out who is /// responsible for allowing it in the first place and meet up with that person in /// a dark alley. (c=='-' && (!lookAhead || isStringCharacter (mStream.peek(), false))); } bool Scanner::isWhitespace (char c) { return c==' ' || c=='\t'; } // constructor Scanner::Scanner (ErrorHandler& errorHandler, std::istream& inputStream, const Extensions *extensions) : mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions), mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0), mStrictKeywords (false) { } void Scanner::scan (Parser& parser) { while (scanToken (parser)); } void Scanner::putbackSpecial (int code, const TokenLoc& loc) { mPutback = Putback_Special; mPutbackCode = code; mPutbackLoc = loc; } void Scanner::putbackInt (int value, const TokenLoc& loc) { mPutback = Putback_Integer; mPutbackInteger = value; mPutbackLoc = loc; } void Scanner::putbackFloat (float value, const TokenLoc& loc) { mPutback = Putback_Float; mPutbackFloat = value; mPutbackLoc = loc; } void Scanner::putbackName (const std::string& name, const TokenLoc& loc) { mPutback = Putback_Name; mPutbackName = name; mPutbackLoc = loc; } void Scanner::putbackKeyword (int keyword, const TokenLoc& loc) { mPutback = Putback_Keyword; mPutbackCode = keyword; mPutbackLoc = loc; } void Scanner::listKeywords (std::vector& keywords) { for (int i=0; Compiler::keywords[i]; ++i) keywords.push_back (Compiler::keywords[i]); if (mExtensions) mExtensions->listKeywords (keywords); } void Scanner::enableStrictKeywords() { mStrictKeywords = true; } } openmw-openmw-0.38.0/components/compiler/scanner.hpp000066400000000000000000000074111264522266000225630ustar00rootroot00000000000000#ifndef COMPILER_SCANNER_H_INCLUDED #define COMPILER_SCANNER_H_INCLUDED #include #include #include #include "tokenloc.hpp" namespace Compiler { class ErrorHandler; class Parser; class Extensions; /// \brief Scanner /// /// This class translate a char-stream to a token stream (delivered via /// parser-callbacks). class Scanner { enum putback_type { Putback_None, Putback_Special, Putback_Integer, Putback_Float, Putback_Name, Putback_Keyword }; ErrorHandler& mErrorHandler; TokenLoc mLoc; TokenLoc mPrevLoc; std::istream& mStream; const Extensions *mExtensions; putback_type mPutback; int mPutbackCode; int mPutbackInteger; float mPutbackFloat; std::string mPutbackName; TokenLoc mPutbackLoc; bool mStrictKeywords; public: enum keyword { K_begin, K_end, K_short, K_long, K_float, K_if, K_endif, K_else, K_elseif, K_while, K_endwhile, K_return, K_messagebox, K_set, K_to, K_getsquareroot, K_menumode, K_random, K_startscript, K_stopscript, K_scriptrunning, K_getdistance, K_getsecondspassed, K_enable, K_disable, K_getdisabled }; enum special { S_newline, S_open, S_close, S_cmpEQ, S_cmpNE, S_cmpLT, S_cmpLE, S_cmpGT, S_cmpGE, S_plus, S_minus, S_mult, S_div, S_comma, S_ref, S_member }; private: // not implemented Scanner (const Scanner&); Scanner& operator= (const Scanner&); bool get (char& c); void putback (char c); bool scanToken (Parser& parser); bool scanInt (char c, Parser& parser, bool& cont); bool scanFloat (const std::string& intValue, Parser& parser, bool& cont); bool scanName (char c, Parser& parser, bool& cont); /// \param name May contain the start of the name (one or more characters) bool scanName (std::string& name); bool scanSpecial (char c, Parser& parser, bool& cont); bool isStringCharacter (char c, bool lookAhead = true); static bool isWhitespace (char c); public: Scanner (ErrorHandler& errorHandler, std::istream& inputStream, const Extensions *extensions = 0); ///< constructor void scan (Parser& parser); ///< Scan a token and deliver it to the parser. void putbackSpecial (int code, const TokenLoc& loc); ///< put back a special token void putbackInt (int value, const TokenLoc& loc); ///< put back an integer token void putbackFloat (float value, const TokenLoc& loc); ///< put back a float token void putbackName (const std::string& name, const TokenLoc& loc); ///< put back a name token void putbackKeyword (int keyword, const TokenLoc& loc); ///< put back a keyword token void listKeywords (std::vector& keywords); ///< Append all known keywords to \a keywords. /// Do not accept keywords in quotation marks anymore. /// /// \attention This mode lasts only until the next newline is reached. void enableStrictKeywords(); }; } #endif openmw-openmw-0.38.0/components/compiler/scriptparser.cpp000066400000000000000000000052021264522266000236420ustar00rootroot00000000000000#include "scriptparser.hpp" #include "scanner.hpp" #include "skipparser.hpp" #include "errorhandler.hpp" namespace Compiler { ScriptParser::ScriptParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, bool end) : Parser (errorHandler, context), mOutput (locals), mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()), mControlParser (errorHandler, context, locals, mOutput.getLiterals()), mEnd (end) {} void ScriptParser::getCode (std::vector& code) const { mOutput.getCode (code); } bool ScriptParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { mLineParser.reset(); if (mLineParser.parseName (name, loc, scanner)) scanner.scan (mLineParser); return true; } bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (keyword==Scanner::K_while || keyword==Scanner::K_if || keyword==Scanner::K_elseif) { mControlParser.reset(); if (mControlParser.parseKeyword (keyword, loc, scanner)) scanner.scan (mControlParser); mControlParser.appendCode (mOutput.getCode()); return true; } /// \todo add an option to disable this nonsense if (keyword==Scanner::K_endif) { // surplus endif getErrorHandler().warning ("endif without matching if/elseif", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return true; } if (keyword==Scanner::K_end && mEnd) { return false; } mLineParser.reset(); if (mLineParser.parseKeyword (keyword, loc, scanner)) scanner.scan (mLineParser); return true; } bool ScriptParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_newline) // empty line return true; if (code==Scanner::S_open) /// \todo Option to switch this off { scanner.putbackSpecial (code, loc); return parseKeyword (Scanner::K_if, loc, scanner); } mLineParser.reset(); if (mLineParser.parseSpecial (code, loc, scanner)) scanner.scan (mLineParser); return true; } void ScriptParser::parseEOF (Scanner& scanner) { if (mEnd) Parser::parseEOF (scanner); } void ScriptParser::reset() { mLineParser.reset(); mOutput.clear(); } } openmw-openmw-0.38.0/components/compiler/scriptparser.hpp000066400000000000000000000030321264522266000236460ustar00rootroot00000000000000#ifndef COMPILER_SCRIPTPARSER_H_INCLUDED #define COMPILER_SCRIPTPARSER_H_INCLUDED #include "parser.hpp" #include "lineparser.hpp" #include "controlparser.hpp" #include "output.hpp" namespace Compiler { class Locals; // Script parser, to be used in dialogue scripts and as part of FileParser class ScriptParser : public Parser { Output mOutput; LineParser mLineParser; ControlParser mControlParser; bool mEnd; public: /// \param end of script is marked by end keyword. ScriptParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, bool end = false); void getCode (std::vector& code) const; ///< store generated code in \a code. virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? virtual void parseEOF (Scanner& scanner); ///< Handle EOF token. void reset(); ///< Reset parser to clean state. }; } #endif openmw-openmw-0.38.0/components/compiler/skipparser.cpp000066400000000000000000000015531264522266000233110ustar00rootroot00000000000000#include "skipparser.hpp" #include "scanner.hpp" namespace Compiler { SkipParser::SkipParser (ErrorHandler& errorHandler, const Context& context) : Parser (errorHandler, context) {} bool SkipParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) { return true; } bool SkipParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) { return true; } bool SkipParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { return true; } bool SkipParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { return true; } bool SkipParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_newline) return false; return true; } } openmw-openmw-0.38.0/components/compiler/skipparser.hpp000066400000000000000000000024511264522266000233140ustar00rootroot00000000000000#ifndef COMPILER_SKIPPARSER_H_INCLUDED #define COMPILER_SKIPPARSER_H_INCLUDED #include "parser.hpp" namespace Compiler { // \brief Skip parser for skipping a line // // This parser is mainly intended for skipping the rest of a faulty line. class SkipParser : public Parser { public: SkipParser (ErrorHandler& errorHandler, const Context& context); virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. /// \return fetch another token? virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); ///< Handle a float token. /// \return fetch another token? virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? }; } #endif openmw-openmw-0.38.0/components/compiler/streamerrorhandler.cpp000066400000000000000000000017161264522266000250320ustar00rootroot00000000000000#include "streamerrorhandler.hpp" #include "tokenloc.hpp" namespace Compiler { // Report error to the user. void StreamErrorHandler::report (const std::string& message, const TokenLoc& loc, Type type) { if (type==ErrorMessage) mStream << "error "; else mStream << "warning "; mStream << "line " << loc.mLine+1 << ", column " << loc.mColumn+1 << " (" << loc.mLiteral << ")" << std::endl << " " << message << std::endl; } // Report a file related error void StreamErrorHandler::report (const std::string& message, Type type) { if (type==ErrorMessage) mStream << "error "; else mStream << "warning "; mStream << "file:" << std::endl << " " << message << std::endl; } StreamErrorHandler::StreamErrorHandler (std::ostream& ErrorStream) : mStream (ErrorStream) {} } openmw-openmw-0.38.0/components/compiler/streamerrorhandler.hpp000066400000000000000000000015761264522266000250430ustar00rootroot00000000000000#ifndef COMPILER_STREAMERRORHANDLER_H_INCLUDED #define COMPILER_STREAMERRORHANDLER_H_INCLUDED #include #include "errorhandler.hpp" namespace Compiler { /// \brief Error handler implementation: Write errors into stream class StreamErrorHandler : public ErrorHandler { std::ostream& mStream; // not implemented StreamErrorHandler (const StreamErrorHandler&); StreamErrorHandler& operator= (const StreamErrorHandler&); virtual void report (const std::string& message, const TokenLoc& loc, Type type); ///< Report error to the user. virtual void report (const std::string& message, Type type); ///< Report a file related error public: // constructors StreamErrorHandler (std::ostream& ErrorStream); ///< constructor }; } #endif openmw-openmw-0.38.0/components/compiler/stringparser.cpp000066400000000000000000000045231264522266000236510ustar00rootroot00000000000000#include "stringparser.hpp" #include #include #include #include "scanner.hpp" #include "generator.hpp" #include "context.hpp" #include "extensions.hpp" namespace Compiler { StringParser::StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals) : Parser (errorHandler, context), mLiterals (literals), mState (StartState), mSmashCase (false) { } bool StringParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (mState==StartState || mState==CommaState) { start(); if (mSmashCase) Generator::pushString (mCode, mLiterals, Misc::StringUtils::lowerCase (name)); else Generator::pushString (mCode, mLiterals, name); return false; } return Parser::parseName (name, loc, scanner); } bool StringParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (const Extensions *extensions = getContext().getExtensions()) { std::string argumentType; // ignored bool hasExplicit = false; // ignored if (extensions->isInstruction (keyword, argumentType, hasExplicit)) { // pretend this is not a keyword std::string name = loc.mLiteral; if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"') name = name.substr (1, name.size()-2); return parseName (name, loc, scanner); } } return Parser::parseKeyword (keyword, loc, scanner); } bool StringParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_comma && mState==StartState) { mState = CommaState; return true; } return Parser::parseSpecial (code, loc, scanner); } void StringParser::append (std::vector& code) { std::copy (mCode.begin(), mCode.end(), std::back_inserter (code)); } void StringParser::reset() { mState = StartState; mCode.clear(); mSmashCase = false; Parser::reset(); } void StringParser::smashCase() { mSmashCase = true; } } openmw-openmw-0.38.0/components/compiler/stringparser.hpp000066400000000000000000000027521264522266000236600ustar00rootroot00000000000000#ifndef COMPILER_STRINGPARSER_H_INCLUDED #define COMPILER_STRINGPARSER_H_INCLUDED #include #include #include "parser.hpp" namespace Compiler { class Literals; class StringParser : public Parser { enum State { StartState, CommaState }; Literals& mLiterals; State mState; std::vector mCode; bool mSmashCase; public: StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals); virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. /// \return fetch another token? virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); ///< Handle a keyword token. /// \return fetch another token? virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? void append (std::vector& code); ///< Append code for parsed string. void smashCase(); ///< Transform all scanned strings to lower case void reset(); ///< Reset parser to clean state (this includes the smashCase function). }; } #endif openmw-openmw-0.38.0/components/compiler/tokenloc.hpp000066400000000000000000000005571264522266000227540ustar00rootroot00000000000000#ifndef COMPILER_TOKENLOC_H_INCLUDED #define COMPILER_TOKENLOC_H_INCLUDED #include namespace Compiler { /// \brief Location of a token in a source file struct TokenLoc { int mColumn; int mLine; std::string mLiteral; TokenLoc() : mColumn (0), mLine (0), mLiteral ("") {} }; } #endif // TOKENLOC_H_INCLUDED openmw-openmw-0.38.0/components/config/000077500000000000000000000000001264522266000200515ustar00rootroot00000000000000openmw-openmw-0.38.0/components/config/gamesettings.cpp000066400000000000000000000346661264522266000232660ustar00rootroot00000000000000#include "gamesettings.hpp" #include "launchersettings.hpp" #include #include #include #include #include #include #include #include /** * Workaround for problems with whitespaces in paths in older versions of Boost library */ #if (BOOST_VERSION <= 104600) namespace boost { template<> inline boost::filesystem::path lexical_cast(const std::string& arg) { return boost::filesystem::path(arg); } } /* namespace boost */ #endif /* (BOOST_VERSION <= 104600) */ const char Config::GameSettings::sContentKey[] = "content"; Config::GameSettings::GameSettings(Files::ConfigurationManager &cfg) : mCfgMgr(cfg) { } Config::GameSettings::~GameSettings() { } void Config::GameSettings::validatePaths() { QStringList paths = mSettings.values(QString("data")); Files::PathContainer dataDirs; foreach (const QString &path, paths) { QByteArray bytes = path.toUtf8(); dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length()))); } // Parse the data dirs to convert the tokenized paths mCfgMgr.processPaths(dataDirs); mDataDirs.clear(); for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { QString path = QString::fromUtf8(it->string().c_str()); path.remove(QChar('\"')); QDir dir(path); if (dir.exists()) mDataDirs.append(path); } // Do the same for data-local QString local = mSettings.value(QString("data-local")); if (local.isEmpty()) return; dataDirs.clear(); QByteArray bytes = local.toUtf8(); dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length()))); mCfgMgr.processPaths(dataDirs); if (!dataDirs.empty()) { QString path = QString::fromUtf8(dataDirs.front().string().c_str()); path.remove(QChar('\"')); QDir dir(path); if (dir.exists()) mDataLocal = path; } } QStringList Config::GameSettings::values(const QString &key, const QStringList &defaultValues) const { if (!mSettings.values(key).isEmpty()) return mSettings.values(key); return defaultValues; } bool Config::GameSettings::readFile(QTextStream &stream) { return readFile(stream, mSettings); } bool Config::GameSettings::readUserFile(QTextStream &stream) { return readFile(stream, mUserSettings); } bool Config::GameSettings::readFile(QTextStream &stream, QMap &settings) { QMap cache; QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; if (keyRe.indexIn(line) != -1) { QString key = keyRe.cap(1).trimmed(); QString value = keyRe.cap(2).trimmed(); // Don't remove existing data entries if (key != QLatin1String("data")) settings.remove(key); QStringList values = cache.values(key); values.append(settings.values(key)); if (!values.contains(value)) { cache.insertMulti(key, value); } } } if (settings.isEmpty()) { settings = cache; // This is the first time we read a file validatePaths(); return true; } // Merge the changed keys with those which didn't settings.unite(cache); validatePaths(); return true; } bool Config::GameSettings::writeFile(QTextStream &stream) { // Iterate in reverse order to preserve insertion order QMapIterator i(mUserSettings); i.toBack(); while (i.hasPrevious()) { i.previous(); // Quote paths with spaces if (i.key() == QLatin1String("data") || i.key() == QLatin1String("data-local") || i.key() == QLatin1String("resources")) { if (i.value().contains(QChar(' '))) { QString stripped = i.value(); stripped.remove(QChar('\"')); // Remove quotes stream << i.key() << "=\"" << stripped << "\"\n"; continue; } } stream << i.key() << "=" << i.value() << "\n"; } return true; } bool Config::GameSettings::isOrderedLine(const QString& line) const { return line.contains(QRegExp("^\\s*fallback-archive\\s*=")) || line.contains(QRegExp("^\\s*fallback\\s*=")) || line.contains(QRegExp("^\\s*data\\s*=")) || line.contains(QRegExp("^\\s*data-local\\s*=")) || line.contains(QRegExp("^\\s*resources\\s*=")) || line.contains(QRegExp("^\\s*content\\s*=")); } // Policy: // // - Always ignore a line beginning with '#' or empty lines; added above a config // entry. // // - If a line in file exists with matching key and first part of value (before ',', // '\n', etc) also matches, then replace the line with that of mUserSettings. // - else remove line // // - If there is no corresponding line in file, add at the end // // - Removed content items are saved as comments if the item had any comments. // Content items prepended with '##' are considered previously removed. // bool Config::GameSettings::writeFileWithComments(QFile &file) { QTextStream stream(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); // slurp std::vector fileCopy; QString line = stream.readLine(); while (!line.isNull()) { fileCopy.push_back(line); line = stream.readLine(); } stream.seek(0); // empty file, no comments to keep if (fileCopy.empty()) return writeFile(stream); // start // | // | +----------------------------------------------------------+ // | | | // v v | // skip non-"ordered" lines (remove "ordered" lines) | // | ^ | // | | | // | non-"ordered" line, write saved comments | // | ^ | // v | | // blank or comment line, save in temp buffer <--------+ | // | | | | // | +------- comment line ------+ | // v (special processing '##') | // "ordered" line | // | | // v | // save in a separate map of comments keyed by "ordered" line | // | | // +----------------------------------------------------------+ // // QRegExp settingRegex("^([^=]+)\\s*=\\s*([^,]+)(.*)$"); std::vector comments; std::vector::iterator commentStart = fileCopy.end(); std::map > commentsMap; for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) { if (isOrderedLine(*iter)) { // save in a separate map of comments keyed by "ordered" line if (!comments.empty()) { if (settingRegex.indexIn(*iter) != -1) { commentsMap[settingRegex.cap(1)+"="+settingRegex.cap(2)] = comments; comments.clear(); commentStart = fileCopy.end(); } // else do nothing, malformed line } *iter = QString(); // "ordered" lines to be removed later } else if ((*iter).isEmpty() || (*iter).contains(QRegExp("^\\s*#"))) { // comment line, save in temp buffer if (comments.empty()) commentStart = iter; // special removed content processing if ((*iter).contains(QRegExp("^##content\\s*="))) { if (!comments.empty()) { commentsMap[*iter] = comments; comments.clear(); commentStart = fileCopy.end(); } } else comments.push_back(*iter); *iter = QString(); // assume to be deleted later } else { int index = settingRegex.indexIn(*iter); // blank or non-"ordered" line, write saved comments if (!comments.empty() && index != -1 && settingRegex.captureCount() >= 2 && mUserSettings.find(settingRegex.cap(1)) != mUserSettings.end()) { for (std::vector::const_iterator it = comments.begin(); it != comments.end(); ++it) { *commentStart = *it; ++commentStart; } comments.clear(); commentStart = fileCopy.end(); } // keep blank lines and non-"ordered" lines other than comments // look for a key in the line if (index == -1 || settingRegex.captureCount() < 2) { // no key or first part of value found in line, replace with a null string which // will be remved later *iter = QString(); comments.clear(); commentStart = fileCopy.end(); continue; } // look for a matching key in user settings *iter = QString(); // assume no match QString key = settingRegex.cap(1); QString keyVal = settingRegex.cap(1)+"="+settingRegex.cap(2); QMap::const_iterator i = mUserSettings.find(key); while (i != mUserSettings.end() && i.key() == key) { QString settingLine = i.key() + "=" + i.value(); if (settingRegex.indexIn(settingLine) != -1) { if ((settingRegex.cap(1)+"="+settingRegex.cap(2)) == keyVal) { *iter = settingLine; break; } } ++i; } } } // comments at top of file for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) { if ((*iter).isNull()) continue; // Below is based on readFile() code, if that changes corresponding change may be // required (for example duplicates may be inserted if the rules don't match) if (/*(*iter).isEmpty() ||*/ (*iter).contains(QRegExp("^\\s*#"))) { stream << *iter << "\n"; continue; } } // Iterate in reverse order to preserve insertion order QString settingLine; QMapIterator it(mUserSettings); it.toBack(); while (it.hasPrevious()) { it.previous(); // Quote paths with spaces if ((it.key() == QLatin1String("data") || it.key() == QLatin1String("data-local") || it.key() == QLatin1String("resources")) && it.value().contains(QChar(' '))) { QString stripped = it.value(); stripped.remove(QChar('\"')); // Remove quotes settingLine = it.key() + "=\"" + stripped + "\""; } else settingLine = it.key() + "=" + it.value(); if (settingRegex.indexIn(settingLine) != -1) { std::map >::iterator i = commentsMap.find(settingRegex.cap(1)+"="+settingRegex.cap(2)); // check if previous removed content item with comments if (i == commentsMap.end()) i = commentsMap.find("##"+settingRegex.cap(1)+"="+settingRegex.cap(2)); if (i != commentsMap.end()) { std::vector cLines = i->second; for (std::vector::const_iterator ci = cLines.begin(); ci != cLines.end(); ++ci) stream << *ci << "\n"; commentsMap.erase(i); } } stream << settingLine << "\n"; } // flush any removed settings if (!commentsMap.empty()) { std::map >::const_iterator i = commentsMap.begin(); for (; i != commentsMap.end(); ++i) { if (i->first.contains(QRegExp("^\\s*content\\s*="))) { std::vector cLines = i->second; for (std::vector::const_iterator ci = cLines.begin(); ci != cLines.end(); ++ci) stream << *ci << "\n"; // mark the content line entry for future preocessing stream << "##" << i->first << "\n"; //commentsMap.erase(i); } } } // flush any end comments if (!comments.empty()) { for (std::vector::const_iterator ci = comments.begin(); ci != comments.end(); ++ci) stream << *ci << "\n"; } file.resize(file.pos()); return true; } bool Config::GameSettings::hasMaster() { bool result = false; QStringList content = mSettings.values(QString(Config::GameSettings::sContentKey)); for (int i = 0; i < content.count(); ++i) { if (content.at(i).contains(".omwgame") || content.at(i).contains(".esm")) { result = true; break; } } return result; } void Config::GameSettings::setContentList(const QStringList& fileNames) { remove(sContentKey); foreach(const QString& fileName, fileNames) { setMultiValue(sContentKey, fileName); } } QStringList Config::GameSettings::getContentList() const { // QMap returns multiple rows in LIFO order, so need to reverse return Config::LauncherSettings::reverse(values(sContentKey)); } void Config::GameSettings::clear() { mSettings.clear(); mUserSettings.clear(); mDataDirs.clear(); mDataLocal.clear(); } openmw-openmw-0.38.0/components/config/gamesettings.hpp000066400000000000000000000050231264522266000232540ustar00rootroot00000000000000#ifndef GAMESETTINGS_HPP #define GAMESETTINGS_HPP #include #include #include #include #include #include namespace Files { typedef std::vector PathContainer; struct ConfigurationManager; } namespace Config { class GameSettings { public: GameSettings(Files::ConfigurationManager &cfg); ~GameSettings(); inline QString value(const QString &key, const QString &defaultValue = QString()) { return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); } inline void setValue(const QString &key, const QString &value) { mSettings.insert(key, value); mUserSettings.insert(key, value); } inline void setMultiValue(const QString &key, const QString &value) { QStringList values = mSettings.values(key); if (!values.contains(value)) mSettings.insertMulti(key, value); values = mUserSettings.values(key); if (!values.contains(value)) mUserSettings.insertMulti(key, value); } inline void remove(const QString &key) { mSettings.remove(key); mUserSettings.remove(key); } inline QStringList getDataDirs() { return mDataDirs; } inline void removeDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.removeAll(dir); } inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } inline QString getDataLocal() {return mDataLocal; } bool hasMaster(); QStringList values(const QString &key, const QStringList &defaultValues = QStringList()) const; bool readFile(QTextStream &stream); bool readFile(QTextStream &stream, QMap &settings); bool readUserFile(QTextStream &stream); bool writeFile(QTextStream &stream); bool writeFileWithComments(QFile &file); void setContentList(const QStringList& fileNames); QStringList getContentList() const; void clear(); private: Files::ConfigurationManager &mCfgMgr; void validatePaths(); QMap mSettings; QMap mUserSettings; QStringList mDataDirs; QString mDataLocal; static const char sContentKey[]; bool isOrderedLine(const QString& line) const; }; } #endif // GAMESETTINGS_HPP openmw-openmw-0.38.0/components/config/launchersettings.cpp000066400000000000000000000125511264522266000241430ustar00rootroot00000000000000#include "launchersettings.hpp" #include #include #include #include #include const char Config::LauncherSettings::sCurrentContentListKey[] = "Profiles/currentprofile"; const char Config::LauncherSettings::sLauncherConfigFileName[] = "launcher.cfg"; const char Config::LauncherSettings::sContentListsSectionPrefix[] = "Profiles/"; const char Config::LauncherSettings::sContentListSuffix[] = "/content"; Config::LauncherSettings::LauncherSettings() { } Config::LauncherSettings::~LauncherSettings() { } QStringList Config::LauncherSettings::subKeys(const QString &key) { QMap settings = SettingsBase::getSettings(); QStringList keys = settings.uniqueKeys(); QRegExp keyRe("(.+)/"); QStringList result; foreach (const QString ¤tKey, keys) { if (keyRe.indexIn(currentKey) != -1) { QString prefixedKey = keyRe.cap(1); if(prefixedKey.startsWith(key)) { QString subKey = prefixedKey.remove(key); if (!subKey.isEmpty()) result.append(subKey); } } } result.removeDuplicates(); return result; } bool Config::LauncherSettings::writeFile(QTextStream &stream) { QString sectionPrefix; QRegExp sectionRe("([^/]+)/(.+)$"); QMap settings = SettingsBase::getSettings(); QMapIterator i(settings); i.toBack(); while (i.hasPrevious()) { i.previous(); QString prefix; QString key; if (sectionRe.exactMatch(i.key())) { prefix = sectionRe.cap(1); key = sectionRe.cap(2); } // Get rid of legacy settings if (key.contains(QChar('\\'))) continue; if (key == QLatin1String("CurrentProfile")) continue; if (sectionPrefix != prefix) { sectionPrefix = prefix; stream << "\n[" << prefix << "]\n"; } stream << key << "=" << i.value() << "\n"; } return true; } QStringList Config::LauncherSettings::getContentLists() { return subKeys(QString(sContentListsSectionPrefix)); } QString Config::LauncherSettings::makeContentListKey(const QString& contentListName) { return QString(sContentListsSectionPrefix) + contentListName + QString(sContentListSuffix); } void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) { // obtain content list from game settings (if present) const QStringList files(gameSettings.getContentList()); // if openmw.cfg has no content, exit so we don't create an empty content list. if (files.isEmpty()) { return; } // if any existing profile in launcher matches the content list, make that profile the default foreach(const QString &listName, getContentLists()) { if (isEqual(files, getContentListFiles(listName))) { setCurrentContentListName(listName); return; } } // otherwise, add content list QString newContentListName(makeNewContentListName()); setCurrentContentListName(newContentListName); setContentList(newContentListName, files); } void Config::LauncherSettings::removeContentList(const QString &contentListName) { remove(makeContentListKey(contentListName)); } void Config::LauncherSettings::setCurrentContentListName(const QString &contentListName) { remove(QString(sCurrentContentListKey)); setValue(QString(sCurrentContentListKey), contentListName); } void Config::LauncherSettings::setContentList(const QString& contentListName, const QStringList& fileNames) { removeContentList(contentListName); QString key = makeContentListKey(contentListName); foreach(const QString& fileName, fileNames) { setMultiValue(key, fileName); } } QString Config::LauncherSettings::getCurrentContentListName() const { return value(QString(sCurrentContentListKey)); } QStringList Config::LauncherSettings::getContentListFiles(const QString& contentListName) const { // QMap returns multiple rows in LIFO order, so need to reverse return reverse(getSettings().values(makeContentListKey(contentListName))); } QStringList Config::LauncherSettings::reverse(const QStringList& toReverse) { QStringList result; result.reserve(toReverse.size()); std::reverse_copy(toReverse.begin(), toReverse.end(), std::back_inserter(result)); return result; } bool Config::LauncherSettings::isEqual(const QStringList& list1, const QStringList& list2) { if (list1.count() != list2.count()) { return false; } for (int i = 0; i < list1.count(); ++i) { if (list1.at(i) != list2.at(i)) { return false; } } // if get here, lists are same return true; } QString Config::LauncherSettings::makeNewContentListName() { // basically, use date and time as the name e.g. YYYY-MM-DDThh:mm:ss time_t rawtime; struct tm * timeinfo; time(&rawtime); timeinfo = localtime(&rawtime); int base = 10; QChar zeroPad('0'); return QString("%1-%2-%3T%4:%5:%6") .arg(timeinfo->tm_year + 1900, 4).arg(timeinfo->tm_mon + 1, 2, base, zeroPad).arg(timeinfo->tm_mday, 2, base, zeroPad) .arg(timeinfo->tm_hour, 2, base, zeroPad).arg(timeinfo->tm_min, 2, base, zeroPad).arg(timeinfo->tm_sec, 2, base, zeroPad); } openmw-openmw-0.38.0/components/config/launchersettings.hpp000066400000000000000000000037021264522266000241460ustar00rootroot00000000000000#ifndef LAUNCHERSETTINGS_HPP #define LAUNCHERSETTINGS_HPP #include "settingsbase.hpp" #include "gamesettings.hpp" namespace Config { class LauncherSettings : public SettingsBase > { public: LauncherSettings(); ~LauncherSettings(); bool writeFile(QTextStream &stream); /// \return names of all Content Lists in the launcher's .cfg file. QStringList getContentLists(); /// Set initially selected content list to match values from openmw.cfg, creating if necessary void setContentList(const GameSettings& gameSettings); /// Create a Content List (or replace if it already exists) void setContentList(const QString& contentListName, const QStringList& fileNames); void removeContentList(const QString &contentListName); void setCurrentContentListName(const QString &contentListName); QString getCurrentContentListName() const; QStringList getContentListFiles(const QString& contentListName) const; /// \return new list that is reversed order of input static QStringList reverse(const QStringList& toReverse); static const char sLauncherConfigFileName[]; private: /// \return key to use to get/set the files in the specified Content List static QString makeContentListKey(const QString& contentListName); /// \return true if both lists are same static bool isEqual(const QStringList& list1, const QStringList& list2); static QString makeNewContentListName(); QStringList subKeys(const QString &key); /// name of entry in launcher.cfg that holds name of currently selected Content List static const char sCurrentContentListKey[]; /// section of launcher.cfg holding the Content Lists static const char sContentListsSectionPrefix[]; static const char sContentListSuffix[]; }; } #endif // LAUNCHERSETTINGS_HPP openmw-openmw-0.38.0/components/config/settingsbase.hpp000066400000000000000000000057211264522266000232620ustar00rootroot00000000000000#ifndef SETTINGSBASE_HPP #define SETTINGSBASE_HPP #include #include #include #include #include namespace Config { template class SettingsBase { public: SettingsBase() { mMultiValue = false; } ~SettingsBase() {} inline QString value(const QString &key, const QString &defaultValue = QString()) const { return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); } inline void setValue(const QString &key, const QString &value) { QStringList values = mSettings.values(key); if (!values.contains(value)) mSettings.insert(key, value); } inline void setMultiValue(const QString &key, const QString &value) { QStringList values = mSettings.values(key); if (!values.contains(value)) mSettings.insertMulti(key, value); } inline void setMultiValueEnabled(bool enable) { mMultiValue = enable; } inline void remove(const QString &key) { mSettings.remove(key); } Map getSettings() const {return mSettings;} bool readFile(QTextStream &stream) { Map cache; QString sectionPrefix; QRegExp sectionRe("^\\[([^]]+)\\]"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; if (sectionRe.exactMatch(line)) { sectionPrefix = sectionRe.cap(1); sectionPrefix.append("/"); continue; } if (keyRe.indexIn(line) != -1) { QString key = keyRe.cap(1).trimmed(); QString value = keyRe.cap(2).trimmed(); if (!sectionPrefix.isEmpty()) key.prepend(sectionPrefix); mSettings.remove(key); QStringList values = cache.values(key); if (!values.contains(value)) { if (mMultiValue) { cache.insertMulti(key, value); } else { cache.insert(key, value); } } } } if (mSettings.isEmpty()) { mSettings = cache; // This is the first time we read a file return true; } // Merge the changed keys with those which didn't mSettings.unite(cache); return true; } void clear() { mSettings.clear(); } private: Map mSettings; bool mMultiValue; }; } #endif // SETTINGSBASE_HPP openmw-openmw-0.38.0/components/contentselector/000077500000000000000000000000001264522266000220175ustar00rootroot00000000000000openmw-openmw-0.38.0/components/contentselector/model/000077500000000000000000000000001264522266000231175ustar00rootroot00000000000000openmw-openmw-0.38.0/components/contentselector/model/contentmodel.cpp000066400000000000000000000466511264522266000263320ustar00rootroot00000000000000#include "contentmodel.hpp" #include "esmfile.hpp" #include #include #include #include #include ContentSelectorModel::ContentModel::ContentModel(QObject *parent, QIcon warningIcon) : QAbstractTableModel(parent), mWarningIcon(warningIcon), mMimeType ("application/omwcontent"), mMimeTypes (QStringList() << mMimeType), mColumnCount (1), mDropActions (Qt::MoveAction) { setEncoding ("win1252"); uncheckAll(); } ContentSelectorModel::ContentModel::~ContentModel() { qDeleteAll(mFiles); mFiles.clear(); } void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding) { mEncoding = encoding; } int ContentSelectorModel::ContentModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return mColumnCount; } int ContentSelectorModel::ContentModel::rowCount(const QModelIndex &parent) const { if(parent.isValid()) return 0; return mFiles.size(); } const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) const { if (row >= 0 && row < mFiles.size()) return mFiles.at(row); return 0; } ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) { if (row >= 0 && row < mFiles.count()) return mFiles.at(row); return 0; } const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(const QString &name) const { EsmFile::FileProperty fp = EsmFile::FileProperty_FileName; if (name.contains ('/')) fp = EsmFile::FileProperty_FilePath; foreach (const EsmFile *file, mFiles) { if (name.compare(file->fileProperty (fp).toString(), Qt::CaseInsensitive) == 0) return file; } return 0; } QModelIndex ContentSelectorModel::ContentModel::indexFromItem(const EsmFile *item) const { //workaround: non-const pointer cast for calls from outside contentmodel/contentselector EsmFile *non_const_file_ptr = const_cast(item); if (item) return index(mFiles.indexOf(non_const_file_ptr),0); return QModelIndex(); } Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::ItemIsDropEnabled; const EsmFile *file = item(index.row()); if (!file) return Qt::NoItemFlags; //game files can always be checked if (file->isGameFile()) return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; Qt::ItemFlags returnFlags; // addon can be checked if its gamefile is // ... special case, addon with no dependency can be used with any gamefile. bool gamefileChecked = (file->gameFiles().count() == 0); foreach (const QString &fileName, file->gameFiles()) { foreach (EsmFile *dependency, mFiles) { //compare filenames only. Multiple instances //of the filename (with different paths) is not relevant here. bool depFound = (dependency->fileName().compare(fileName, Qt::CaseInsensitive) == 0); if (!depFound) continue; if (!gamefileChecked) { if (isChecked (dependency->filePath())) gamefileChecked = (dependency->isGameFile()); } // force it to iterate all files in cases where the current // dependency is a game file to ensure that a later duplicate // game file is / is not checked. // (i.e., break only if it's not a gamefile or the game file has been checked previously) if (gamefileChecked || !(dependency->isGameFile())) break; } } if (gamefileChecked) { returnFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled; } return returnFlags; } QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() >= mFiles.size()) return QVariant(); const EsmFile *file = item(index.row()); if (!file) return QVariant(); const int column = index.column(); switch (role) { case Qt::DecorationRole: { return isLoadOrderError(file) ? mWarningIcon : QVariant(); } case Qt::EditRole: case Qt::DisplayRole: { if (column >=0 && column <=EsmFile::FileProperty_GameFile) return file->fileProperty(static_cast(column)); return QVariant(); } case Qt::TextAlignmentRole: { switch (column) { case 0: case 1: return Qt::AlignLeft + Qt::AlignVCenter; case 2: case 3: return Qt::AlignRight + Qt::AlignVCenter; default: return Qt::AlignLeft + Qt::AlignVCenter; } } case Qt::ToolTipRole: { if (column != 0) return QVariant(); return toolTip(file); } case Qt::CheckStateRole: { if (file->isGameFile()) return QVariant(); return mCheckStates[file->filePath()]; } case Qt::UserRole: { if (file->isGameFile()) return ContentType_GameFile; else if (flags(index)) return ContentType_Addon; break; } case Qt::UserRole + 1: return isChecked(file->filePath()); } return QVariant(); } bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isValid()) return false; EsmFile *file = item(index.row()); QString fileName = file->fileName(); bool success = false; switch(role) { case Qt::EditRole: { QStringList list = value.toStringList(); for (int i = 0; i < EsmFile::FileProperty_GameFile; i++) file->setFileProperty(static_cast(i), list.at(i)); for (int i = EsmFile::FileProperty_GameFile; i < list.size(); i++) file->setFileProperty (EsmFile::FileProperty_GameFile, list.at(i)); emit dataChanged(index, index); success = true; } break; case Qt::UserRole+1: { success = (flags (index) & Qt::ItemIsEnabled); if (success) { success = setCheckState(file->filePath(), value.toBool()); emit dataChanged(index, index); } } break; case Qt::CheckStateRole: { int checkValue = value.toInt(); bool setState = false; if ((checkValue==Qt::Checked) && !isChecked(file->filePath())) { setState = true; success = true; } else if ((checkValue == Qt::Checked) && isChecked (file->filePath())) setState = true; else if (checkValue == Qt::Unchecked) setState = true; if (setState) { setCheckState(file->filePath(), success); emit dataChanged(index, index); checkForLoadOrderErrors(); } else return success; foreach (EsmFile *file, mFiles) { if (file->gameFiles().contains(fileName, Qt::CaseInsensitive)) { QModelIndex idx = indexFromItem(file); emit dataChanged(idx, idx); } } success = true; } break; } return success; } bool ContentSelectorModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent) { if (parent.isValid()) return false; beginInsertRows(parent, position, position+rows-1); { for (int row = 0; row < rows; ++row) mFiles.insert(position, new EsmFile); } endInsertRows(); return true; } bool ContentSelectorModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent) { if (parent.isValid()) return false; beginRemoveRows(parent, position, position+rows-1); { for (int row = 0; row < rows; ++row) delete mFiles.takeAt(position); } endRemoveRows(); // at this point we know that drag and drop has finished. checkForLoadOrderErrors(); return true; } Qt::DropActions ContentSelectorModel::ContentModel::supportedDropActions() const { return mDropActions; } QStringList ContentSelectorModel::ContentModel::mimeTypes() const { return mMimeTypes; } QMimeData *ContentSelectorModel::ContentModel::mimeData(const QModelIndexList &indexes) const { QByteArray encodedData; foreach (const QModelIndex &index, indexes) { if (!index.isValid()) continue; encodedData.append(item(index.row())->encodedData()); } QMimeData *mimeData = new QMimeData(); mimeData->setData(mMimeType, encodedData); return mimeData; } bool ContentSelectorModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (action == Qt::IgnoreAction) return true; if (column > 0) return false; if (!data->hasFormat(mMimeType)) return false; int beginRow = rowCount(); if (row != -1) beginRow = row; else if (parent.isValid()) beginRow = parent.row(); QByteArray encodedData = data->data(mMimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { QString value; QStringList values; QStringList gamefiles; for (int i = 0; i < EsmFile::FileProperty_GameFile; ++i) { stream >> value; values << value; } stream >> gamefiles; insertRows(beginRow, 1); QModelIndex idx = index(beginRow++, 0, QModelIndex()); setData(idx, QStringList() << values << gamefiles, Qt::EditRole); } return true; } void ContentSelectorModel::ContentModel::addFile(EsmFile *file) { beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); mFiles.append(file); endInsertRows(); QModelIndex idx = index (mFiles.size() - 2, 0, QModelIndex()); emit dataChanged (idx, idx); } void ContentSelectorModel::ContentModel::addFiles(const QString &path) { QDir dir(path); QStringList filters; filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; dir.setNameFilters(filters); foreach (const QString &path, dir.entryList()) { QFileInfo info(dir.absoluteFilePath(path)); if (item(info.absoluteFilePath()) != 0) continue; try { ESM::ESMReader fileReader; ToUTF8::Utf8Encoder encoder = ToUTF8::calculateEncoding(mEncoding.toStdString()); fileReader.setEncoder(&encoder); fileReader.open(std::string(dir.absoluteFilePath(path).toUtf8().constData())); EsmFile *file = new EsmFile(path); foreach (const ESM::Header::MasterData &item, fileReader.getGameFiles()) file->addGameFile(QString::fromUtf8(item.name.c_str())); file->setAuthor (QString::fromUtf8(fileReader.getAuthor().c_str())); file->setDate (info.lastModified()); file->setFormat (fileReader.getFormat()); file->setFilePath (info.absoluteFilePath()); file->setDescription(QString::fromUtf8(fileReader.getDesc().c_str())); // HACK // Load order constraint of Bloodmoon.esm needing Tribunal.esm is missing // from the file supplied by Bethesda, so we have to add it ourselves if (file->fileName().compare("Bloodmoon.esm", Qt::CaseInsensitive) == 0) { file->addGameFile(QString::fromUtf8("Tribunal.esm")); } // Put the file in the table addFile(file); } catch(std::runtime_error &e) { // An error occurred while reading the .esp qWarning() << "Error reading addon file: " << e.what(); continue; } } sortFiles(); } void ContentSelectorModel::ContentModel::clearFiles() { beginRemoveRows(QModelIndex(), 0, mFiles.count()-1); mFiles.clear(); endRemoveRows(); } QStringList ContentSelectorModel::ContentModel::gameFiles() const { QStringList gameFiles; foreach(const ContentSelectorModel::EsmFile *file, mFiles) { if (file->isGameFile()) { gameFiles.append(file->fileName()); } } return gameFiles; } void ContentSelectorModel::ContentModel::sortFiles() { //first, sort the model such that all dependencies are ordered upstream (gamefile) first. bool movedFiles = true; int fileCount = mFiles.size(); //Dependency sort //iterate until no sorting of files occurs while (movedFiles) { movedFiles = false; //iterate each file, obtaining a reference to it's gamefiles list for (int i = 0; i < fileCount; i++) { QModelIndex idx1 = index (i, 0, QModelIndex()); const QStringList &gamefiles = mFiles.at(i)->gameFiles(); //iterate each file after the current file, verifying that none of it's //dependencies appear. for (int j = i + 1; j < fileCount; j++) { if (gamefiles.contains(mFiles.at(j)->fileName(), Qt::CaseInsensitive)) { mFiles.move(j, i); QModelIndex idx2 = index (j, 0, QModelIndex()); emit dataChanged (idx1, idx2); movedFiles = true; } } if (movedFiles) break; } } } bool ContentSelectorModel::ContentModel::isChecked(const QString& filepath) const { if (mCheckStates.contains(filepath)) return (mCheckStates[filepath] == Qt::Checked); return false; } bool ContentSelectorModel::ContentModel::isEnabled (QModelIndex index) const { return (flags(index) & Qt::ItemIsEnabled); } bool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile *file) const { return mPluginsWithLoadOrderError.contains(file->filePath()); } void ContentSelectorModel::ContentModel::setContentList(const QStringList &fileList) { mPluginsWithLoadOrderError.clear(); int previousPosition = -1; foreach (const QString &filepath, fileList) { if (setCheckState(filepath, true)) { // as necessary, move plug-ins in visible list to match sequence of supplied filelist const EsmFile* file = item(filepath); int filePosition = indexFromItem(file).row(); if (filePosition < previousPosition) { mFiles.move(filePosition, previousPosition); emit dataChanged(index(filePosition, 0, QModelIndex()), index(previousPosition, 0, QModelIndex())); } else { previousPosition = filePosition; } } } checkForLoadOrderErrors(); } void ContentSelectorModel::ContentModel::checkForLoadOrderErrors() { for (int row = 0; row < mFiles.count(); ++row) { EsmFile* file = item(row); bool isRowInError = checkForLoadOrderErrors(file, row).count() != 0; if (isRowInError) { mPluginsWithLoadOrderError.insert(file->filePath()); } else { mPluginsWithLoadOrderError.remove(file->filePath()); } } } QList ContentSelectorModel::ContentModel::checkForLoadOrderErrors(const EsmFile *file, int row) const { QList errors = QList(); foreach(const QString &dependentfileName, file->gameFiles()) { const EsmFile* dependentFile = item(dependentfileName); if (!dependentFile) { errors.append(LoadOrderError(LoadOrderError::ErrorCode_MissingDependency, dependentfileName)); } else { if (!isChecked(dependentFile->filePath())) { errors.append(LoadOrderError(LoadOrderError::ErrorCode_InactiveDependency, dependentfileName)); } if (row < indexFromItem(dependentFile).row()) { errors.append(LoadOrderError(LoadOrderError::ErrorCode_LoadOrder, dependentfileName)); } } } return errors; } QString ContentSelectorModel::ContentModel::toolTip(const EsmFile *file) const { if (isLoadOrderError(file)) { QString text(""); int index = indexFromItem(item(file->filePath())).row(); foreach(const LoadOrderError& error, checkForLoadOrderErrors(file, index)) { text += "

"; text += error.toolTip(); text += "

"; } text += ("
"); text += file->toolTip(); return text; } else { return file->toolTip(); } } void ContentSelectorModel::ContentModel::refreshModel() { emit dataChanged (index(0,0), index(rowCount()-1,0)); } bool ContentSelectorModel::ContentModel::setCheckState(const QString &filepath, bool checkState) { if (filepath.isEmpty()) return false; const EsmFile *file = item(filepath); if (!file) return false; Qt::CheckState state = Qt::Unchecked; if (checkState) state = Qt::Checked; mCheckStates[filepath] = state; emit dataChanged(indexFromItem(item(filepath)), indexFromItem(item(filepath))); if (file->isGameFile()) refreshModel(); //if we're checking an item, ensure all "upstream" files (dependencies) are checked as well. if (state == Qt::Checked) { foreach (QString upstreamName, file->gameFiles()) { const EsmFile *upstreamFile = item(upstreamName); if (!upstreamFile) continue; if (!isChecked(upstreamFile->filePath())) mCheckStates[upstreamFile->filePath()] = Qt::Checked; emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile)); } } //otherwise, if we're unchecking an item (or the file is a game file) ensure all downstream files are unchecked. if (state == Qt::Unchecked) { foreach (const EsmFile *downstreamFile, mFiles) { QFileInfo fileInfo(filepath); QString filename = fileInfo.fileName(); if (downstreamFile->gameFiles().contains(filename, Qt::CaseInsensitive)) { if (mCheckStates.contains(downstreamFile->filePath())) mCheckStates[downstreamFile->filePath()] = Qt::Unchecked; emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile)); } } } return true; } ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checkedItems() const { ContentFileList list; // TODO: // First search for game files and next addons, // so we get more or less correct game files vs addons order. foreach (EsmFile *file, mFiles) if (isChecked(file->filePath())) list << file; return list; } void ContentSelectorModel::ContentModel::uncheckAll() { emit layoutAboutToBeChanged(); mCheckStates.clear(); emit layoutChanged(); } openmw-openmw-0.38.0/components/contentselector/model/contentmodel.hpp000066400000000000000000000057541264522266000263360ustar00rootroot00000000000000#ifndef CONTENTMODEL_HPP #define CONTENTMODEL_HPP #include #include #include #include #include "loadordererror.hpp" namespace ContentSelectorModel { class EsmFile; typedef QList ContentFileList; enum ContentType { ContentType_GameFile, ContentType_Addon }; class ContentModel : public QAbstractTableModel { Q_OBJECT public: explicit ContentModel(QObject *parent, QIcon warningIcon); ~ContentModel(); void setEncoding(const QString &encoding); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()); Qt::DropActions supportedDropActions() const; QStringList mimeTypes() const; QMimeData *mimeData(const QModelIndexList &indexes) const; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); void addFiles(const QString &path); void clearFiles(); QModelIndex indexFromItem(const EsmFile *item) const; const EsmFile *item(const QString &name) const; QStringList gameFiles() const; bool isEnabled (QModelIndex index) const; bool isChecked(const QString &filepath) const; bool setCheckState(const QString &filepath, bool isChecked); void setContentList(const QStringList &fileList); ContentFileList checkedItems() const; void uncheckAll(); void refreshModel(); /// Checks all plug-ins for load order errors and updates mPluginsWithLoadOrderError with plug-ins with issues void checkForLoadOrderErrors(); private: void addFile(EsmFile *file); const EsmFile *item(int row) const; EsmFile *item(int row); void sortFiles(); /// Checks a specific plug-in for load order errors /// \return all errors found for specific plug-in QList checkForLoadOrderErrors(const EsmFile *file, int row) const; /// \return true if plug-in has a Load Order error bool isLoadOrderError(const EsmFile *file) const; QString toolTip(const EsmFile *file) const; ContentFileList mFiles; QHash mCheckStates; QSet mPluginsWithLoadOrderError; QString mEncoding; QIcon mWarningIcon; public: QString mMimeType; QStringList mMimeTypes; int mColumnCount; Qt::DropActions mDropActions; }; } #endif // CONTENTMODEL_HPP openmw-openmw-0.38.0/components/contentselector/model/esmfile.cpp000066400000000000000000000066051264522266000252560ustar00rootroot00000000000000#include "esmfile.hpp" #include #include int ContentSelectorModel::EsmFile::sPropertyCount = 7; QString ContentSelectorModel::EsmFile::sToolTip = QString("Author: %1
\ Version: %2
\ Modified: %3
\ Path:
%4
\
Description:
%5
\
Dependencies: %6
"); ContentSelectorModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) : ModelItem(parent), mFileName(fileName), mFormat(0) {} void ContentSelectorModel::EsmFile::setFileName(const QString &fileName) { mFileName = fileName; } void ContentSelectorModel::EsmFile::setAuthor(const QString &author) { mAuthor = author; } void ContentSelectorModel::EsmFile::setDate(const QDateTime &modified) { mModified = modified; } void ContentSelectorModel::EsmFile::setFormat(int format) { mFormat = format; } void ContentSelectorModel::EsmFile::setFilePath(const QString &path) { mPath = path; } void ContentSelectorModel::EsmFile::setGameFiles(const QStringList &gamefiles) { mGameFiles = gamefiles; } void ContentSelectorModel::EsmFile::setDescription(const QString &description) { mDescription = description; } QByteArray ContentSelectorModel::EsmFile::encodedData() const { QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); stream << mFileName << mAuthor << QString::number(mFormat) << mModified.toString() << mPath << mDescription << mGameFiles; return encodedData; } bool ContentSelectorModel::EsmFile::isGameFile() const { return (mGameFiles.size() == 0) && (mFileName.endsWith(QLatin1String(".esm"), Qt::CaseInsensitive) || mFileName.endsWith(QLatin1String(".omwgame"), Qt::CaseInsensitive)); } QVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) const { switch (prop) { case FileProperty_FileName: return mFileName; break; case FileProperty_Author: return mAuthor; break; case FileProperty_Format: return mFormat; break; case FileProperty_DateModified: return mModified.toString(Qt::ISODate); break; case FileProperty_FilePath: return mPath; break; case FileProperty_Description: return mDescription; break; case FileProperty_GameFile: return mGameFiles; break; default: break; } return QVariant(); } void ContentSelectorModel::EsmFile::setFileProperty (const FileProperty prop, const QString &value) { switch (prop) { case FileProperty_FileName: mFileName = value; break; case FileProperty_Author: mAuthor = value; break; case FileProperty_Format: mFormat = value.toInt(); break; case FileProperty_DateModified: mModified = QDateTime::fromString(value); break; case FileProperty_FilePath: mPath = value; break; case FileProperty_Description: mDescription = value; break; case FileProperty_GameFile: mGameFiles << value; break; default: break; } } openmw-openmw-0.38.0/components/contentselector/model/esmfile.hpp000066400000000000000000000055561264522266000252670ustar00rootroot00000000000000#ifndef ESMFILE_HPP #define ESMFILE_HPP #include #include #include "modelitem.hpp" class QMimeData; namespace ContentSelectorModel { class EsmFile : public ModelItem { Q_OBJECT Q_PROPERTY(QString filename READ fileName) public: enum FileProperty { FileProperty_FileName = 0, FileProperty_Author = 1, FileProperty_Format = 2, FileProperty_DateModified = 3, FileProperty_FilePath = 4, FileProperty_Description = 5, FileProperty_GameFile = 6 }; EsmFile(QString fileName = QString(), ModelItem *parent = 0); // EsmFile(const EsmFile &); ~EsmFile() {} void setFileProperty (const FileProperty prop, const QString &value); void setFileName(const QString &fileName); void setAuthor(const QString &author); void setSize(const int size); void setDate(const QDateTime &modified); void setFormat(const int format); void setFilePath(const QString &path); void setGameFiles(const QStringList &gameFiles); void setDescription(const QString &description); inline void addGameFile (const QString &name) {mGameFiles.append(name); } QVariant fileProperty (const FileProperty prop) const; inline QString fileName() const { return mFileName; } inline QString author() const { return mAuthor; } inline QDateTime modified() const { return mModified; } inline float format() const { return mFormat; } inline QString filePath() const { return mPath; } /// @note Contains file names, not paths. inline const QStringList &gameFiles() const { return mGameFiles; } inline QString description() const { return mDescription; } inline QString toolTip() const { return sToolTip.arg(mAuthor) .arg(mFormat) .arg(mModified.toString(Qt::ISODate)) .arg(mPath) .arg(mDescription) .arg(mGameFiles.join(", ")); } bool isGameFile() const; QByteArray encodedData() const; public: static int sPropertyCount; static QString sToolTip; private: QString mFileName; QString mAuthor; QDateTime mModified; int mFormat; QString mPath; QStringList mGameFiles; QString mDescription; QString mToolTip; }; } #endif openmw-openmw-0.38.0/components/contentselector/model/loadordererror.cpp000066400000000000000000000006711264522266000266540ustar00rootroot00000000000000#include "loadordererror.hpp" #include QString ContentSelectorModel::LoadOrderError::sErrorToolTips[ErrorCode_LoadOrder] = { QString("Unable to find dependent file: %1"), QString("Dependent file needs to be active: %1"), QString("This file needs to load after %1") }; QString ContentSelectorModel::LoadOrderError::toolTip() const { assert(mErrorCode); return sErrorToolTips[mErrorCode - 1].arg(mFileName); } openmw-openmw-0.38.0/components/contentselector/model/loadordererror.hpp000066400000000000000000000017561264522266000266660ustar00rootroot00000000000000#ifndef LOADORDERERROR_HPP #define LOADORDERERROR_HPP #include namespace ContentSelectorModel { /// \brief Details of a suspected Load Order problem a plug-in will have. This is basically a POD. class LoadOrderError { public: enum ErrorCode { ErrorCode_None = 0, ErrorCode_MissingDependency = 1, ErrorCode_InactiveDependency = 2, ErrorCode_LoadOrder = 3 }; inline LoadOrderError() : mErrorCode(ErrorCode_None) {} inline LoadOrderError(ErrorCode errorCode, QString fileName) : mErrorCode(errorCode), mFileName(fileName) {} inline ErrorCode errorCode() const { return mErrorCode; } inline QString fileName() const { return mFileName; } QString toolTip() const; private: ErrorCode mErrorCode; QString mFileName; static QString sErrorToolTips[ErrorCode_LoadOrder]; }; } #endif // LOADORDERERROR_HPP openmw-openmw-0.38.0/components/contentselector/model/modelitem.cpp000066400000000000000000000026121264522266000256030ustar00rootroot00000000000000#include "modelitem.hpp" ContentSelectorModel::ModelItem::ModelItem(ModelItem *parent) : mParentItem(parent) { } /* ContentSelectorModel::ModelItem::ModelItem(const ModelItem *parent) // : mParentItem(parent) { } */ ContentSelectorModel::ModelItem::~ModelItem() { qDeleteAll(mChildItems); } ContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::parent() const { return mParentItem; } bool ContentSelectorModel::ModelItem::hasFormat(const QString &mimetype) const { if (mimetype == "application/omwcontent") return true; return QMimeData::hasFormat(mimetype); } int ContentSelectorModel::ModelItem::row() const { if (mParentItem) return 1; //return mParentItem->childRow(const_cast(this)); //return mParentItem->mChildItems.indexOf(const_cast(this)); return -1; } int ContentSelectorModel::ModelItem::childCount() const { return mChildItems.count(); } int ContentSelectorModel::ModelItem::childRow(ModelItem *child) const { Q_ASSERT(child); return mChildItems.indexOf(child); } ContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::child(int row) { return mChildItems.value(row); } void ContentSelectorModel::ModelItem::appendChild(ModelItem *item) { mChildItems.append(item); } void ContentSelectorModel::ModelItem::removeChild(int row) { mChildItems.removeAt(row); } openmw-openmw-0.38.0/components/contentselector/model/modelitem.hpp000066400000000000000000000014111264522266000256040ustar00rootroot00000000000000#ifndef MODELITEM_HPP #define MODELITEM_HPP #include #include namespace ContentSelectorModel { class ModelItem : public QMimeData { Q_OBJECT public: ModelItem(ModelItem *parent = 0); //ModelItem(const ModelItem *parent = 0); ~ModelItem(); ModelItem *parent() const; int row() const; int childCount() const; int childRow(ModelItem *child) const; ModelItem *child(int row); void appendChild(ModelItem *child); void removeChild(int row); bool hasFormat(const QString &mimetype) const; //virtual bool acceptChild(ModelItem *child); protected: ModelItem *mParentItem; QList mChildItems; }; } #endif openmw-openmw-0.38.0/components/contentselector/model/naturalsort.cpp000066400000000000000000000066751264522266000262170ustar00rootroot00000000000000/* * This file contains code found in the QtGui module of the Qt Toolkit. * See Qt's qfilesystemmodel source files for more information */ #include "naturalsort.hpp" static inline QChar getNextChar(const QString &s, int location) { return (location < s.length()) ? s.at(location) : QChar(); } /*! * Natural number sort, skips spaces. * * Examples: * 1, 2, 10, 55, 100 * 01.jpg, 2.jpg, 10.jpg * * Note on the algorithm: * Only as many characters as necessary are looked at and at most they all * are looked at once. * * Slower then QString::compare() (of course) */ int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs) { for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) { // skip spaces, tabs and 0's QChar c1 = getNextChar(s1, l1); while (c1.isSpace()) c1 = getNextChar(s1, ++l1); QChar c2 = getNextChar(s2, l2); while (c2.isSpace()) c2 = getNextChar(s2, ++l2); if (c1.isDigit() && c2.isDigit()) { while (c1.digitValue() == 0) c1 = getNextChar(s1, ++l1); while (c2.digitValue() == 0) c2 = getNextChar(s2, ++l2); int lookAheadLocation1 = l1; int lookAheadLocation2 = l2; int currentReturnValue = 0; // find the last digit, setting currentReturnValue as we go if it isn't equal for ( QChar lookAhead1 = c1, lookAhead2 = c2; (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); lookAhead1 = getNextChar(s1, ++lookAheadLocation1), lookAhead2 = getNextChar(s2, ++lookAheadLocation2) ) { bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit(); bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit(); if (!is1ADigit && !is2ADigit) break; if (!is1ADigit) return -1; if (!is2ADigit) return 1; if (currentReturnValue == 0) { if (lookAhead1 < lookAhead2) { currentReturnValue = -1; } else if (lookAhead1 > lookAhead2) { currentReturnValue = 1; } } } if (currentReturnValue != 0) return currentReturnValue; } if (cs == Qt::CaseInsensitive) { if (!c1.isLower()) c1 = c1.toLower(); if (!c2.isLower()) c2 = c2.toLower(); } int r = QString::localeAwareCompare(c1, c2); if (r < 0) return -1; if (r > 0) return 1; } // The two strings are the same (02 == 2) so fall back to the normal sort return QString::compare(s1, s2, cs); } bool naturalSortLessThanCS( const QString &left, const QString &right ) { return (naturalCompare( left, right, Qt::CaseSensitive ) < 0); } bool naturalSortLessThanCI( const QString &left, const QString &right ) { return (naturalCompare( left, right, Qt::CaseInsensitive ) < 0); } bool naturalSortGreaterThanCS( const QString &left, const QString &right ) { return (naturalCompare( left, right, Qt::CaseSensitive ) > 0); } bool naturalSortGreaterThanCI( const QString &left, const QString &right ) { return (naturalCompare( left, right, Qt::CaseInsensitive ) > 0); } openmw-openmw-0.38.0/components/contentselector/model/naturalsort.hpp000066400000000000000000000006031264522266000262050ustar00rootroot00000000000000#ifndef NATURALSORT_H #define NATURALSORT_H #include bool naturalSortLessThanCS( const QString &left, const QString &right ); bool naturalSortLessThanCI( const QString &left, const QString &right ); bool naturalSortGreaterThanCS( const QString &left, const QString &right ); bool naturalSortGreaterThanCI( const QString &left, const QString &right ); #endif openmw-openmw-0.38.0/components/contentselector/view/000077500000000000000000000000001264522266000227715ustar00rootroot00000000000000openmw-openmw-0.38.0/components/contentselector/view/combobox.cpp000066400000000000000000000021421264522266000253040ustar00rootroot00000000000000#include #include #include #include #include #include "combobox.hpp" ContentSelectorView::ComboBox::ComboBox(QWidget *parent) : QComboBox(parent) { mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore setValidator(mValidator); setCompleter(0); setEnabled (true); setInsertPolicy(QComboBox::NoInsert); } void ContentSelectorView::ComboBox::paintEvent(QPaintEvent *) { QStylePainter painter(this); painter.setPen(palette().color(QPalette::Text)); // draw the combobox frame, focusrect and selected etc. QStyleOptionComboBox opt; initStyleOption(&opt); painter.drawComplexControl(QStyle::CC_ComboBox, opt); // draw the icon and text if (!opt.editable && currentIndex() == -1) // <<< we adjust the text displayed when nothing is selected opt.currentText = mPlaceholderText; painter.drawControl(QStyle::CE_ComboBoxLabel, opt); } void ContentSelectorView::ComboBox::setPlaceholderText(const QString &text) { mPlaceholderText = text; } openmw-openmw-0.38.0/components/contentselector/view/combobox.hpp000066400000000000000000000010031264522266000253040ustar00rootroot00000000000000#ifndef COMBOBOX_HPP #define COMBOBOX_HPP #include #include class QString; class QRegExpValidator; namespace ContentSelectorView { class ComboBox : public QComboBox { Q_OBJECT public: explicit ComboBox (QWidget *parent = 0); void setPlaceholderText(const QString &text); private: QString mPlaceholderText; protected: void paintEvent(QPaintEvent *); QRegExpValidator *mValidator; }; } #endif // COMBOBOX_HPP openmw-openmw-0.38.0/components/contentselector/view/contentselector.cpp000066400000000000000000000162741264522266000267220ustar00rootroot00000000000000#include "contentselector.hpp" #include #include #include #include #include #include #include #include #include ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : QObject(parent) { ui.setupUi(parent); ui.addonView->setDragDropMode(QAbstractItemView::InternalMove); buildContentModel(); buildGameFileView(); buildAddonView(); } void ContentSelectorView::ContentSelector::buildContentModel() { QIcon warningIcon(ui.addonView->style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(QSize(16, 15))); mContentModel = new ContentSelectorModel::ContentModel(this, warningIcon); } void ContentSelectorView::ContentSelector::buildGameFileView() { ui.gameFileView->setVisible (true); ui.gameFileView->setPlaceholderText(QString("Select a game file...")); connect (ui.gameFileView, SIGNAL (currentIndexChanged(int)), this, SLOT (slotCurrentGameFileIndexChanged(int))); ui.gameFileView->setCurrentIndex(-1); ui.gameFileView->setCurrentIndex(0); } void ContentSelectorView::ContentSelector::buildAddonView() { ui.addonView->setVisible (true); mAddonProxyModel = new QSortFilterProxyModel(this); mAddonProxyModel->setFilterRegExp (QString::number((int)ContentSelectorModel::ContentType_Addon)); mAddonProxyModel->setFilterRole (Qt::UserRole); mAddonProxyModel->setDynamicSortFilter (true); mAddonProxyModel->setSourceModel (mContentModel); ui.addonView->setModel(mAddonProxyModel); connect(ui.addonView, SIGNAL(activated(const QModelIndex&)), this, SLOT(slotAddonTableItemActivated(const QModelIndex&))); connect(mContentModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SIGNAL(signalAddonDataChanged(QModelIndex,QModelIndex))); buildContextMenu(); } void ContentSelectorView::ContentSelector::buildContextMenu() { ui.addonView->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui.addonView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(slotShowContextMenu(const QPoint&))); mContextMenu = new QMenu(ui.addonView); mContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems())); mContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems())); } void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &fileList) { clearCheckStates(); foreach (const QString &filepath, fileList) { const ContentSelectorModel::EsmFile *file = mContentModel->item(filepath); if (file && file->isGameFile()) { setGameFile (filepath); break; } } setContentList(fileList); } void ContentSelectorView::ContentSelector::setGameFile(const QString &filename) { int index = -1; if (!filename.isEmpty()) { const ContentSelectorModel::EsmFile *file = mContentModel->item (filename); index = ui.gameFileView->findText (file->fileName()); //verify that the current index is also checked in the model if (!mContentModel->setCheckState(filename, true)) { //throw error in case file not found? return; } } ui.gameFileView->setCurrentIndex(index); } void ContentSelectorView::ContentSelector::clearCheckStates() { mContentModel->uncheckAll(); } void ContentSelectorView::ContentSelector::setContentList(const QStringList &list) { if (list.isEmpty()) { slotCurrentGameFileIndexChanged (ui.gameFileView->currentIndex()); } else mContentModel->setContentList(list); } ContentSelectorModel::ContentFileList ContentSelectorView::ContentSelector::selectedFiles() const { if (!mContentModel) return ContentSelectorModel::ContentFileList(); return mContentModel->checkedItems(); } void ContentSelectorView::ContentSelector::addFiles(const QString &path) { mContentModel->addFiles(path); // add any game files to the combo box foreach(const QString gameFileName, mContentModel->gameFiles()) { if (ui.gameFileView->findText(gameFileName) == -1) { ui.gameFileView->addItem(gameFileName); } } if (ui.gameFileView->currentIndex() != -1) ui.gameFileView->setCurrentIndex(-1); mContentModel->uncheckAll(); } void ContentSelectorView::ContentSelector::clearFiles() { mContentModel->clearFiles(); } QString ContentSelectorView::ContentSelector::currentFile() const { QModelIndex currentIdx = ui.addonView->currentIndex(); if (!currentIdx.isValid()) return ui.gameFileView->currentText(); QModelIndex idx = mContentModel->index(mAddonProxyModel->mapToSource(currentIdx).row(), 0, QModelIndex()); return mContentModel->data(idx, Qt::DisplayRole).toString(); } void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int index) { static int oldIndex = -1; if (index != oldIndex) { if (oldIndex > -1) { setGameFileSelected(oldIndex, false); } oldIndex = index; setGameFileSelected(index, true); mContentModel->checkForLoadOrderErrors(); } emit signalCurrentGamefileIndexChanged (index); } void ContentSelectorView::ContentSelector::setGameFileSelected(int index, bool selected) { QString fileName = ui.gameFileView->itemText(index); const ContentSelectorModel::EsmFile* file = mContentModel->item(fileName); if (file != NULL) { QModelIndex index(mContentModel->indexFromItem(file)); mContentModel->setData(index, selected, Qt::UserRole + 1); } } void ContentSelectorView::ContentSelector::slotAddonTableItemActivated(const QModelIndex &index) { // toggles check state when an AddOn file is double clicked or activated by keyboard QModelIndex sourceIndex = mAddonProxyModel->mapToSource (index); if (!mContentModel->isEnabled (sourceIndex)) return; Qt::CheckState checkState = Qt::Unchecked; if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() == Qt::Unchecked) checkState = Qt::Checked; mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole); } void ContentSelectorView::ContentSelector::slotShowContextMenu(const QPoint& pos) { QPoint globalPos = ui.addonView->viewport()->mapToGlobal(pos); mContextMenu->exec(globalPos); } void ContentSelectorView::ContentSelector::setCheckStateForMultiSelectedItems(bool checked) { Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked; foreach(const QModelIndex& index, ui.addonView->selectionModel()->selectedIndexes()) { QModelIndex sourceIndex = mAddonProxyModel->mapToSource(index); if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() != checkState) { mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole); } } } void ContentSelectorView::ContentSelector::slotUncheckMultiSelectedItems() { setCheckStateForMultiSelectedItems(false); } void ContentSelectorView::ContentSelector::slotCheckMultiSelectedItems() { setCheckStateForMultiSelectedItems(true); } openmw-openmw-0.38.0/components/contentselector/view/contentselector.hpp000066400000000000000000000036141264522266000267210ustar00rootroot00000000000000#ifndef CONTENTSELECTOR_HPP #define CONTENTSELECTOR_HPP #include #include "ui_contentselector.h" #include class QSortFilterProxyModel; namespace ContentSelectorView { class ContentSelector : public QObject { Q_OBJECT QMenu *mContextMenu; QStringList mFilePaths; protected: ContentSelectorModel::ContentModel *mContentModel; QSortFilterProxyModel *mAddonProxyModel; public: explicit ContentSelector(QWidget *parent = 0); QString currentFile() const; void addFiles(const QString &path); void clearFiles(); void setProfileContent (const QStringList &fileList); void clearCheckStates(); void setContentList(const QStringList &list); ContentSelectorModel::ContentFileList selectedFiles() const; void setGameFile (const QString &filename = QString("")); bool isGamefileSelected() const { return ui.gameFileView->currentIndex() != -1; } QWidget *uiWidget() const { return ui.contentGroupBox; } private: Ui::ContentSelector ui; void buildContentModel(); void buildGameFileView(); void buildAddonView(); void buildContextMenu(); void setGameFileSelected(int index, bool selected); void setCheckStateForMultiSelectedItems(bool checked); signals: void signalCurrentGamefileIndexChanged (int); void signalAddonDataChanged (const QModelIndex& topleft, const QModelIndex& bottomright); private slots: void slotCurrentGameFileIndexChanged(int index); void slotAddonTableItemActivated(const QModelIndex& index); void slotShowContextMenu(const QPoint& pos); void slotCheckMultiSelectedItems(); void slotUncheckMultiSelectedItems(); }; } #endif // CONTENTSELECTOR_HPP openmw-openmw-0.38.0/components/doc.hpp000066400000000000000000000012271264522266000200640ustar00rootroot00000000000000// Note: This is not a regular source file. /// \defgroup components Components /// \namespace ESMS /// \ingroup components /// \brief ESM/ESP record store /// \namespace ESM /// \ingroup components /// \brief ESM/ESP records /// \namespace FileFinder /// \ingroup components /// \brief Linux/Windows-path resolving /// \namespace ToUTF /// \ingroup components /// \brief Text encoding /// \namespace Compiler /// \ingroup components /// \brief script compiler /// \namespace Interpreter /// \ingroup components /// \brief script interpreter // TODO put nif and nifogre in different namespaces (or merge them) // TODO put other components into namespaces openmw-openmw-0.38.0/components/esm/000077500000000000000000000000001264522266000173705ustar00rootroot00000000000000openmw-openmw-0.38.0/components/esm/activespells.cpp000066400000000000000000000034611264522266000225760ustar00rootroot00000000000000#include "activespells.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { void ActiveSpells::save(ESMWriter &esm) const { for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it) { esm.writeHNString ("ID__", it->first); const ActiveSpellParams& params = it->second; esm.writeHNT ("CAST", params.mCasterActorId); esm.writeHNString ("DISP", params.mDisplayName); esm.writeHNT ("TIME", params.mTimeStamp); for (std::vector::const_iterator effectIt = params.mEffects.begin(); effectIt != params.mEffects.end(); ++effectIt) { esm.writeHNT ("MGEF", effectIt->mEffectId); if (effectIt->mArg != -1) esm.writeHNT ("ARG_", effectIt->mArg); esm.writeHNT ("MAGN", effectIt->mMagnitude); esm.writeHNT ("DURA", effectIt->mDuration); } } } void ActiveSpells::load(ESMReader &esm) { while (esm.isNextSub("ID__")) { std::string spellId = esm.getHString(); ActiveSpellParams params; esm.getHNT (params.mCasterActorId, "CAST"); params.mDisplayName = esm.getHNString ("DISP"); esm.getHNT (params.mTimeStamp, "TIME"); while (esm.isNextSub("MGEF")) { ActiveEffect effect; esm.getHT(effect.mEffectId); effect.mArg = -1; esm.getHNOT(effect.mArg, "ARG_"); esm.getHNT (effect.mMagnitude, "MAGN"); esm.getHNT (effect.mDuration, "DURA"); params.mEffects.push_back(effect); } mSpells.insert(std::make_pair(spellId, params)); } } } openmw-openmw-0.38.0/components/esm/activespells.hpp000066400000000000000000000021641264522266000226020ustar00rootroot00000000000000#ifndef OPENMW_ESM_ACTIVESPELLS_H #define OPENMW_ESM_ACTIVESPELLS_H #include "effectlist.hpp" #include "defs.hpp" #include #include namespace ESM { class ESMReader; class ESMWriter; // Parameters of an effect concerning lasting effects. // Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc. // It could also be a negative magnitude, in case of inversing an effect, e.g. Absorb spell causes damage on target, but heals the caster. struct ActiveEffect { int mEffectId; float mMagnitude; int mArg; // skill or attribute float mDuration; }; // format 0, saved games only struct ActiveSpells { struct ActiveSpellParams { std::vector mEffects; ESM::TimeStamp mTimeStamp; std::string mDisplayName; int mCasterActorId; }; typedef std::multimap TContainer; TContainer mSpells; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/aipackage.cpp000066400000000000000000000043621264522266000220060ustar00rootroot00000000000000#include "aipackage.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { void AIData::blank() { mHello = mU1 = mFight = mFlee = mAlarm = mU2 = mU3 = mU4 = 0; mServices = 0; } void AIPackageList::add(ESMReader &esm) { AIPackage pack; if (esm.retSubName() == AI_CNDT) { mList.back().mCellName = esm.getHString(); } else if (esm.retSubName() == AI_Wander) { pack.mType = AI_Wander; esm.getHExact(&pack.mWander, 14); mList.push_back(pack); } else if (esm.retSubName() == AI_Travel) { pack.mType = AI_Travel; esm.getHExact(&pack.mTravel, 16); mList.push_back(pack); } else if (esm.retSubName() == AI_Escort || esm.retSubName() == AI_Follow) { pack.mType = (esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow; esm.getHExact(&pack.mTarget, 48); mList.push_back(pack); } else if (esm.retSubName() == AI_Activate) { pack.mType = AI_Activate; esm.getHExact(&pack.mActivate, 33); mList.push_back(pack); } else { // not AI package related data, so leave return; } } void AIPackageList::save(ESMWriter &esm) const { typedef std::vector::const_iterator PackageIter; for (PackageIter it = mList.begin(); it != mList.end(); ++it) { switch (it->mType) { case AI_Wander: esm.writeHNT("AI_W", it->mWander, sizeof(it->mWander)); break; case AI_Travel: esm.writeHNT("AI_T", it->mTravel, sizeof(it->mTravel)); break; case AI_Activate: esm.writeHNT("AI_A", it->mActivate, sizeof(it->mActivate)); break; case AI_Escort: case AI_Follow: { const char *name = (it->mType == AI_Escort) ? "AI_E" : "AI_F"; esm.writeHNT(name, it->mTarget, sizeof(it->mTarget)); esm.writeHNOCString("CNDT", it->mCellName); break; } default: break; } } } } openmw-openmw-0.38.0/components/esm/aipackage.hpp000066400000000000000000000040331264522266000220060ustar00rootroot00000000000000#ifndef OPENMW_ESM_AIPACKAGE_H #define OPENMW_ESM_AIPACKAGE_H #include #include #include "esmcommon.hpp" namespace ESM { class ESMReader; class ESMWriter; #pragma pack(push) #pragma pack(1) struct AIData { unsigned char mHello; char mU1; unsigned char mFight, mFlee, mAlarm; // These are probabilities [0, 100] char mU2, mU3, mU4; // Unknown values int mServices; // See the Services enum void blank(); ///< Set record to default state (does not touch the ID). }; // 12 bytes struct AIWander { short mDistance; short mDuration; unsigned char mTimeOfDay; unsigned char mIdle[8]; unsigned char mShouldRepeat; }; struct AITravel { float mX, mY, mZ; int mUnk; }; struct AITarget { float mX, mY, mZ; short mDuration; NAME32 mId; short mUnk; }; struct AIActivate { NAME32 mName; unsigned char mUnk; }; #pragma pack(pop) enum { AI_Wander = 0x575f4941, AI_Travel = 0x545f4941, AI_Follow = 0x465f4941, AI_Escort = 0x455f4941, AI_Activate = 0x415f4941, AI_CNDT = 0x54444e43 }; /// \note Used for storaging packages in a single container /// w/o manual memory allocation accordingly to policy standards struct AIPackage { int mType; // Anonymous union union { AIWander mWander; AITravel mTravel; AITarget mTarget; AIActivate mActivate; }; /// \note for AITarget only, placed here to stick with union, /// overhead should be not so awful std::string mCellName; }; struct AIPackageList { std::vector mList; /// Add a single AIPackage, assumes subrecord name was already read void add(ESMReader &esm); void save(ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/aisequence.cpp000066400000000000000000000141361264522266000222230ustar00rootroot00000000000000#include "aisequence.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" #include namespace ESM { namespace AiSequence { void AiWander::load(ESMReader &esm) { esm.getHNT (mData, "DATA"); esm.getHNT(mStartTime, "STAR"); mStoredInitialActorPosition = false; if (esm.isNextSub("POS_")) { mStoredInitialActorPosition = true; esm.getHT(mInitialActorPosition); } } void AiWander::save(ESMWriter &esm) const { esm.writeHNT ("DATA", mData); esm.writeHNT ("STAR", mStartTime); if (mStoredInitialActorPosition) esm.writeHNT ("POS_", mInitialActorPosition); } void AiTravel::load(ESMReader &esm) { esm.getHNT (mData, "DATA"); } void AiTravel::save(ESMWriter &esm) const { esm.writeHNT ("DATA", mData); } void AiEscort::load(ESMReader &esm) { esm.getHNT (mData, "DATA"); mTargetId = esm.getHNString("TARG"); esm.getHNT (mRemainingDuration, "DURA"); mCellId = esm.getHNOString ("CELL"); } void AiEscort::save(ESMWriter &esm) const { esm.writeHNT ("DATA", mData); esm.writeHNString ("TARG", mTargetId); esm.writeHNT ("DURA", mRemainingDuration); if (!mCellId.empty()) esm.writeHNString ("CELL", mCellId); } void AiFollow::load(ESMReader &esm) { esm.getHNT (mData, "DATA"); mTargetId = esm.getHNString("TARG"); esm.getHNT (mRemainingDuration, "DURA"); mCellId = esm.getHNOString ("CELL"); esm.getHNT (mAlwaysFollow, "ALWY"); mCommanded = false; esm.getHNOT (mCommanded, "CMND"); mActive = false; esm.getHNOT (mActive, "ACTV"); } void AiFollow::save(ESMWriter &esm) const { esm.writeHNT ("DATA", mData); esm.writeHNString("TARG", mTargetId); esm.writeHNT ("DURA", mRemainingDuration); if (!mCellId.empty()) esm.writeHNString ("CELL", mCellId); esm.writeHNT ("ALWY", mAlwaysFollow); esm.writeHNT ("CMND", mCommanded); if (mActive) esm.writeHNT("ACTV", mActive); } void AiActivate::load(ESMReader &esm) { mTargetId = esm.getHNString("TARG"); } void AiActivate::save(ESMWriter &esm) const { esm.writeHNString("TARG", mTargetId); } void AiCombat::load(ESMReader &esm) { esm.getHNT (mTargetActorId, "TARG"); } void AiCombat::save(ESMWriter &esm) const { esm.writeHNT ("TARG", mTargetActorId); } void AiPursue::load(ESMReader &esm) { esm.getHNT (mTargetActorId, "TARG"); } void AiPursue::save(ESMWriter &esm) const { esm.writeHNT ("TARG", mTargetActorId); } AiSequence::~AiSequence() { for (std::vector::iterator it = mPackages.begin(); it != mPackages.end(); ++it) delete it->mPackage; } void AiSequence::save(ESMWriter &esm) const { for (std::vector::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) { esm.writeHNT ("AIPK", it->mType); switch (it->mType) { case Ai_Wander: static_cast(it->mPackage)->save(esm); break; case Ai_Travel: static_cast(it->mPackage)->save(esm); break; case Ai_Escort: static_cast(it->mPackage)->save(esm); break; case Ai_Follow: static_cast(it->mPackage)->save(esm); break; case Ai_Activate: static_cast(it->mPackage)->save(esm); break; case Ai_Combat: static_cast(it->mPackage)->save(esm); break; case Ai_Pursue: static_cast(it->mPackage)->save(esm); break; default: break; } } } void AiSequence::load(ESMReader &esm) { while (esm.isNextSub("AIPK")) { int type; esm.getHT(type); mPackages.push_back(AiPackageContainer()); mPackages.back().mType = type; switch (type) { case Ai_Wander: { std::auto_ptr ptr (new AiWander()); ptr->load(esm); mPackages.back().mPackage = ptr.release(); break; } case Ai_Travel: { std::auto_ptr ptr (new AiTravel()); ptr->load(esm); mPackages.back().mPackage = ptr.release(); break; } case Ai_Escort: { std::auto_ptr ptr (new AiEscort()); ptr->load(esm); mPackages.back().mPackage = ptr.release(); break; } case Ai_Follow: { std::auto_ptr ptr (new AiFollow()); ptr->load(esm); mPackages.back().mPackage = ptr.release(); break; } case Ai_Activate: { std::auto_ptr ptr (new AiActivate()); ptr->load(esm); mPackages.back().mPackage = ptr.release(); break; } case Ai_Combat: { std::auto_ptr ptr (new AiCombat()); ptr->load(esm); mPackages.back().mPackage = ptr.release(); break; } case Ai_Pursue: { std::auto_ptr ptr (new AiPursue()); ptr->load(esm); mPackages.back().mPackage = ptr.release(); break; } default: return; } } } } } openmw-openmw-0.38.0/components/esm/aisequence.hpp000066400000000000000000000061651264522266000222330ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_ESM_AISEQUENCE_H #define OPENMW_COMPONENTS_ESM_AISEQUENCE_H #include #include #include "defs.hpp" #include "util.hpp" namespace ESM { class ESMReader; class ESMWriter; namespace AiSequence { // format 0, saved games only // As opposed to AiPackageList, this stores the "live" version of AI packages. enum AiPackages { Ai_Wander = ESM::FourCC<'W','A','N','D'>::value, Ai_Travel = ESM::FourCC<'T','R','A','V'>::value, Ai_Escort = ESM::FourCC<'E','S','C','O'>::value, Ai_Follow = ESM::FourCC<'F','O','L','L'>::value, Ai_Activate = ESM::FourCC<'A','C','T','I'>::value, Ai_Combat = ESM::FourCC<'C','O','M','B'>::value, Ai_Pursue = ESM::FourCC<'P','U','R','S'>::value }; struct AiPackage { virtual ~AiPackage() {} }; #pragma pack(push,1) struct AiWanderData { short mDistance; short mDuration; unsigned char mTimeOfDay; unsigned char mIdle[8]; unsigned char mShouldRepeat; }; struct AiTravelData { float mX, mY, mZ; }; struct AiEscortData { float mX, mY, mZ; short mDuration; }; #pragma pack(pop) struct AiWander : AiPackage { AiWanderData mData; ESM::TimeStamp mStartTime; bool mStoredInitialActorPosition; ESM::Vector3 mInitialActorPosition; /// \todo add more AiWander state void load(ESMReader &esm); void save(ESMWriter &esm) const; }; struct AiTravel : AiPackage { AiTravelData mData; void load(ESMReader &esm); void save(ESMWriter &esm) const; }; struct AiEscort : AiPackage { AiEscortData mData; std::string mTargetId; std::string mCellId; float mRemainingDuration; void load(ESMReader &esm); void save(ESMWriter &esm) const; }; struct AiFollow : AiPackage { AiEscortData mData; std::string mTargetId; std::string mCellId; float mRemainingDuration; bool mAlwaysFollow; bool mCommanded; bool mActive; void load(ESMReader &esm); void save(ESMWriter &esm) const; }; struct AiActivate : AiPackage { std::string mTargetId; void load(ESMReader &esm); void save(ESMWriter &esm) const; }; struct AiCombat : AiPackage { int mTargetActorId; void load(ESMReader &esm); void save(ESMWriter &esm) const; }; struct AiPursue : AiPackage { int mTargetActorId; void load(ESMReader &esm); void save(ESMWriter &esm) const; }; struct AiPackageContainer { int mType; AiPackage* mPackage; }; struct AiSequence { AiSequence() {} ~AiSequence(); std::vector mPackages; void load (ESMReader &esm); void save (ESMWriter &esm) const; private: AiSequence(const AiSequence&); AiSequence& operator=(const AiSequence&); }; } } #endif openmw-openmw-0.38.0/components/esm/attr.cpp000066400000000000000000000025601264522266000210510ustar00rootroot00000000000000#include "attr.hpp" using namespace ESM; const Attribute::AttributeID Attribute::sAttributeIds[Attribute::Length] = { Attribute::Strength, Attribute::Intelligence, Attribute::Willpower, Attribute::Agility, Attribute::Speed, Attribute::Endurance, Attribute::Personality, Attribute::Luck }; const std::string Attribute::sAttributeNames[Attribute::Length] = { "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", "Luck" }; const std::string Attribute::sGmstAttributeIds[Attribute::Length] = { "sAttributeStrength", "sAttributeIntelligence", "sAttributeWillpower", "sAttributeAgility", "sAttributeSpeed", "sAttributeEndurance", "sAttributePersonality", "sAttributeLuck" }; const std::string Attribute::sGmstAttributeDescIds[Attribute::Length] = { "sStrDesc", "sIntDesc", "sWilDesc", "sAgiDesc", "sSpdDesc", "sEndDesc", "sPerDesc", "sLucDesc" }; const std::string Attribute::sAttributeIcons[Attribute::Length] = { "icons\\k\\attribute_strength.dds", "icons\\k\\attribute_int.dds", "icons\\k\\attribute_wilpower.dds", "icons\\k\\attribute_agility.dds", "icons\\k\\attribute_speed.dds", "icons\\k\\attribute_endurance.dds", "icons\\k\\attribute_personality.dds", "icons\\k\\attribute_luck.dds" }; openmw-openmw-0.38.0/components/esm/attr.hpp000066400000000000000000000015731264522266000210610ustar00rootroot00000000000000#ifndef OPENMW_ESM_ATTR_H #define OPENMW_ESM_ATTR_H #include namespace ESM { /* * Attribute definitions */ struct Attribute { enum AttributeID { Strength = 0, Intelligence = 1, Willpower = 2, Agility = 3, Speed = 4, Endurance = 5, Personality = 6, Luck = 7, Length }; AttributeID mId; std::string mName, mDescription; static const AttributeID sAttributeIds[Length]; static const std::string sAttributeNames[Length]; static const std::string sGmstAttributeIds[Length]; static const std::string sGmstAttributeDescIds[Length]; static const std::string sAttributeIcons[Length]; Attribute(AttributeID id, const std::string &name, const std::string &description) : mId(id) , mName(name) , mDescription(description) { } }; } #endif openmw-openmw-0.38.0/components/esm/cellid.cpp000066400000000000000000000024751264522266000213400ustar00rootroot00000000000000#include "cellid.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::CellId::load (ESMReader &esm) { mWorldspace = esm.getHNString ("SPAC"); if (esm.isNextSub ("CIDX")) { esm.getHT (mIndex, 8); mPaged = true; } else mPaged = false; } void ESM::CellId::save (ESMWriter &esm) const { esm.writeHNString ("SPAC", mWorldspace); if (mPaged) esm.writeHNT ("CIDX", mIndex, 8); } bool ESM::operator== (const CellId& left, const CellId& right) { return left.mWorldspace==right.mWorldspace && left.mPaged==right.mPaged && (!left.mPaged || (left.mIndex.mX==right.mIndex.mX && left.mIndex.mY==right.mIndex.mY)); } bool ESM::operator!= (const CellId& left, const CellId& right) { return !(left==right); } bool ESM::operator < (const CellId& left, const CellId& right) { if (left.mPaged < right.mPaged) return true; if (left.mPaged > right.mPaged) return false; if (left.mPaged) { if (left.mIndex.mX < right.mIndex.mX) return true; if (left.mIndex.mX > right.mIndex.mX) return false; if (left.mIndex.mY < right.mIndex.mY) return true; if (left.mIndex.mY > right.mIndex.mY) return false; } return left.mWorldspace < right.mWorldspace; } openmw-openmw-0.38.0/components/esm/cellid.hpp000066400000000000000000000011431264522266000213340ustar00rootroot00000000000000#ifndef OPENMW_ESM_CELLID_H #define OPENMW_ESM_CELLID_H #include namespace ESM { class ESMReader; class ESMWriter; struct CellId { struct CellIndex { int mX; int mY; }; std::string mWorldspace; CellIndex mIndex; bool mPaged; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; bool operator== (const CellId& left, const CellId& right); bool operator!= (const CellId& left, const CellId& right); bool operator< (const CellId& left, const CellId& right); } #endif openmw-openmw-0.38.0/components/esm/cellref.cpp000066400000000000000000000137711264522266000215210ustar00rootroot00000000000000#include "cellref.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::RefNum::load (ESMReader& esm, bool wide, const std::string& tag) { if (wide) esm.getHNT (*this, tag.c_str(), 8); else esm.getHNT (mIndex, tag.c_str()); } void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const { if (wide) esm.writeHNT (tag, *this, 8); else { int refNum = (mIndex & 0xffffff) | ((hasContentFile() ? mContentFile : 0xff)<<24); esm.writeHNT (tag, refNum, 4); } } void ESM::CellRef::load (ESMReader& esm, bool &isDeleted, bool wideRefNum) { loadId(esm, wideRefNum); loadData(esm, isDeleted); } void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum) { // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. // Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway, // because any item can theoretically be moved by a script. if (esm.isNextSub ("NAM0")) esm.skipHSub(); blank(); mRefNum.load (esm, wideRefNum); mRefID = esm.getHNString ("NAME"); } void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool isLoaded = false; while (!isLoaded && esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'U','N','A','M'>::value: esm.getHT(mReferenceBlocked); break; case ESM::FourCC<'X','S','C','L'>::value: esm.getHT(mScale); break; case ESM::FourCC<'A','N','A','M'>::value: mOwner = esm.getHString(); break; case ESM::FourCC<'B','N','A','M'>::value: mGlobalVariable = esm.getHString(); break; case ESM::FourCC<'X','S','O','L'>::value: mSoul = esm.getHString(); break; case ESM::FourCC<'C','N','A','M'>::value: mFaction = esm.getHString(); break; case ESM::FourCC<'I','N','D','X'>::value: esm.getHT(mFactionRank); break; case ESM::FourCC<'X','C','H','G'>::value: esm.getHT(mEnchantmentCharge); break; case ESM::FourCC<'I','N','T','V'>::value: esm.getHT(mChargeInt); break; case ESM::FourCC<'N','A','M','9'>::value: esm.getHT(mGoldValue); break; case ESM::FourCC<'D','O','D','T'>::value: esm.getHT(mDoorDest); mTeleport = true; break; case ESM::FourCC<'D','N','A','M'>::value: mDestCell = esm.getHString(); break; case ESM::FourCC<'F','L','T','V'>::value: esm.getHT(mLockLevel); break; case ESM::FourCC<'K','N','A','M'>::value: mKey = esm.getHString(); break; case ESM::FourCC<'T','N','A','M'>::value: mTrap = esm.getHString(); break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mPos, 24); break; case ESM::FourCC<'N','A','M','0'>::value: esm.skipHSub(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.cacheSubName(); isLoaded = true; break; } } } void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool isDeleted) const { mRefNum.save (esm, wideRefNum); esm.writeHNCString("NAME", mRefID); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } if (mScale != 1.0) { esm.writeHNT("XSCL", mScale); } esm.writeHNOCString("ANAM", mOwner); esm.writeHNOCString("BNAM", mGlobalVariable); esm.writeHNOCString("XSOL", mSoul); esm.writeHNOCString("CNAM", mFaction); if (mFactionRank != -2) { esm.writeHNT("INDX", mFactionRank); } if (mEnchantmentCharge != -1) esm.writeHNT("XCHG", mEnchantmentCharge); if (mChargeInt != -1) esm.writeHNT("INTV", mChargeInt); if (mGoldValue != 1) { esm.writeHNT("NAM9", mGoldValue); } if (!inInventory && mTeleport) { esm.writeHNT("DODT", mDoorDest); esm.writeHNOCString("DNAM", mDestCell); } if (!inInventory && mLockLevel != 0) { esm.writeHNT("FLTV", mLockLevel); } if (!inInventory) esm.writeHNOCString ("KNAM", mKey); if (!inInventory) esm.writeHNOCString ("TNAM", mTrap); if (mReferenceBlocked != -1) esm.writeHNT("UNAM", mReferenceBlocked); if (!inInventory) esm.writeHNT("DATA", mPos, 24); } void ESM::CellRef::blank() { mRefNum.unset(); mRefID.clear(); mScale = 1; mOwner.clear(); mGlobalVariable.clear(); mSoul.clear(); mFaction.clear(); mFactionRank = -2; mChargeInt = -1; mEnchantmentCharge = -1; mGoldValue = 0; mDestCell.clear(); mLockLevel = 0; mKey.clear(); mTrap.clear(); mReferenceBlocked = -1; mTeleport = false; for (int i=0; i<3; ++i) { mDoorDest.pos[i] = 0; mDoorDest.rot[i] = 0; mPos.pos[i] = 0; mPos.rot[i] = 0; } } bool ESM::operator== (const RefNum& left, const RefNum& right) { return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; } bool ESM::operator< (const RefNum& left, const RefNum& right) { if (left.mIndexright.mIndex) return false; return left.mContentFile #include "defs.hpp" namespace ESM { class ESMWriter; class ESMReader; struct RefNum { unsigned int mIndex; int mContentFile; void load (ESMReader& esm, bool wide = false, const std::string& tag = "FRMR"); void save (ESMWriter &esm, bool wide = false, const std::string& tag = "FRMR") const; enum { RefNum_NoContentFile = -1 }; inline bool hasContentFile() const { return mContentFile != RefNum_NoContentFile; } inline void unset() { mIndex = 0; mContentFile = RefNum_NoContentFile; } }; /* Cell reference. This represents ONE object (of many) inside the cell. The cell references are not loaded as part of the normal loading process, but are rather loaded later on demand when we are setting up a specific cell. */ class CellRef { public: // Reference number // Note: Currently unused for items in containers RefNum mRefNum; std::string mRefID; // ID of object being referenced float mScale; // Scale applied to mesh // The NPC that owns this object (and will get angry if you steal it) std::string mOwner; // Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed // even if it has an Owner field. // Used by bed rent scripts to allow the player to use the bed for the duration of the rent. std::string mGlobalVariable; // ID of creature trapped in this soul gem std::string mSoul; // The faction that owns this object (and will get angry if // you take it and are not a faction member) std::string mFaction; // PC faction rank required to use the item. Sometimes is -1, which means "any rank". int mFactionRank; // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. // For lights it is remaining time. // This could be -1 if the charge was not touched yet (i.e. full). union { int mChargeInt; // Used by everything except lights float mChargeFloat; // Used only by lights }; // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full). float mEnchantmentCharge; // This is 5 for Gold_005 references, 100 for Gold_100 and so on. int mGoldValue; // For doors - true if this door teleports to somewhere else, false // if it should open through animation. bool mTeleport; // Teleport location for the door, if this is a teleporting door. Position mDoorDest; // Destination cell for doors (optional) std::string mDestCell; // Lock level for doors and containers int mLockLevel; std::string mKey, mTrap; // Key and trap ID names, if any // This corresponds to the "Reference Blocked" checkbox in the construction set, // which prevents editing that reference. // -1 is not blocked, otherwise it is blocked. signed char mReferenceBlocked; // Position and rotation of this object within the cell Position mPos; /// Calls loadId and loadData void load (ESMReader& esm, bool &isDeleted, bool wideRefNum = false); void loadId (ESMReader& esm, bool wideRefNum = false); /// Implicitly called by load void loadData (ESMReader& esm, bool &isDeleted); void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const; void blank(); }; bool operator== (const RefNum& left, const RefNum& right); bool operator< (const RefNum& left, const RefNum& right); } #endif openmw-openmw-0.38.0/components/esm/cellstate.cpp000066400000000000000000000010461264522266000220550ustar00rootroot00000000000000#include "cellstate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::CellState::load (ESMReader &esm) { mWaterLevel = 0; esm.getHNOT (mWaterLevel, "WLVL"); mHasFogOfWar = false; esm.getHNOT (mHasFogOfWar, "HFOW"); mLastRespawn.mDay = 0; mLastRespawn.mHour = 0; esm.getHNOT (mLastRespawn, "RESP"); } void ESM::CellState::save (ESMWriter &esm) const { if (!mId.mPaged) esm.writeHNT ("WLVL", mWaterLevel); esm.writeHNT ("HFOW", mHasFogOfWar); esm.writeHNT ("RESP", mLastRespawn); } openmw-openmw-0.38.0/components/esm/cellstate.hpp000066400000000000000000000010331264522266000220560ustar00rootroot00000000000000#ifndef OPENMW_ESM_CELLSTATE_H #define OPENMW_ESM_CELLSTATE_H #include "cellid.hpp" #include "defs.hpp" namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only /// \note Does not include references struct CellState { CellId mId; float mWaterLevel; int mHasFogOfWar; // Do we have fog of war state (0 or 1)? (see fogstate.hpp) ESM::TimeStamp mLastRespawn; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/containerstate.cpp000066400000000000000000000004371264522266000231230ustar00rootroot00000000000000#include "containerstate.hpp" void ESM::ContainerState::load (ESMReader &esm) { ObjectState::load (esm); mInventory.load (esm); } void ESM::ContainerState::save (ESMWriter &esm, bool inInventory) const { ObjectState::save (esm, inInventory); mInventory.save (esm); } openmw-openmw-0.38.0/components/esm/containerstate.hpp000066400000000000000000000006301264522266000231230ustar00rootroot00000000000000#ifndef OPENMW_ESM_CONTAINERSTATE_H #define OPENMW_ESM_CONTAINERSTATE_H #include "objectstate.hpp" #include "inventorystate.hpp" namespace ESM { // format 0, saved games only struct ContainerState : public ObjectState { InventoryState mInventory; virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; }; } #endif openmw-openmw-0.38.0/components/esm/creaturelevliststate.cpp000066400000000000000000000011571264522266000243560ustar00rootroot00000000000000#include "creaturelevliststate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { void CreatureLevListState::load(ESMReader &esm) { ObjectState::load(esm); mSpawnActorId = -1; esm.getHNOT (mSpawnActorId, "SPAW"); mSpawn = false; esm.getHNOT (mSpawn, "RESP"); } void CreatureLevListState::save(ESMWriter &esm, bool inInventory) const { ObjectState::save(esm, inInventory); if (mSpawnActorId != -1) esm.writeHNT ("SPAW", mSpawnActorId); if (mSpawn) esm.writeHNT ("RESP", mSpawn); } } openmw-openmw-0.38.0/components/esm/creaturelevliststate.hpp000066400000000000000000000006311264522266000243570ustar00rootroot00000000000000#ifndef OPENMW_ESM_CREATURELEVLISTSTATE_H #define OPENMW_ESM_CREATURELEVLISTSTATE_H #include "objectstate.hpp" namespace ESM { // format 0, saved games only struct CreatureLevListState : public ObjectState { int mSpawnActorId; bool mSpawn; virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; }; } #endif openmw-openmw-0.38.0/components/esm/creaturestate.cpp000066400000000000000000000010221264522266000227420ustar00rootroot00000000000000#include "creaturestate.hpp" void ESM::CreatureState::load (ESMReader &esm) { ObjectState::load (esm); if (mHasCustomState) { mInventory.load (esm); mCreatureStats.load (esm); } } void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const { ObjectState::save (esm, inInventory); if (mHasCustomState) { mInventory.save (esm); mCreatureStats.save (esm); } } void ESM::CreatureState::blank() { ObjectState::blank(); mCreatureStats.blank(); } openmw-openmw-0.38.0/components/esm/creaturestate.hpp000066400000000000000000000010271264522266000227540ustar00rootroot00000000000000#ifndef OPENMW_ESM_CREATURESTATE_H #define OPENMW_ESM_CREATURESTATE_H #include "objectstate.hpp" #include "inventorystate.hpp" #include "creaturestats.hpp" namespace ESM { // format 0, saved games only struct CreatureState : public ObjectState { InventoryState mInventory; CreatureStats mCreatureStats; /// Initialize to default state void blank(); virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; }; } #endif openmw-openmw-0.38.0/components/esm/creaturestats.cpp000066400000000000000000000130501264522266000227640ustar00rootroot00000000000000#include "creaturestats.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::CreatureStats::load (ESMReader &esm) { for (int i=0; i<8; ++i) mAttributes[i].load (esm); for (int i=0; i<3; ++i) mDynamic[i].load (esm); mGoldPool = 0; esm.getHNOT (mGoldPool, "GOLD"); mTradeTime.mDay = 0; mTradeTime.mHour = 0; esm.getHNOT (mTradeTime, "TIME"); mDead = false; esm.getHNOT (mDead, "DEAD"); mDied = false; esm.getHNOT (mDied, "DIED"); mMurdered = false; esm.getHNOT (mMurdered, "MURD"); if (esm.isNextSub("FRHT")) esm.skipHSub(); // Friendly hits, no longer used mTalkedTo = false; esm.getHNOT (mTalkedTo, "TALK"); mAlarmed = false; esm.getHNOT (mAlarmed, "ALRM"); mAttacked = false; esm.getHNOT (mAttacked, "ATKD"); if (esm.isNextSub("HOST")) esm.skipHSub(); // Hostile, no longer used if (esm.isNextSub("ATCK")) esm.skipHSub(); // attackingOrSpell, no longer used mKnockdown = false; esm.getHNOT (mKnockdown, "KNCK"); mKnockdownOneFrame = false; esm.getHNOT (mKnockdownOneFrame, "KNC1"); mKnockdownOverOneFrame = false; esm.getHNOT (mKnockdownOverOneFrame, "KNCO"); mHitRecovery = false; esm.getHNOT (mHitRecovery, "HITR"); mBlock = false; esm.getHNOT (mBlock, "BLCK"); mMovementFlags = 0; esm.getHNOT (mMovementFlags, "MOVE"); if (esm.isNextSub("ASTR")) esm.skipHSub(); // attackStrength, no longer used mFallHeight = 0; esm.getHNOT (mFallHeight, "FALL"); mLastHitObject = esm.getHNOString ("LHIT"); mLastHitAttemptObject = esm.getHNOString ("LHAT"); mRecalcDynamicStats = false; esm.getHNOT (mRecalcDynamicStats, "CALC"); mDrawState = 0; esm.getHNOT (mDrawState, "DRAW"); mLevel = 1; esm.getHNOT (mLevel, "LEVL"); mActorId = -1; esm.getHNOT (mActorId, "ACID"); mDeathAnimation = 0; esm.getHNOT (mDeathAnimation, "DANM"); mSpells.load(esm); mActiveSpells.load(esm); mAiSequence.load(esm); mMagicEffects.load(esm); while (esm.isNextSub("SUMM")) { int magicEffect; esm.getHT(magicEffect); std::string source = esm.getHNOString("SOUR"); int actorId; esm.getHNT (actorId, "ACID"); mSummonedCreatureMap[std::make_pair(magicEffect, source)] = actorId; } while (esm.isNextSub("GRAV")) { int actorId; esm.getHT(actorId); mSummonGraveyard.push_back(actorId); } mHasAiSettings = false; esm.getHNOT(mHasAiSettings, "AISE"); if (mHasAiSettings) { for (int i=0; i<4; ++i) mAiSettings[i].load(esm); } } void ESM::CreatureStats::save (ESMWriter &esm) const { for (int i=0; i<8; ++i) mAttributes[i].save (esm); for (int i=0; i<3; ++i) mDynamic[i].save (esm); if (mGoldPool) esm.writeHNT ("GOLD", mGoldPool); esm.writeHNT ("TIME", mTradeTime); if (mDead) esm.writeHNT ("DEAD", mDead); if (mDied) esm.writeHNT ("DIED", mDied); if (mMurdered) esm.writeHNT ("MURD", mMurdered); if (mTalkedTo) esm.writeHNT ("TALK", mTalkedTo); if (mAlarmed) esm.writeHNT ("ALRM", mAlarmed); if (mAttacked) esm.writeHNT ("ATKD", mAttacked); if (mKnockdown) esm.writeHNT ("KNCK", mKnockdown); if (mKnockdownOneFrame) esm.writeHNT ("KNC1", mKnockdownOneFrame); if (mKnockdownOverOneFrame) esm.writeHNT ("KNCO", mKnockdownOverOneFrame); if (mHitRecovery) esm.writeHNT ("HITR", mHitRecovery); if (mBlock) esm.writeHNT ("BLCK", mBlock); if (mMovementFlags) esm.writeHNT ("MOVE", mMovementFlags); if (mFallHeight) esm.writeHNT ("FALL", mFallHeight); if (!mLastHitObject.empty()) esm.writeHNString ("LHIT", mLastHitObject); if (!mLastHitAttemptObject.empty()) esm.writeHNString ("LHAT", mLastHitAttemptObject); if (mRecalcDynamicStats) esm.writeHNT ("CALC", mRecalcDynamicStats); if (mDrawState) esm.writeHNT ("DRAW", mDrawState); if (mLevel != 1) esm.writeHNT ("LEVL", mLevel); if (mActorId != -1) esm.writeHNT ("ACID", mActorId); if (mDeathAnimation) esm.writeHNT ("DANM", mDeathAnimation); mSpells.save(esm); mActiveSpells.save(esm); mAiSequence.save(esm); mMagicEffects.save(esm); for (std::map, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it) { esm.writeHNT ("SUMM", it->first.first); esm.writeHNString ("SOUR", it->first.second); esm.writeHNT ("ACID", it->second); } for (std::vector::const_iterator it = mSummonGraveyard.begin(); it != mSummonGraveyard.end(); ++it) { esm.writeHNT ("GRAV", *it); } esm.writeHNT("AISE", mHasAiSettings); if (mHasAiSettings) { for (int i=0; i<4; ++i) mAiSettings[i].save(esm); } } void ESM::CreatureStats::blank() { mTradeTime.mHour = 0; mTradeTime.mDay = 0; mGoldPool = 0; mActorId = -1; mHasAiSettings = false; mDead = false; mDied = false; mMurdered = false; mTalkedTo = false; mAlarmed = false; mAttacked = false; mKnockdown = false; mKnockdownOneFrame = false; mKnockdownOverOneFrame = false; mHitRecovery = false; mBlock = false; mMovementFlags = 0; mFallHeight = 0.f; mRecalcDynamicStats = false; mDrawState = 0; mDeathAnimation = 0; mLevel = 1; } openmw-openmw-0.38.0/components/esm/creaturestats.hpp000066400000000000000000000030711264522266000227730ustar00rootroot00000000000000#ifndef OPENMW_ESM_CREATURESTATS_H #define OPENMW_ESM_CREATURESTATS_H #include #include #include #include "statstate.hpp" #include "defs.hpp" #include "spellstate.hpp" #include "activespells.hpp" #include "magiceffects.hpp" #include "aisequence.hpp" namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only struct CreatureStats { StatState mAttributes[8]; StatState mDynamic[3]; MagicEffects mMagicEffects; AiSequence::AiSequence mAiSequence; bool mHasAiSettings; StatState mAiSettings[4]; std::map, int> mSummonedCreatureMap; std::vector mSummonGraveyard; ESM::TimeStamp mTradeTime; int mGoldPool; int mActorId; bool mDead; bool mDied; bool mMurdered; bool mTalkedTo; bool mAlarmed; bool mAttacked; bool mKnockdown; bool mKnockdownOneFrame; bool mKnockdownOverOneFrame; bool mHitRecovery; bool mBlock; unsigned int mMovementFlags; float mFallHeight; std::string mLastHitObject; std::string mLastHitAttemptObject; bool mRecalcDynamicStats; int mDrawState; unsigned char mDeathAnimation; int mLevel; SpellState mSpells; ActiveSpells mActiveSpells; /// Initialize to default state void blank(); void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/custommarkerstate.cpp000066400000000000000000000007541264522266000236570ustar00rootroot00000000000000#include "custommarkerstate.hpp" #include "esmwriter.hpp" #include "esmreader.hpp" namespace ESM { void CustomMarker::save(ESM::ESMWriter &esm) const { esm.writeHNT("POSX", mWorldX); esm.writeHNT("POSY", mWorldY); mCell.save(esm); if (!mNote.empty()) esm.writeHNString("NOTE", mNote); } void CustomMarker::load(ESM::ESMReader &esm) { esm.getHNT(mWorldX, "POSX"); esm.getHNT(mWorldY, "POSY"); mCell.load(esm); mNote = esm.getHNOString("NOTE"); } } openmw-openmw-0.38.0/components/esm/custommarkerstate.hpp000066400000000000000000000010311264522266000236510ustar00rootroot00000000000000#ifndef OPENMW_ESM_CUSTOMMARKERSTATE_H #define OPENMW_ESM_CUSTOMMARKERSTATE_H #include "cellid.hpp" namespace ESM { // format 0, saved games only struct CustomMarker { float mWorldX; float mWorldY; ESM::CellId mCell; std::string mNote; bool operator == (const CustomMarker& other) { return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY; } void load (ESM::ESMReader& reader); void save (ESM::ESMWriter& writer) const; }; } #endif openmw-openmw-0.38.0/components/esm/debugprofile.cpp000066400000000000000000000027041264522266000225460ustar00rootroot00000000000000#include "debugprofile.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" unsigned int ESM::DebugProfile::sRecordId = REC_DBGP; void ESM::DebugProfile::load (ESMReader& esm, bool &isDeleted) { isDeleted = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; case ESM::FourCC<'S','C','R','P'>::value: mScriptText = esm.getHString(); break; case ESM::FourCC<'F','L','A','G'>::value: esm.getHT(mFlags); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } } void ESM::DebugProfile::save (ESMWriter& esm, bool isDeleted) const { esm.writeHNCString ("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString ("DESC", mDescription); esm.writeHNCString ("SCRP", mScriptText); esm.writeHNT ("FLAG", mFlags); } void ESM::DebugProfile::blank() { mDescription.clear(); mScriptText.clear(); mFlags = 0; } openmw-openmw-0.38.0/components/esm/debugprofile.hpp000066400000000000000000000015261264522266000225540ustar00rootroot00000000000000#ifndef COMPONENTS_ESM_DEBUGPROFILE_H #define COMPONENTS_ESM_DEBUGPROFILE_H #include namespace ESM { class ESMReader; class ESMWriter; struct DebugProfile { static unsigned int sRecordId; enum Flags { Flag_Default = 1, // add to newly opened scene subviews Flag_BypassNewGame = 2, // bypass regular game startup Flag_Global = 4 // make available from main menu (i.e. not location specific) }; std::string mId; std::string mDescription; std::string mScriptText; unsigned int mFlags; void load (ESMReader& esm, bool &isDeleted); void save (ESMWriter& esm, bool isDeleted = false) const; /// Set record to default state (does not touch the ID). void blank(); }; } #endif openmw-openmw-0.38.0/components/esm/defs.hpp000066400000000000000000000070021264522266000210210ustar00rootroot00000000000000#ifndef OPENMW_ESM_DEFS_H #define OPENMW_ESM_DEFS_H #include #include namespace ESM { struct TimeStamp { float mHour; int mDay; }; // Pixel color value. Standard four-byte rr,gg,bb,aa format. typedef uint32_t Color; enum Specialization { SPC_Combat = 0, SPC_Magic = 1, SPC_Stealth = 2 }; enum RangeType { RT_Self = 0, RT_Touch = 1, RT_Target = 2 }; #pragma pack(push) #pragma pack(1) // Position and rotation struct Position { float pos[3]; // In radians float rot[3]; osg::Vec3f asVec3() const { return osg::Vec3f(pos[0], pos[1], pos[2]); } }; #pragma pack(pop) template struct FourCC { static const unsigned int value = (((((d << 8) | c) << 8) | b) << 8) | a; }; enum RecNameInts { // format 0 / legacy REC_ACTI = 0x49544341, REC_ALCH = 0x48434c41, REC_APPA = 0x41505041, REC_ARMO = 0x4f4d5241, REC_BODY = 0x59444f42, REC_BOOK = 0x4b4f4f42, REC_BSGN = 0x4e475342, REC_CELL = 0x4c4c4543, REC_CLAS = 0x53414c43, REC_CLOT = 0x544f4c43, REC_CNTC = 0x43544e43, REC_CONT = 0x544e4f43, REC_CREA = 0x41455243, REC_CREC = 0x43455243, REC_DIAL = 0x4c414944, REC_DOOR = 0x524f4f44, REC_ENCH = 0x48434e45, REC_FACT = 0x54434146, REC_GLOB = 0x424f4c47, REC_GMST = 0x54534d47, REC_INFO = 0x4f464e49, REC_INGR = 0x52474e49, REC_LAND = 0x444e414c, REC_LEVC = 0x4356454c, REC_LEVI = 0x4956454c, REC_LIGH = 0x4847494c, REC_LOCK = 0x4b434f4c, REC_LTEX = 0x5845544c, REC_MGEF = 0x4645474d, REC_MISC = 0x4353494d, REC_NPC_ = 0x5f43504e, REC_NPCC = 0x4343504e, REC_PGRD = 0x44524750, REC_PROB = 0x424f5250, REC_RACE = 0x45434152, REC_REGN = 0x4e474552, REC_REPA = 0x41504552, REC_SCPT = 0x54504353, REC_SKIL = 0x4c494b53, REC_SNDG = 0x47444e53, REC_SOUN = 0x4e554f53, REC_SPEL = 0x4c455053, REC_SSCR = 0x52435353, REC_STAT = 0x54415453, REC_WEAP = 0x50414557, // format 0 - saved games REC_SAVE = FourCC<'S','A','V','E'>::value, REC_JOUR_LEGACY = FourCC<0xa4,'U','O','R'>::value, // "\xa4UOR", rather than "JOUR", little oversight when magic numbers were // calculated by hand, needs to be supported for older files now REC_JOUR = FourCC<'J','O','U','R'>::value, REC_QUES = FourCC<'Q','U','E','S'>::value, REC_GSCR = FourCC<'G','S','C','R'>::value, REC_PLAY = FourCC<'P','L','A','Y'>::value, REC_CSTA = FourCC<'C','S','T','A'>::value, REC_GMAP = FourCC<'G','M','A','P'>::value, REC_DIAS = FourCC<'D','I','A','S'>::value, REC_WTHR = FourCC<'W','T','H','R'>::value, REC_KEYS = FourCC<'K','E','Y','S'>::value, REC_DYNA = FourCC<'D','Y','N','A'>::value, REC_ASPL = FourCC<'A','S','P','L'>::value, REC_ACTC = FourCC<'A','C','T','C'>::value, REC_MPRJ = FourCC<'M','P','R','J'>::value, REC_PROJ = FourCC<'P','R','O','J'>::value, REC_DCOU = FourCC<'D','C','O','U'>::value, REC_MARK = FourCC<'M','A','R','K'>::value, REC_ENAB = FourCC<'E','N','A','B'>::value, REC_CAM_ = FourCC<'C','A','M','_'>::value, REC_STLN = FourCC<'S','T','L','N'>::value, // format 1 REC_FILT = FourCC<'F','I','L','T'>::value, REC_DBGP = FourCC<'D','B','G','P'>::value ///< only used in project files }; /// Common subrecords enum SubRecNameInts { SREC_DELE = ESM::FourCC<'D','E','L','E'>::value, SREC_NAME = ESM::FourCC<'N','A','M','E'>::value }; } #endif openmw-openmw-0.38.0/components/esm/dialoguestate.cpp000066400000000000000000000027131264522266000227310ustar00rootroot00000000000000#include "dialoguestate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::DialogueState::load (ESMReader &esm) { while (esm.isNextSub ("TOPI")) mKnownTopics.push_back (esm.getHString()); while (esm.isNextSub ("FACT")) { std::string faction = esm.getHString(); while (esm.isNextSub("REA2")) { std::string faction2 = esm.getHString(); int reaction; esm.getHNT(reaction, "INTV"); mChangedFactionReaction[faction][faction2] = reaction; } // no longer used while (esm.isNextSub ("REAC")) { esm.skipHSub(); esm.getSubName(); esm.skipHSub(); } } } void ESM::DialogueState::save (ESMWriter &esm) const { for (std::vector::const_iterator iter (mKnownTopics.begin()); iter!=mKnownTopics.end(); ++iter) { esm.writeHNString ("TOPI", *iter); } for (std::map >::const_iterator iter = mChangedFactionReaction.begin(); iter != mChangedFactionReaction.end(); ++iter) { esm.writeHNString ("FACT", iter->first); for (std::map::const_iterator reactIter = iter->second.begin(); reactIter != iter->second.end(); ++reactIter) { esm.writeHNString ("REA2", reactIter->first); esm.writeHNT ("INTV", reactIter->second); } } } openmw-openmw-0.38.0/components/esm/dialoguestate.hpp000066400000000000000000000010561264522266000227350ustar00rootroot00000000000000#ifndef OPENMW_ESM_DIALOGUESTATE_H #define OPENMW_ESM_DIALOGUESTATE_H #include #include #include namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only struct DialogueState { // must be lower case topic IDs std::vector mKnownTopics; // must be lower case faction IDs std::map > mChangedFactionReaction; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/doorstate.cpp000066400000000000000000000007011264522266000220760ustar00rootroot00000000000000#include "doorstate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { void DoorState::load(ESMReader &esm) { ObjectState::load(esm); mDoorState = 0; esm.getHNOT (mDoorState, "ANIM"); } void DoorState::save(ESMWriter &esm, bool inInventory) const { ObjectState::save(esm, inInventory); if (mDoorState != 0) esm.writeHNT ("ANIM", mDoorState); } } openmw-openmw-0.38.0/components/esm/doorstate.hpp000066400000000000000000000005401264522266000221040ustar00rootroot00000000000000#ifndef OPENMW_ESM_DOORSTATE_H #define OPENMW_ESM_DOORSTATE_H #include "objectstate.hpp" namespace ESM { // format 0, saved games only struct DoorState : public ObjectState { int mDoorState; virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; }; } #endif openmw-openmw-0.38.0/components/esm/effectlist.cpp000066400000000000000000000010401264522266000222170ustar00rootroot00000000000000#include "effectlist.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { void EffectList::load(ESMReader &esm) { mList.clear(); while (esm.isNextSub("ENAM")) { add(esm); } } void EffectList::add(ESMReader &esm) { ENAMstruct s; esm.getHT(s, 24); mList.push_back(s); } void EffectList::save(ESMWriter &esm) const { for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("ENAM", *it, 24); } } } // end namespace openmw-openmw-0.38.0/components/esm/effectlist.hpp000066400000000000000000000020561264522266000222340ustar00rootroot00000000000000#ifndef OPENMW_ESM_EFFECTLIST_H #define OPENMW_ESM_EFFECTLIST_H #include namespace ESM { class ESMReader; class ESMWriter; #pragma pack(push) #pragma pack(1) /** Defines a spell effect. Shared between SPEL (Spells), ALCH (Potions) and ENCH (Item enchantments) records */ struct ENAMstruct { // Magical effect, hard-coded ID short mEffectID; // Which skills/attributes are affected (for restore/drain spells // etc.) signed char mSkill, mAttribute; // -1 if N/A // Other spell parameters int mRange; // 0 - self, 1 - touch, 2 - target (RangeType enum) int mArea, mDuration, mMagnMin, mMagnMax; }; #pragma pack(pop) /// EffectList, ENAM subrecord struct EffectList { std::vector mList; /// Load one effect, assumes subrecord name was already read void add(ESMReader &esm); /// Load all effects void load(ESMReader &esm); void save(ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/esmcommon.hpp000066400000000000000000000041571264522266000221050ustar00rootroot00000000000000#ifndef OPENMW_ESM_COMMON_H #define OPENMW_ESM_COMMON_H #include #include #include #include namespace ESM { enum Version { VER_12 = 0x3f99999a, VER_13 = 0x3fa66666 }; /* A structure used for holding fixed-length strings. In the case of LEN=4, it can be more efficient to match the string as a 32 bit number, therefore the struct is implemented as a union with an int. */ template union NAME_T { char name[LEN]; uint32_t val; bool operator==(const char *str) const { for(int i=0; i NAME; typedef NAME_T<32> NAME32; typedef NAME_T<64> NAME64; typedef NAME_T<256> NAME256; /* This struct defines a file 'context' which can be saved and later restored by an ESMReader instance. It will save the position within a file, and when restored will let you read from that position as if you never left it. */ struct ESM_Context { std::string filename; uint32_t leftRec, leftSub; size_t leftFile; NAME recName, subName; // When working with multiple esX files, we will generate lists of all files that // actually contribute to a specific cell. Therefore, we need to store the index // of the file belonging to this contest. See CellStore::(list/load)refs for details. int index; // True if subName has been read but not used. bool subCached; // File position. Only used for stored contexts, not regularly // updated within the reader itself. size_t filePos; }; } #endif openmw-openmw-0.38.0/components/esm/esmreader.cpp000066400000000000000000000176371264522266000220610ustar00rootroot00000000000000#include "esmreader.hpp" #include namespace ESM { using namespace Misc; std::string ESMReader::getName() const { return mCtx.filename; } ESM_Context ESMReader::getContext() { // Update the file position before returning mCtx.filePos = mEsm->tellg(); return mCtx; } ESMReader::ESMReader() : mIdx(0) , mRecordFlags(0) , mBuffer(50*1024) , mGlobalReaderList(NULL) , mEncoder(NULL) , mFileSize(0) { } int ESMReader::getFormat() const { return mHeader.mFormat; } void ESMReader::restoreContext(const ESM_Context &rc) { // Reopen the file if necessary if (mCtx.filename != rc.filename) openRaw(rc.filename); // Copy the data mCtx = rc; // Make sure we seek to the right place mEsm->seekg(mCtx.filePos); } void ESMReader::close() { mEsm.reset(); mCtx.filename.clear(); mCtx.leftFile = 0; mCtx.leftRec = 0; mCtx.leftSub = 0; mCtx.subCached = false; mCtx.recName.val = 0; mCtx.subName.val = 0; } void ESMReader::openRaw(Files::IStreamPtr _esm, const std::string& name) { close(); mEsm = _esm; mCtx.filename = name; mEsm->seekg(0, mEsm->end); mCtx.leftFile = mFileSize = mEsm->tellg(); mEsm->seekg(0, mEsm->beg); } void ESMReader::openRaw(const std::string& filename) { openRaw(Files::openConstrainedFileStream(filename.c_str()), filename); } void ESMReader::open(Files::IStreamPtr _esm, const std::string &name) { openRaw(_esm, name); if (getRecName() != "TES3") fail("Not a valid Morrowind file"); getRecHeader(); mHeader.load (*this); } void ESMReader::open(const std::string &file) { open (Files::openConstrainedFileStream (file.c_str ()), file); } int64_t ESMReader::getHNLong(const char *name) { int64_t val; getHNT(val, name); return val; } std::string ESMReader::getHNOString(const char* name) { if (isNextSub(name)) return getHString(); return ""; } std::string ESMReader::getHNString(const char* name) { getSubNameIs(name); return getHString(); } std::string ESMReader::getHString() { getSubHeader(); // Hack to make MultiMark.esp load. Zero-length strings do not // occur in any of the official mods, but MultiMark makes use of // them. For some reason, they break the rules, and contain a byte // (value 0) even if the header says there is no data. If // Morrowind accepts it, so should we. if (mCtx.leftSub == 0) { // Skip the following zero byte mCtx.leftRec--; char c; getExact(&c, 1); return ""; } return getString(mCtx.leftSub); } void ESMReader::getHExact(void*p, int size) { getSubHeader(); if (size != static_cast (mCtx.leftSub)) { std::stringstream error; error << "getHExact(): size mismatch (requested " << size << ", got " << mCtx.leftSub << ")"; fail(error.str()); } getExact(p, size); } // Read the given number of bytes from a named subrecord void ESMReader::getHNExact(void*p, int size, const char* name) { getSubNameIs(name); getHExact(p, size); } // Get the next subrecord name and check if it matches the parameter void ESMReader::getSubNameIs(const char* name) { getSubName(); if (mCtx.subName != name) fail( "Expected subrecord " + std::string(name) + " but got " + mCtx.subName.toString()); } bool ESMReader::isNextSub(const char* name) { if (!mCtx.leftRec) return false; getSubName(); // If the name didn't match, then mark the it as 'cached' so it's // available for the next call to getSubName. mCtx.subCached = (mCtx.subName != name); // If subCached is false, then subName == name. return !mCtx.subCached; } bool ESMReader::peekNextSub(const char *name) { if (!mCtx.leftRec) return false; getSubName(); mCtx.subCached = true; return mCtx.subName == name; } void ESMReader::cacheSubName() { mCtx.subCached = true; } // Read subrecord name. This gets called a LOT, so I've optimized it // slightly. void ESMReader::getSubName() { // If the name has already been read, do nothing if (mCtx.subCached) { mCtx.subCached = false; return; } // reading the subrecord data anyway. getExact(mCtx.subName.name, 4); mCtx.leftRec -= 4; } bool ESMReader::isEmptyOrGetName() { if (mCtx.leftRec) { getExact(mCtx.subName.name, 4); mCtx.leftRec -= 4; return false; } return true; } void ESMReader::skipHSub() { getSubHeader(); skip(mCtx.leftSub); } void ESMReader::skipHSubSize(int size) { skipHSub(); if (static_cast (mCtx.leftSub) != size) fail("skipHSubSize() mismatch"); } void ESMReader::skipHSubUntil(const char *name) { while (hasMoreSubs() && !isNextSub(name)) { mCtx.subCached = false; skipHSub(); } if (hasMoreSubs()) mCtx.subCached = true; } void ESMReader::getSubHeader() { if (mCtx.leftRec < 4) fail("End of record while reading sub-record header"); // Get subrecord size getT(mCtx.leftSub); // Adjust number of record bytes left mCtx.leftRec -= mCtx.leftSub + 4; } void ESMReader::getSubHeaderIs(int size) { getSubHeader(); if (size != static_cast (mCtx.leftSub)) fail("getSubHeaderIs(): Sub header mismatch"); } NAME ESMReader::getRecName() { if (!hasMoreRecs()) fail("No more records, getRecName() failed"); getName(mCtx.recName); mCtx.leftFile -= 4; // Make sure we don't carry over any old cached subrecord // names. This can happen in some cases when we skip parts of a // record. mCtx.subCached = false; return mCtx.recName; } void ESMReader::skipRecord() { skip(mCtx.leftRec); mCtx.leftRec = 0; mCtx.subCached = false; } void ESMReader::getRecHeader(uint32_t &flags) { // General error checking if (mCtx.leftFile < 12) fail("End of file while reading record header"); if (mCtx.leftRec) fail("Previous record contains unread bytes"); getUint(mCtx.leftRec); getUint(flags);// This header entry is always zero getUint(flags); mCtx.leftFile -= 12; // Check that sizes add up if (mCtx.leftFile < mCtx.leftRec) fail("Record size is larger than rest of file"); // Adjust number of bytes mCtx.left in file mCtx.leftFile -= mCtx.leftRec; } /************************************************************************* * * Lowest level data reading and misc methods * *************************************************************************/ void ESMReader::getExact(void*x, int size) { try { mEsm->read((char*)x, size); } catch (std::exception& e) { fail(std::string("Read error: ") + e.what()); } } std::string ESMReader::getString(int size) { size_t s = size; if (mBuffer.size() <= s) // Add some extra padding to reduce the chance of having to resize // again later. mBuffer.resize(3*s); // And make sure the string is zero terminated mBuffer[s] = 0; // read ESM data char *ptr = &mBuffer[0]; getExact(ptr, size); size = strnlen(ptr, size); // Convert to UTF8 and return if (mEncoder) return mEncoder->getUtf8(ptr, size); return std::string (ptr, size); } void ESMReader::fail(const std::string &msg) { using namespace std; stringstream ss; ss << "ESM Error: " << msg; ss << "\n File: " << mCtx.filename; ss << "\n Record: " << mCtx.recName.toString(); ss << "\n Subrecord: " << mCtx.subName.toString(); if (mEsm.get()) ss << "\n Offset: 0x" << hex << mEsm->tellg(); throw std::runtime_error(ss.str()); } void ESMReader::setEncoder(ToUTF8::Utf8Encoder* encoder) { mEncoder = encoder; } size_t ESMReader::getFileOffset() { return mEsm->tellg(); } void ESMReader::skip(int bytes) { mEsm->seekg(getFileOffset()+bytes); } } openmw-openmw-0.38.0/components/esm/esmreader.hpp000066400000000000000000000204631264522266000220550ustar00rootroot00000000000000#ifndef OPENMW_ESM_READER_H #define OPENMW_ESM_READER_H #include #include #include #include #include #include #include #include #include "esmcommon.hpp" #include "loadtes3.hpp" namespace ESM { class ESMReader { public: ESMReader(); /************************************************************************* * * Information retrieval * *************************************************************************/ int getVer() const { return mHeader.mData.version; } int getRecordCount() const { return mHeader.mData.records; } float getFVer() const { return (mHeader.mData.version == VER_12) ? 1.2f : 1.3f; } const std::string getAuthor() const { return mHeader.mData.author.toString(); } const std::string getDesc() const { return mHeader.mData.desc.toString(); } const std::vector &getGameFiles() const { return mHeader.mMaster; } const Header& getHeader() const { return mHeader; } int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } std::string getName() const; /************************************************************************* * * Opening and closing * *************************************************************************/ /** Save the current file position and information in a ESM_Context struct */ ESM_Context getContext(); /** Restore a previously saved context */ void restoreContext(const ESM_Context &rc); /** Close the file, resets all information. After calling close() the structure may be reused to load a new file. */ void close(); /// Raw opening. Opens the file and sets everything up but doesn't /// parse the header. void openRaw(Files::IStreamPtr _esm, const std::string &name); /// Load ES file from a new stream, parses the header. Closes the /// currently open file first, if any. void open(Files::IStreamPtr _esm, const std::string &name); void open(const std::string &file); void openRaw(const std::string &filename); /// Get the current position in the file. Make sure that the file has been opened! size_t getFileOffset(); // This is a quick hack for multiple esm/esp files. Each plugin introduces its own // terrain palette, but ESMReader does not pass a reference to the correct plugin // to the individual load() methods. This hack allows to pass this reference // indirectly to the load() method. int mIdx; void setIndex(const int index) {mIdx = index; mCtx.index = index;} int getIndex() {return mIdx;} void setGlobalReaderList(std::vector *list) {mGlobalReaderList = list;} std::vector *getGlobalReaderList() {return mGlobalReaderList;} /************************************************************************* * * Medium-level reading shortcuts * *************************************************************************/ // Read data of a given type, stored in a subrecord of a given name template void getHNT(X &x, const char* name) { getSubNameIs(name); getHT(x); } // Optional version of getHNT template void getHNOT(X &x, const char* name) { if(isNextSub(name)) getHT(x); } // Version with extra size checking, to make sure the compiler // doesn't mess up our struct padding. template void getHNT(X &x, const char* name, int size) { assert(sizeof(X) == size); getSubNameIs(name); getHT(x); } template void getHNOT(X &x, const char* name, int size) { assert(sizeof(X) == size); if(isNextSub(name)) getHT(x); } int64_t getHNLong(const char *name); // Get data of a given type/size, including subrecord header template void getHT(X &x) { getSubHeader(); if (mCtx.leftSub != sizeof(X)) { std::stringstream error; error << "getHT(): subrecord size mismatch (requested " << sizeof(X) << ", got " << mCtx.leftSub << ")"; fail(error.str()); } getT(x); } // Version with extra size checking, to make sure the compiler // doesn't mess up our struct padding. template void getHT(X &x, int size) { assert(sizeof(X) == size); getHT(x); } // Read a string by the given name if it is the next record. std::string getHNOString(const char* name); // Read a string with the given sub-record name std::string getHNString(const char* name); // Read a string, including the sub-record header (but not the name) std::string getHString(); // Read the given number of bytes from a subrecord void getHExact(void*p, int size); // Read the given number of bytes from a named subrecord void getHNExact(void*p, int size, const char* name); /************************************************************************* * * Low level sub-record methods * *************************************************************************/ // Get the next subrecord name and check if it matches the parameter void getSubNameIs(const char* name); /** Checks if the next sub record name matches the parameter. If it does, it is read into 'subName' just as if getSubName() was called. If not, the read name will still be available for future calls to getSubName(), isNextSub() and getSubNameIs(). */ bool isNextSub(const char* name); bool peekNextSub(const char* name); // Store the current subrecord name for the next call of getSubName() void cacheSubName(); // Read subrecord name. This gets called a LOT, so I've optimized it // slightly. void getSubName(); // This is specially optimized for LoadINFO. bool isEmptyOrGetName(); // Skip current sub record, including header (but not including // name.) void skipHSub(); // Skip sub record and check its size void skipHSubSize(int size); // Skip all subrecords until the given subrecord or no more subrecords remaining void skipHSubUntil(const char* name); /* Sub-record header. This updates leftRec beyond the current sub-record as well. leftSub contains size of current sub-record. */ void getSubHeader(); /** Get sub header and check the size */ void getSubHeaderIs(int size); /************************************************************************* * * Low level record methods * *************************************************************************/ // Get the next record name NAME getRecName(); // Skip the rest of this record. Assumes the name and header have // already been read void skipRecord(); /* Read record header. This updatesleftFile BEYOND the data that follows the header, ie beyond the entire record. You should use leftRec to orient yourself inside the record itself. */ void getRecHeader() { getRecHeader(mRecordFlags); } void getRecHeader(uint32_t &flags); bool hasMoreRecs() const { return mCtx.leftFile > 0; } bool hasMoreSubs() const { return mCtx.leftRec > 0; } /************************************************************************* * * Lowest level data reading and misc methods * *************************************************************************/ template void getT(X &x) { getExact(&x, sizeof(X)); } void getExact(void*x, int size); void getName(NAME &name) { getT(name); } void getUint(uint32_t &u) { getT(u); } // Read the next 'size' bytes and return them as a string. Converts // them from native encoding to UTF8 in the process. std::string getString(int size); void skip(int bytes); /// Used for error handling void fail(const std::string &msg); /// Sets font encoder for ESM strings void setEncoder(ToUTF8::Utf8Encoder* encoder); /// Get record flags of last record unsigned int getRecordFlags() { return mRecordFlags; } size_t getFileSize() const { return mFileSize; } private: Files::IStreamPtr mEsm; ESM_Context mCtx; unsigned int mRecordFlags; // Special file signifier (see SpecialFile enum above) // Buffer for ESM strings std::vector mBuffer; Header mHeader; std::vector *mGlobalReaderList; ToUTF8::Utf8Encoder* mEncoder; size_t mFileSize; }; } #endif openmw-openmw-0.38.0/components/esm/esmwriter.cpp000066400000000000000000000124611264522266000221210ustar00rootroot00000000000000#include "esmwriter.hpp" #include #include #include #include namespace ESM { ESMWriter::ESMWriter() : mStream(NULL) , mEncoder (0) , mRecordCount (0) , mCounting (true) {} unsigned int ESMWriter::getVersion() const { return mHeader.mData.version; } void ESMWriter::setVersion(unsigned int ver) { mHeader.mData.version = ver; } void ESMWriter::setType(int type) { mHeader.mData.type = type; } void ESMWriter::setAuthor(const std::string& auth) { mHeader.mData.author.assign (auth); } void ESMWriter::setDescription(const std::string& desc) { mHeader.mData.desc.assign (desc); } void ESMWriter::setRecordCount (int count) { mHeader.mData.records = count; } void ESMWriter::setFormat (int format) { mHeader.mFormat = format; } void ESMWriter::clearMaster() { mHeader.mMaster.clear(); } void ESMWriter::addMaster(const std::string& name, uint64_t size) { Header::MasterData d; d.name = name; d.size = size; mHeader.mMaster.push_back(d); } void ESMWriter::save(std::ostream& file) { mRecordCount = 0; mRecords.clear(); mCounting = true; mStream = &file; startRecord("TES3", 0); mHeader.save (*this); endRecord("TES3"); } void ESMWriter::close() { if (!mRecords.empty()) throw std::runtime_error ("Unclosed record remaining"); } void ESMWriter::startRecord(const std::string& name, uint32_t flags) { mRecordCount++; writeName(name); RecordData rec; rec.name = name; rec.position = mStream->tellp(); rec.size = 0; writeT(0); // Size goes here writeT(0); // Unused header? writeT(flags); mRecords.push_back(rec); assert(mRecords.back().size == 0); } void ESMWriter::startRecord (uint32_t name, uint32_t flags) { std::string type; for (int i=0; i<4; ++i) /// \todo make endianess agnostic type += reinterpret_cast (&name)[i]; startRecord (type, flags); } void ESMWriter::startSubRecord(const std::string& name) { // Sub-record hierarchies are not properly supported in ESMReader. This should be fixed later. assert (mRecords.size() <= 1); writeName(name); RecordData rec; rec.name = name; rec.position = mStream->tellp(); rec.size = 0; writeT(0); // Size goes here mRecords.push_back(rec); assert(mRecords.back().size == 0); } void ESMWriter::endRecord(const std::string& name) { RecordData rec = mRecords.back(); assert(rec.name == name); mRecords.pop_back(); mStream->seekp(rec.position); mCounting = false; write (reinterpret_cast (&rec.size), sizeof(uint32_t)); mCounting = true; mStream->seekp(0, std::ios::end); } void ESMWriter::endRecord (uint32_t name) { std::string type; for (int i=0; i<4; ++i) /// \todo make endianess agnostic type += reinterpret_cast (&name)[i]; endRecord (type); } void ESMWriter::writeHNString(const std::string& name, const std::string& data) { startSubRecord(name); writeHString(data); endRecord(name); } void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) { assert(data.size() <= size); startSubRecord(name); writeHString(data); if (data.size() < size) { for (size_t i = data.size(); i < size; ++i) write("\0",1); } endRecord(name); } void ESMWriter::writeFixedSizeString(const std::string &data, int size) { std::string string; if (!data.empty()) string = mEncoder ? mEncoder->getLegacyEnc(data) : data; string.resize(size); write(string.c_str(), string.size()); } void ESMWriter::writeHString(const std::string& data) { if (data.size() == 0) write("\0", 1); else { // Convert to UTF8 and return std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; write(string.c_str(), string.size()); } } void ESMWriter::writeHCString(const std::string& data) { writeHString(data); if (data.size() > 0 && data[data.size()-1] != '\0') write("\0", 1); } void ESMWriter::writeName(const std::string& name) { assert((name.size() == 4 && name[3] != '\0')); write(name.c_str(), name.size()); } void ESMWriter::write(const char* data, size_t size) { if (mCounting && !mRecords.empty()) { for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) it->size += size; } mStream->write(data, size); } void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) { mEncoder = encoder; } } openmw-openmw-0.38.0/components/esm/esmwriter.hpp000066400000000000000000000103061264522266000221220ustar00rootroot00000000000000#ifndef OPENMW_ESM_WRITER_H #define OPENMW_ESM_WRITER_H #include #include #include "esmcommon.hpp" #include "loadtes3.hpp" namespace ToUTF8 { class Utf8Encoder; } namespace ESM { class ESMWriter { struct RecordData { std::string name; std::streampos position; uint32_t size; }; public: ESMWriter(); unsigned int getVersion() const; // Set various header data (ESM::Header::Data). All of the below functions must be called before writing, // otherwise this data will be left uninitialized. void setVersion(unsigned int ver = 0x3fa66666); void setType(int type); void setEncoder(ToUTF8::Utf8Encoder *encoding); void setAuthor(const std::string& author); void setDescription(const std::string& desc); // Set the record count for writing it in the file header void setRecordCount (int count); // Counts how many records we have actually written. // It is a good idea to compare this with the value you wrote into the header (setRecordCount) // It should be the record count you set + 1 (1 additional record for the TES3 header) int getRecordCount() { return mRecordCount; } void setFormat (int format); void clearMaster(); void addMaster(const std::string& name, uint64_t size); void save(std::ostream& file); ///< Start saving a file by writing the TES3 header. void close(); ///< \note Does not close the stream. void writeHNString(const std::string& name, const std::string& data); void writeHNString(const std::string& name, const std::string& data, size_t size); void writeHNCString(const std::string& name, const std::string& data) { startSubRecord(name); writeHCString(data); endRecord(name); } void writeHNOString(const std::string& name, const std::string& data) { if (!data.empty()) writeHNString(name, data); } void writeHNOCString(const std::string& name, const std::string& data) { if (!data.empty()) writeHNCString(name, data); } template void writeHNT(const std::string& name, const T& data) { startSubRecord(name); writeT(data); endRecord(name); } private: // Prevent using writeHNT with strings. This already happened by accident and results in // state being discarded without any error on writing or reading it. :( // writeHNString and friends must be used instead. void writeHNT(const std::string &name, std::string data) { } void writeT(const std::string& data) { } public: template void writeHNT(const std::string& name, const T& data, int size) { startSubRecord(name); writeT(data, size); endRecord(name); } template void writeT(const T& data) { write((char*)&data, sizeof(T)); } template void writeT(const T& data, size_t size) { write((char*)&data, size); } void startRecord(const std::string& name, uint32_t flags = 0); void startRecord(uint32_t name, uint32_t flags = 0); /// @note Sub-record hierarchies are not properly supported in ESMReader. This should be fixed later. void startSubRecord(const std::string& name); void endRecord(const std::string& name); void endRecord(uint32_t name); void writeFixedSizeString(const std::string& data, int size); void writeHString(const std::string& data); void writeHCString(const std::string& data); void writeName(const std::string& data); void write(const char* data, size_t size); private: std::list mRecords; std::ostream* mStream; std::streampos mHeaderPos; ToUTF8::Utf8Encoder* mEncoder; int mRecordCount; bool mCounting; Header mHeader; }; } #endif openmw-openmw-0.38.0/components/esm/filter.cpp000066400000000000000000000024251264522266000213640ustar00rootroot00000000000000#include "filter.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" unsigned int ESM::Filter::sRecordId = REC_FILT; void ESM::Filter::load (ESMReader& esm, bool &isDeleted) { isDeleted = false; while (esm.hasMoreSubs()) { esm.getSubName(); uint32_t name = esm.retSubName().val; switch (name) { case ESM::SREC_NAME: mId = esm.getHString(); break; case ESM::FourCC<'F','I','L','T'>::value: mFilter = esm.getHString(); break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } } void ESM::Filter::save (ESMWriter& esm, bool isDeleted) const { esm.writeHNCString ("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString ("FILT", mFilter); esm.writeHNCString ("DESC", mDescription); } void ESM::Filter::blank() { mFilter.clear(); mDescription.clear(); } openmw-openmw-0.38.0/components/esm/filter.hpp000066400000000000000000000010131264522266000213610ustar00rootroot00000000000000#ifndef COMPONENTS_ESM_FILTER_H #define COMPONENTS_ESM_FILTER_H #include namespace ESM { class ESMReader; class ESMWriter; struct Filter { static unsigned int sRecordId; std::string mId; std::string mDescription; std::string mFilter; void load (ESMReader& esm, bool &isDeleted); void save (ESMWriter& esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/fogstate.cpp000066400000000000000000000020221264522266000217040ustar00rootroot00000000000000#include "fogstate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::FogState::load (ESMReader &esm) { esm.getHNOT(mBounds, "BOUN"); esm.getHNOT(mNorthMarkerAngle, "ANGL"); while (esm.isNextSub("FTEX")) { esm.getSubHeader(); FogTexture tex; esm.getT(tex.mX); esm.getT(tex.mY); size_t imageSize = esm.getSubSize()-sizeof(int)*2; tex.mImageData.resize(imageSize); esm.getExact(&tex.mImageData[0], imageSize); mFogTextures.push_back(tex); } } void ESM::FogState::save (ESMWriter &esm, bool interiorCell) const { if (interiorCell) { esm.writeHNT("BOUN", mBounds); esm.writeHNT("ANGL", mNorthMarkerAngle); } for (std::vector::const_iterator it = mFogTextures.begin(); it != mFogTextures.end(); ++it) { esm.startSubRecord("FTEX"); esm.writeT(it->mX); esm.writeT(it->mY); esm.write(&it->mImageData[0], it->mImageData.size()); esm.endRecord("FTEX"); } } openmw-openmw-0.38.0/components/esm/fogstate.hpp000066400000000000000000000013371264522266000217210ustar00rootroot00000000000000#ifndef OPENMW_ESM_FOGSTATE_H #define OPENMW_ESM_FOGSTATE_H #include namespace ESM { class ESMReader; class ESMWriter; struct FogTexture { int mX, mY; // Only used for interior cells std::vector mImageData; }; // format 0, saved games only // Fog of war state struct FogState { // Only used for interior cells float mNorthMarkerAngle; struct Bounds { float mMinX; float mMinY; float mMaxX; float mMaxY; } mBounds; std::vector mFogTextures; void load (ESMReader &esm); void save (ESMWriter &esm, bool interiorCell) const; }; } #endif openmw-openmw-0.38.0/components/esm/globalmap.cpp000066400000000000000000000017561264522266000220430ustar00rootroot00000000000000#include "globalmap.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" unsigned int ESM::GlobalMap::sRecordId = ESM::REC_GMAP; void ESM::GlobalMap::load (ESMReader &esm) { esm.getHNT(mBounds, "BNDS"); esm.getSubNameIs("DATA"); esm.getSubHeader(); mImageData.resize(esm.getSubSize()); esm.getExact(&mImageData[0], mImageData.size()); while (esm.isNextSub("MRK_")) { esm.getSubHeader(); CellId cell; esm.getT(cell.first); esm.getT(cell.second); mMarkers.insert(cell); } } void ESM::GlobalMap::save (ESMWriter &esm) const { esm.writeHNT("BNDS", mBounds); esm.startSubRecord("DATA"); esm.write(&mImageData[0], mImageData.size()); esm.endRecord("DATA"); for (std::set::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) { esm.startSubRecord("MRK_"); esm.writeT(it->first); esm.writeT(it->second); esm.endRecord("MRK_"); } } openmw-openmw-0.38.0/components/esm/globalmap.hpp000066400000000000000000000013361264522266000220420ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_ESM_GLOBALMAP_H #define OPENMW_COMPONENTS_ESM_GLOBALMAP_H #include #include namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only ///< \brief An image containing the explored areas on the global map. struct GlobalMap { static unsigned int sRecordId; // The minimum and maximum cell coordinates struct Bounds { int mMinX, mMaxX, mMinY, mMaxY; }; Bounds mBounds; std::vector mImageData; typedef std::pair CellId; std::set mMarkers; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/globalscript.cpp000066400000000000000000000010021264522266000225520ustar00rootroot00000000000000#include "globalscript.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::GlobalScript::load (ESMReader &esm) { mId = esm.getHNString ("NAME"); mLocals.load (esm); mRunning = 0; esm.getHNOT (mRunning, "RUN_"); mTargetId = esm.getHNOString ("TARG"); } void ESM::GlobalScript::save (ESMWriter &esm) const { esm.writeHNString ("NAME", mId); mLocals.save (esm); if (mRunning) esm.writeHNT ("RUN_", mRunning); esm.writeHNOString ("TARG", mTargetId); } openmw-openmw-0.38.0/components/esm/globalscript.hpp000066400000000000000000000010041264522266000225610ustar00rootroot00000000000000#ifndef OPENMW_ESM_GLOBALSCRIPT_H #define OPENMW_ESM_GLOBALSCRIPT_H #include "locals.hpp" namespace ESM { class ESMReader; class ESMWriter; /// \brief Storage structure for global script state (only used in saved games) struct GlobalScript { std::string mId; /// \note must be lowercase Locals mLocals; int mRunning; std::string mTargetId; // for targeted scripts void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/inventorystate.cpp000066400000000000000000000063601264522266000231770ustar00rootroot00000000000000#include "inventorystate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::InventoryState::load (ESMReader &esm) { int index = 0; while (esm.isNextSub ("IOBJ")) { int unused; // no longer used esm.getHT(unused); ObjectState state; // obsolete if (esm.isNextSub("SLOT")) { int slot; esm.getHT(slot); mEquipmentSlots[index] = slot; } state.mRef.loadId(esm, true); state.load (esm); if (state.mCount == 0) continue; mItems.push_back (state); ++index; } //Next item is Levelled item while (esm.isNextSub("LEVM")) { //Get its name std::string id = esm.getHString(); int count; std::string parentGroup = ""; //Then get its count esm.getHNT (count, "COUN"); //Old save formats don't have information about parent group; check for that if(esm.isNextSub("LGRP")) //Newest saves contain parent group parentGroup = esm.getHString(); mLevelledItemMap[std::make_pair(id, parentGroup)] = count; } while (esm.isNextSub("MAGI")) { std::string id = esm.getHString(); std::vector > params; while (esm.isNextSub("RAND")) { float rand, multiplier; esm.getHT (rand); esm.getHNT (multiplier, "MULT"); params.push_back(std::make_pair(rand, multiplier)); } mPermanentMagicEffectMagnitudes[id] = params; } while (esm.isNextSub("EQUI")) { esm.getSubHeader(); int index; esm.getT(index); int slot; esm.getT(slot); mEquipmentSlots[index] = slot; } mSelectedEnchantItem = -1; esm.getHNOT(mSelectedEnchantItem, "SELE"); } void ESM::InventoryState::save (ESMWriter &esm) const { for (std::vector::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) { int unused = 0; esm.writeHNT ("IOBJ", unused); iter->save (esm, true); } for (std::map, int>::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) { esm.writeHNString ("LEVM", it->first.first); esm.writeHNT ("COUN", it->second); esm.writeHNString("LGRP", it->first.second); } for (TEffectMagnitudes::const_iterator it = mPermanentMagicEffectMagnitudes.begin(); it != mPermanentMagicEffectMagnitudes.end(); ++it) { esm.writeHNString("MAGI", it->first); const std::vector >& params = it->second; for (std::vector >::const_iterator pIt = params.begin(); pIt != params.end(); ++pIt) { esm.writeHNT ("RAND", pIt->first); esm.writeHNT ("MULT", pIt->second); } } for (std::map::const_iterator it = mEquipmentSlots.begin(); it != mEquipmentSlots.end(); ++it) { esm.startSubRecord("EQUI"); esm.writeT(it->first); esm.writeT(it->second); esm.endRecord("EQUI"); } if (mSelectedEnchantItem != -1) esm.writeHNT ("SELE", mSelectedEnchantItem); } openmw-openmw-0.38.0/components/esm/inventorystate.hpp000066400000000000000000000016351264522266000232040ustar00rootroot00000000000000#ifndef OPENMW_ESM_INVENTORYSTATE_H #define OPENMW_ESM_INVENTORYSTATE_H #include #include "objectstate.hpp" namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only /// \brief State for inventories and containers struct InventoryState { std::vector mItems; // std::map mEquipmentSlots; std::map, int> mLevelledItemMap; typedef std::map > > TEffectMagnitudes; TEffectMagnitudes mPermanentMagicEffectMagnitudes; int mSelectedEnchantItem; // For inventories only InventoryState() : mSelectedEnchantItem(-1) {} virtual ~InventoryState() {} virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/journalentry.cpp000066400000000000000000000017041264522266000226320ustar00rootroot00000000000000#include "journalentry.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::JournalEntry::load (ESMReader &esm) { esm.getHNOT (mType, "JETY"); mTopic = esm.getHNString ("YETO"); mInfo = esm.getHNString ("YEIN"); mText = esm.getHNString ("TEXT"); if (mType==Type_Journal) { esm.getHNT (mDay, "JEDA"); esm.getHNT (mMonth, "JEMO"); esm.getHNT (mDayOfMonth, "JEDM"); } else if (mType==Type_Topic) mActorName = esm.getHNOString("ACT_"); } void ESM::JournalEntry::save (ESMWriter &esm) const { esm.writeHNT ("JETY", mType); esm.writeHNString ("YETO", mTopic); esm.writeHNString ("YEIN", mInfo); esm.writeHNString ("TEXT", mText); if (mType==Type_Journal) { esm.writeHNT ("JEDA", mDay); esm.writeHNT ("JEMO", mMonth); esm.writeHNT ("JEDM", mDayOfMonth); } else if (mType==Type_Topic) esm.writeHNString ("ACT_", mActorName); } openmw-openmw-0.38.0/components/esm/journalentry.hpp000066400000000000000000000013501264522266000226340ustar00rootroot00000000000000#ifndef OPENMW_ESM_JOURNALENTRY_H #define OPENMW_ESM_JOURNALENTRY_H #include namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only struct JournalEntry { enum Type { Type_Journal = 0, Type_Topic = 1, Type_Quest = 2 }; int mType; std::string mTopic; std::string mInfo; std::string mText; std::string mActorName; // Could also be Actor ID to allow switching of localisation, but since mText is plaintext anyway... int mDay; // time stamp int mMonth; int mDayOfMonth; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/loadacti.cpp000066400000000000000000000033531264522266000216600ustar00rootroot00000000000000#include "loadacti.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Activator::sRecordId = REC_ACTI; void Activator::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); } void Activator::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); } void Activator::blank() { mName.clear(); mScript.clear(); mModel.clear(); } } openmw-openmw-0.38.0/components/esm/loadacti.hpp000066400000000000000000000011241264522266000216570ustar00rootroot00000000000000#ifndef OPENMW_ESM_ACTI_H #define OPENMW_ESM_ACTI_H #include namespace ESM { class ESMReader; class ESMWriter; struct Activator { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Activator"; } std::string mId, mName, mScript, mModel; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadalch.cpp000066400000000000000000000050571264522266000216520ustar00rootroot00000000000000#include "loadalch.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Potion::sRecordId = REC_ALCH; void Potion::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mEffects.mList.clear(); bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'T','E','X','T'>::value: // not ITEX here for some reason mIcon = esm.getHString(); break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'A','L','D','T'>::value: esm.getHT(mData, 12); hasData = true; break; case ESM::FourCC<'E','N','A','M'>::value: mEffects.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing ALDT subrecord"); } void Potion::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("TEXT", mIcon); esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("FNAM", mName); esm.writeHNT("ALDT", mData, 12); mEffects.save(esm); } void Potion::blank() { mData.mWeight = 0; mData.mValue = 0; mData.mAutoCalc = 0; mName.clear(); mModel.clear(); mIcon.clear(); mScript.clear(); mEffects.mList.clear(); } } openmw-openmw-0.38.0/components/esm/loadalch.hpp000066400000000000000000000014541264522266000216540ustar00rootroot00000000000000#ifndef OPENMW_ESM_ALCH_H #define OPENMW_ESM_ALCH_H #include #include "effectlist.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Alchemy item (potions) */ struct Potion { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Potion"; } struct ALDTstruct { float mWeight; int mValue; int mAutoCalc; }; ALDTstruct mData; std::string mId, mName, mModel, mIcon, mScript; EffectList mEffects; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadappa.cpp000066400000000000000000000045241264522266000216620ustar00rootroot00000000000000#include "loadappa.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Apparatus::sRecordId = REC_APPA; void Apparatus::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'A','A','D','T'>::value: esm.getHT(mData); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing AADT subrecord"); } void Apparatus::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); esm.writeHNT("AADT", mData, 16); esm.writeHNOCString("SCRI", mScript); esm.writeHNCString("ITEX", mIcon); } void Apparatus::blank() { mData.mType = 0; mData.mQuality = 0; mData.mWeight = 0; mData.mValue = 0; mModel.clear(); mIcon.clear(); mScript.clear(); mName.clear(); } } openmw-openmw-0.38.0/components/esm/loadappa.hpp000066400000000000000000000016031264522266000216620ustar00rootroot00000000000000#ifndef OPENMW_ESM_APPA_H #define OPENMW_ESM_APPA_H #include namespace ESM { class ESMReader; class ESMWriter; /* * Alchemist apparatus */ struct Apparatus { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Apparatus"; } enum AppaType { MortarPestle = 0, Albemic = 1, Calcinator = 2, Retort = 3 }; struct AADTstruct { int mType; float mQuality; float mWeight; int mValue; }; AADTstruct mData; std::string mId, mModel, mIcon, mScript, mName; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadarmo.cpp000066400000000000000000000070341264522266000216760ustar00rootroot00000000000000#include "loadarmo.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { void PartReferenceList::add(ESMReader &esm) { PartReference pr; esm.getHT(pr.mPart); // The INDX byte pr.mMale = esm.getHNOString("BNAM"); pr.mFemale = esm.getHNOString("CNAM"); mParts.push_back(pr); } void PartReferenceList::load(ESMReader &esm) { mParts.clear(); while (esm.isNextSub("INDX")) { add(esm); } } void PartReferenceList::save(ESMWriter &esm) const { for (std::vector::const_iterator it = mParts.begin(); it != mParts.end(); ++it) { esm.writeHNT("INDX", it->mPart); esm.writeHNOString("BNAM", it->mMale); esm.writeHNOString("CNAM", it->mFemale); } } unsigned int Armor::sRecordId = REC_ARMO; void Armor::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mParts.mParts.clear(); bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'A','O','D','T'>::value: esm.getHT(mData, 24); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::FourCC<'E','N','A','M'>::value: mEnchant = esm.getHString(); break; case ESM::FourCC<'I','N','D','X'>::value: mParts.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing CTDT subrecord"); } void Armor::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); esm.writeHNT("AODT", mData, 24); esm.writeHNOCString("ITEX", mIcon); mParts.save(esm); esm.writeHNOCString("ENAM", mEnchant); } void Armor::blank() { mData.mType = 0; mData.mWeight = 0; mData.mValue = 0; mData.mHealth = 0; mData.mEnchant = 0; mData.mArmor = 0; mParts.mParts.clear(); mName.clear(); mModel.clear(); mIcon.clear(); mScript.clear(); mEnchant.clear(); } } openmw-openmw-0.38.0/components/esm/loadarmo.hpp000066400000000000000000000042201264522266000216750ustar00rootroot00000000000000#ifndef OPENMW_ESM_ARMO_H #define OPENMW_ESM_ARMO_H #include #include namespace ESM { class ESMReader; class ESMWriter; enum PartReferenceType { PRT_Head = 0, PRT_Hair = 1, PRT_Neck = 2, PRT_Cuirass = 3, PRT_Groin = 4, PRT_Skirt = 5, PRT_RHand = 6, PRT_LHand = 7, PRT_RWrist = 8, PRT_LWrist = 9, PRT_Shield = 10, PRT_RForearm = 11, PRT_LForearm = 12, PRT_RUpperarm = 13, PRT_LUpperarm = 14, PRT_RFoot = 15, PRT_LFoot = 16, PRT_RAnkle = 17, PRT_LAnkle = 18, PRT_RKnee = 19, PRT_LKnee = 20, PRT_RLeg = 21, PRT_LLeg = 22, PRT_RPauldron = 23, PRT_LPauldron = 24, PRT_Weapon = 25, PRT_Tail = 26, PRT_Count = 27 }; // Reference to body parts struct PartReference { unsigned char mPart; // possible values [0, 26] std::string mMale, mFemale; }; // A list of references to body parts struct PartReferenceList { std::vector mParts; /// Load one part, assumes the subrecord name was already read void add(ESMReader &esm); /// TODO: remove this method. The ESM format does not guarantee that all Part subrecords follow one another. void load(ESMReader &esm); void save(ESMWriter &esm) const; }; struct Armor { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Armor"; } enum Type { Helmet = 0, Cuirass = 1, LPauldron = 2, RPauldron = 3, Greaves = 4, Boots = 5, LGauntlet = 6, RGauntlet = 7, Shield = 8, LBracer = 9, RBracer = 10 }; struct AODTstruct { int mType; float mWeight; int mValue, mHealth, mEnchant, mArmor; }; AODTstruct mData; PartReferenceList mParts; std::string mId, mName, mModel, mIcon, mScript, mEnchant; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadbody.cpp000066400000000000000000000037011264522266000216720ustar00rootroot00000000000000#include "loadbody.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int BodyPart::sRecordId = REC_BODY; void BodyPart::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mRace = esm.getHString(); break; case ESM::FourCC<'B','Y','D','T'>::value: esm.getHT(mData, 4); hasData = true; break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing BYDT subrecord"); } void BodyPart::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mRace); esm.writeHNT("BYDT", mData, 4); } void BodyPart::blank() { mData.mPart = 0; mData.mVampire = 0; mData.mFlags = 0; mData.mType = 0; mModel.clear(); mRace.clear(); } } openmw-openmw-0.38.0/components/esm/loadbody.hpp000066400000000000000000000025351264522266000217030ustar00rootroot00000000000000#ifndef OPENMW_ESM_BODY_H #define OPENMW_ESM_BODY_H #include namespace ESM { class ESMReader; class ESMWriter; struct BodyPart { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "BodyPart"; } enum MeshPart { MP_Head = 0, MP_Hair = 1, MP_Neck = 2, MP_Chest = 3, MP_Groin = 4, MP_Hand = 5, MP_Wrist = 6, MP_Forearm = 7, MP_Upperarm = 8, MP_Foot = 9, MP_Ankle = 10, MP_Knee = 11, MP_Upperleg = 12, MP_Clavicle = 13, MP_Tail = 14, MP_Count = 15 }; enum Flags { BPF_Female = 1, BPF_NotPlayable = 2 }; enum MeshType { MT_Skin = 0, MT_Clothing = 1, MT_Armor = 2 }; struct BYDTstruct { unsigned char mPart; // mesh part unsigned char mVampire; // boolean unsigned char mFlags; unsigned char mType; // mesh type }; BYDTstruct mData; std::string mId, mModel, mRace; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadbook.cpp000066400000000000000000000053711264522266000216740ustar00rootroot00000000000000#include "loadbook.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Book::sRecordId = REC_BOOK; void Book::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'B','K','D','T'>::value: esm.getHT(mData, 20); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::FourCC<'E','N','A','M'>::value: mEnchant = esm.getHString(); break; case ESM::FourCC<'T','E','X','T'>::value: mText = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing BKDT subrecord"); } void Book::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("BKDT", mData, 20); esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); esm.writeHNOString("TEXT", mText); esm.writeHNOCString("ENAM", mEnchant); } void Book::blank() { mData.mWeight = 0; mData.mValue = 0; mData.mIsScroll = 0; mData.mSkillID = 0; mData.mEnchant = 0; mName.clear(); mModel.clear(); mIcon.clear(); mScript.clear(); mEnchant.clear(); mText.clear(); } } openmw-openmw-0.38.0/components/esm/loadbook.hpp000066400000000000000000000014451264522266000216770ustar00rootroot00000000000000#ifndef OPENMW_ESM_BOOK_H #define OPENMW_ESM_BOOK_H #include namespace ESM { /* * Books, magic scrolls, notes and so on */ class ESMReader; class ESMWriter; struct Book { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Book"; } struct BKDTstruct { float mWeight; int mValue, mIsScroll, mSkillID, mEnchant; }; BKDTstruct mData; std::string mName, mModel, mIcon, mScript, mEnchant, mText; std::string mId; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadbsgn.cpp000066400000000000000000000037301264522266000216700ustar00rootroot00000000000000#include "loadbsgn.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int BirthSign::sRecordId = REC_BSGN; void BirthSign::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mPowers.mList.clear(); bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'T','N','A','M'>::value: mTexture = esm.getHString(); break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; case ESM::FourCC<'N','P','C','S'>::value: mPowers.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); } void BirthSign::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("TNAM", mTexture); esm.writeHNOCString("DESC", mDescription); mPowers.save(esm); } void BirthSign::blank() { mName.clear(); mDescription.clear(); mTexture.clear(); mPowers.mList.clear(); } } openmw-openmw-0.38.0/components/esm/loadbsgn.hpp000066400000000000000000000013261264522266000216740ustar00rootroot00000000000000#ifndef OPENMW_ESM_BSGN_H #define OPENMW_ESM_BSGN_H #include #include "spelllist.hpp" namespace ESM { class ESMReader; class ESMWriter; struct BirthSign { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "BirthSign"; } std::string mId, mName, mDescription, mTexture; // List of powers and abilities that come with this birth sign. SpellList mPowers; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). }; } #endif openmw-openmw-0.38.0/components/esm/loadcell.cpp000066400000000000000000000175701264522266000216650ustar00rootroot00000000000000#include "loadcell.hpp" #include #include #include #include #include #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" #include "cellid.hpp" namespace { ///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum void adjustRefNum (ESM::RefNum& refNum, ESM::ESMReader& reader) { unsigned int local = (refNum.mIndex & 0xff000000) >> 24; // If we have an index value that does not make sense, assume that it was an addition // by the present plugin (but a faulty one) if (local && local <= reader.getGameFiles().size()) { // If the most significant 8 bits are used, then this reference already exists. // In this case, do not spawn a new reference, but overwrite the old one. refNum.mIndex &= 0x00ffffff; // delete old plugin ID refNum.mContentFile = reader.getGameFiles()[local-1].index; } else { // This is an addition by the present plugin. Set the corresponding plugin index. refNum.mContentFile = reader.getIndex(); } } } namespace ESM { unsigned int Cell::sRecordId = REC_CELL; // Some overloaded compare operators. bool operator== (const MovedCellRef& ref, const RefNum& refNum) { return ref.mRefNum == refNum; } bool operator== (const CellRef& ref, const RefNum& refNum) { return ref.mRefNum == refNum; } void Cell::load(ESMReader &esm, bool &isDeleted, bool saveContext) { loadNameAndData(esm, isDeleted); loadCell(esm, saveContext); } void Cell::loadNameAndData(ESMReader &esm, bool &isDeleted) { isDeleted = false; blank(); bool hasData = false; bool isLoaded = false; while (!isLoaded && esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mName = esm.getHString(); break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mData, 12); hasData = true; break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.cacheSubName(); isLoaded = true; break; } } if (!hasData) esm.fail("Missing DATA subrecord"); mCellId.mPaged = !(mData.mFlags & Interior); if (mCellId.mPaged) { mCellId.mWorldspace = "sys::default"; mCellId.mIndex.mX = mData.mX; mCellId.mIndex.mY = mData.mY; } else { mCellId.mWorldspace = Misc::StringUtils::lowerCase (mName); mCellId.mIndex.mX = 0; mCellId.mIndex.mY = 0; } } void Cell::loadCell(ESMReader &esm, bool saveContext) { bool isLoaded = false; while (!isLoaded && esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'I','N','T','V'>::value: int waterl; esm.getHT(waterl); mWater = static_cast(waterl); mWaterInt = true; break; case ESM::FourCC<'W','H','G','T'>::value: esm.getHT(mWater); mWaterInt = false; break; case ESM::FourCC<'A','M','B','I'>::value: esm.getHT(mAmbi); break; case ESM::FourCC<'R','G','N','N'>::value: mRegion = esm.getHString(); break; case ESM::FourCC<'N','A','M','5'>::value: esm.getHT(mMapColor); break; case ESM::FourCC<'N','A','M','0'>::value: esm.getHT(mRefNumCounter); break; default: esm.cacheSubName(); isLoaded = true; break; } } if (saveContext) { mContextList.push_back(esm.getContext()); esm.skipRecord(); } } void Cell::postLoad(ESMReader &esm) { // Save position of the cell references and move on mContextList.push_back(esm.getContext()); esm.skipRecord(); } void Cell::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNOCString("NAME", mName); esm.writeHNT("DATA", mData, 12); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } if (mData.mFlags & Interior) { if (mWaterInt) { int water = (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); esm.writeHNT("INTV", water); } else { esm.writeHNT("WHGT", mWater); } if (mData.mFlags & QuasiEx) esm.writeHNOCString("RGNN", mRegion); else esm.writeHNT("AMBI", mAmbi, 16); } else { esm.writeHNOCString("RGNN", mRegion); if (mMapColor != 0) esm.writeHNT("NAM5", mMapColor); } if (mRefNumCounter != 0) esm.writeHNT("NAM0", mRefNumCounter); } void Cell::restore(ESMReader &esm, int iCtx) const { esm.restoreContext(mContextList.at (iCtx)); } std::string Cell::getDescription() const { if (mData.mFlags & Interior) { return mName; } else { std::ostringstream stream; stream << mData.mX << ", " << mData.mY; return stream.str(); } } bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, bool ignoreMoves, MovedCellRef *mref) { isDeleted = false; // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) return false; // NOTE: We should not need this check. It is a safety check until we have checked // more plugins, and how they treat these moved references. if (esm.isNextSub("MVRF")) { if (ignoreMoves) { esm.getHT (mref->mRefNum.mIndex); esm.getHNOT (mref->mTarget, "CNDT"); adjustRefNum (mref->mRefNum, esm); } else { // skip rest of cell record (moved references), they are handled elsewhere esm.skipRecord(); // skip MVRF, CNDT return false; } } if (esm.peekNextSub("FRMR")) { ref.load (esm, isDeleted); // Identify references belonging to a parent file and adapt the ID accordingly. adjustRefNum (ref.mRefNum, esm); return true; } return false; } bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) { esm.getHT(mref.mRefNum.mIndex); esm.getHNOT(mref.mTarget, "CNDT"); adjustRefNum (mref.mRefNum, esm); return true; } void Cell::blank() { mName.clear(); mRegion.clear(); mWater = 0; mWaterInt = false; mMapColor = 0; mRefNumCounter = 0; mData.mFlags = 0; mData.mX = 0; mData.mY = 0; mAmbi.mAmbient = 0; mAmbi.mSunlight = 0; mAmbi.mFog = 0; mAmbi.mFogDensity = 0; } const CellId& Cell::getCellId() const { return mCellId; } } openmw-openmw-0.38.0/components/esm/loadcell.hpp000066400000000000000000000130141264522266000216570ustar00rootroot00000000000000#ifndef OPENMW_ESM_CELL_H #define OPENMW_ESM_CELL_H #include #include #include #include "esmcommon.hpp" #include "defs.hpp" #include "cellref.hpp" #include "cellid.hpp" namespace MWWorld { class ESMStore; } namespace ESM { class ESMReader; class ESMWriter; /* Moved cell reference tracking object. This mainly stores the target cell of the reference, so we can easily know where it has been moved when another plugin tries to move it independently. Unfortunately, we need to implement this here. */ class MovedCellRef { public: RefNum mRefNum; // Target cell (if exterior) int mTarget[2]; // TODO: Support moving references between exterior and interior cells! // This may happen in saves, when an NPC follows the player. Tribunal // introduces a henchman (which no one uses), so we may need this as well. }; /// Overloaded compare operator used to search inside a list of cell refs. bool operator==(const MovedCellRef& ref, const RefNum& refNum); bool operator==(const CellRef& ref, const RefNum& refNum); typedef std::list MovedCellRefTracker; typedef std::list CellRefTracker; /* Cells hold data about objects, creatures, statics (rocks, walls, buildings) and landscape (for exterior cells). Cells frequently also has other associated LAND and PGRD records. Combined, all this data can be huge, and we cannot load it all at startup. Instead, the strategy we use is to remember the file position of each cell (using ESMReader::getContext()) and jumping back into place whenever we need to load a given cell. */ struct Cell { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Cell"; } enum Flags { Interior = 0x01, // Interior cell HasWater = 0x02, // Does this cell have a water surface NoSleep = 0x04, // Is it allowed to sleep here (without a bed) QuasiEx = 0x80 // Behave like exterior (Tribunal+), with // skybox and weather }; struct DATAstruct { int mFlags; int mX, mY; }; struct AMBIstruct { Color mAmbient, mSunlight, mFog; float mFogDensity; }; Cell() : mName(""), mRegion(""), mWater(0), mWaterInt(false), mMapColor(0), mRefNumCounter(0) {} // Interior cells are indexed by this (it's the 'id'), for exterior // cells it is optional. std::string mName; // Optional region name for exterior and quasi-exterior cells. std::string mRegion; std::vector mContextList; // File position; multiple positions for multiple plugin support DATAstruct mData; CellId mCellId; AMBIstruct mAmbi; float mWater; // Water level bool mWaterInt; int mMapColor; // Counter for RefNums. This is only used during content file editing and has no impact on gameplay. // It prevents overwriting previous refNums, even if they were deleted. // as that would collide with refs when a content file is upgraded. int mRefNumCounter; // References "leased" from another cell (i.e. a different cell // introduced this ref, and it has been moved here by a plugin) CellRefTracker mLeasedRefs; MovedCellRefTracker mMovedRefs; void postLoad(ESMReader &esm); // This method is left in for compatibility with esmtool. Parsing moved references currently requires // passing ESMStore, bit it does not know about this parameter, so we do it this way. void load(ESMReader &esm, bool &isDeleted, bool saveContext = true); // Load everything (except references) void loadNameAndData(ESMReader &esm, bool &isDeleted); // Load NAME and DATAstruct void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except NAME, DATAstruct and references void save(ESMWriter &esm, bool isDeleted = false) const; bool isExterior() const { return !(mData.mFlags & Interior); } int getGridX() const { return mData.mX; } int getGridY() const { return mData.mY; } bool hasWater() const { return (mData.mFlags&HasWater) != 0; } // Restore the given reader to the stored position. Will try to open // the file matching the stored file name. If you want to read from // somewhere other than the file system, you need to pre-open the // ESMReader, and the filename must match the stored filename // exactly. void restore(ESMReader &esm, int iCtx) const; std::string getDescription() const; ///< Return a short string describing the cell (mostly used for debugging/logging purpose) /* Get the next reference in this cell, if any. Returns false when there are no more references in the cell. All fields of the CellRef struct are overwritten. You can safely reuse one memory location without blanking it between calls. */ /// \param ignoreMoves ignore MVRF record and read reference like a regular CellRef. static bool getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, bool ignoreMoves = false, MovedCellRef *mref = 0); /* This fetches an MVRF record, which is used to track moved references. * Since they are comparably rare, we use a separate method for this. */ static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref); void blank(); ///< Set record to default state (does not touch the ID/index). const CellId& getCellId() const; }; } #endif openmw-openmw-0.38.0/components/esm/loadclas.cpp000066400000000000000000000056741264522266000216720ustar00rootroot00000000000000#include "loadclas.hpp" #include #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Class::sRecordId = REC_CLAS; const Class::Specialization Class::sSpecializationIds[3] = { Class::Combat, Class::Magic, Class::Stealth }; const char *Class::sGmstSpecializationIds[3] = { "sSpecializationCombat", "sSpecializationMagic", "sSpecializationStealth" }; int& Class::CLDTstruct::getSkill (int index, bool major) { if (index<0 || index>=5) throw std::logic_error ("skill index out of range"); return mSkills[index][major ? 1 : 0]; } int Class::CLDTstruct::getSkill (int index, bool major) const { if (index<0 || index>=5) throw std::logic_error ("skill index out of range"); return mSkills[index][major ? 1 : 0]; } void Class::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'C','L','D','T'>::value: esm.getHT(mData, 60); if (mData.mIsPlayable > 1) esm.fail("Unknown bool value"); hasData = true; break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing CLDT subrecord"); } void Class::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNOCString("FNAM", mName); esm.writeHNT("CLDT", mData, 60); esm.writeHNOString("DESC", mDescription); } void Class::blank() { mName.clear(); mDescription.clear(); mData.mAttribute[0] = mData.mAttribute[1] = 0; mData.mSpecialization = 0; mData.mIsPlayable = 0; mData.mCalc = 0; for (int i=0; i<5; ++i) for (int i2=0; i2<2; ++i2) mData.mSkills[i][i2] = 0; } } openmw-openmw-0.38.0/components/esm/loadclas.hpp000066400000000000000000000040111264522266000216570ustar00rootroot00000000000000#ifndef OPENMW_ESM_CLAS_H #define OPENMW_ESM_CLAS_H #include namespace ESM { class ESMReader; class ESMWriter; /* * Character class definitions */ // These flags tells us which items should be auto-calculated for this // class struct Class { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Class"; } enum AutoCalc { Weapon = 0x00001, Armor = 0x00002, Clothing = 0x00004, Books = 0x00008, Ingredient = 0x00010, Lockpick = 0x00020, Probe = 0x00040, Lights = 0x00080, Apparatus = 0x00100, Repair = 0x00200, Misc = 0x00400, Spells = 0x00800, MagicItems = 0x01000, Potions = 0x02000, Training = 0x04000, Spellmaking = 0x08000, Enchanting = 0x10000, RepairItem = 0x20000 }; enum Specialization { Combat = 0, Magic = 1, Stealth = 2 }; static const Specialization sSpecializationIds[3]; static const char *sGmstSpecializationIds[3]; struct CLDTstruct { int mAttribute[2]; // Attributes that get class bonus int mSpecialization; // 0 = Combat, 1 = Magic, 2 = Stealth int mSkills[5][2]; // Minor and major skills. int mIsPlayable; // 0x0001 - Playable class // I have no idea how to autocalculate these items... int mCalc; int& getSkill (int index, bool major); ///< Throws an exception for invalid values of \a index. int getSkill (int index, bool major) const; ///< Throws an exception for invalid values of \a index. }; // 60 bytes std::string mId, mName, mDescription; CLDTstruct mData; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). }; } #endif openmw-openmw-0.38.0/components/esm/loadclot.cpp000066400000000000000000000053731264522266000217050ustar00rootroot00000000000000#include "loadclot.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Clothing::sRecordId = REC_CLOT; void Clothing::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mParts.mParts.clear(); bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'C','T','D','T'>::value: esm.getHT(mData, 12); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::FourCC<'E','N','A','M'>::value: mEnchant = esm.getHString(); break; case ESM::FourCC<'I','N','D','X'>::value: mParts.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing CTDT subrecord"); } void Clothing::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("CTDT", mData, 12); esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); mParts.save(esm); esm.writeHNOCString("ENAM", mEnchant); } void Clothing::blank() { mData.mType = 0; mData.mWeight = 0; mData.mValue = 0; mData.mEnchant = 0; mParts.mParts.clear(); mName.clear(); mModel.clear(); mIcon.clear(); mEnchant.clear(); mScript.clear(); } } openmw-openmw-0.38.0/components/esm/loadclot.hpp000066400000000000000000000020311264522266000216760ustar00rootroot00000000000000#ifndef OPENMW_ESM_CLOT_H #define OPENMW_ESM_CLOT_H #include #include "loadarmo.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Clothing */ struct Clothing { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Clothing"; } enum Type { Pants = 0, Shoes = 1, Shirt = 2, Belt = 3, Robe = 4, RGlove = 5, LGlove = 6, Skirt = 7, Ring = 8, Amulet = 9 }; struct CTDTstruct { int mType; float mWeight; short mValue; short mEnchant; }; CTDTstruct mData; PartReferenceList mParts; std::string mId, mName, mModel, mIcon, mEnchant, mScript; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadcont.cpp000066400000000000000000000062701264522266000217040ustar00rootroot00000000000000#include "loadcont.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { void InventoryList::add(ESMReader &esm) { ContItem ci; esm.getHT(ci, 36); mList.push_back(ci); } void InventoryList::save(ESMWriter &esm) const { for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("NPCO", *it, 36); } } unsigned int Container::sRecordId = REC_CONT; void Container::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mInventory.mList.clear(); bool hasName = false; bool hasWeight = false; bool hasFlags = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'C','N','D','T'>::value: esm.getHT(mWeight, 4); hasWeight = true; break; case ESM::FourCC<'F','L','A','G'>::value: esm.getHT(mFlags, 4); if (mFlags & 0xf4) esm.fail("Unknown flags"); if (!(mFlags & 0x8)) esm.fail("Flag 8 not set"); hasFlags = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'N','P','C','O'>::value: mInventory.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasWeight && !isDeleted) esm.fail("Missing CNDT subrecord"); if (!hasFlags && !isDeleted) esm.fail("Missing FLAG subrecord"); } void Container::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("CNDT", mWeight, 4); esm.writeHNT("FLAG", mFlags, 4); esm.writeHNOCString("SCRI", mScript); mInventory.save(esm); } void Container::blank() { mName.clear(); mModel.clear(); mScript.clear(); mWeight = 0; mFlags = 0x8; // set default flag value mInventory.mList.clear(); } } openmw-openmw-0.38.0/components/esm/loadcont.hpp000066400000000000000000000023161264522266000217060ustar00rootroot00000000000000#ifndef OPENMW_ESM_CONT_H #define OPENMW_ESM_CONT_H #include #include #include "esmcommon.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Container definition */ struct ContItem { int mCount; NAME32 mItem; }; /// InventoryList, NPCO subrecord struct InventoryList { std::vector mList; /// Load one item, assumes subrecord name is already read void add(ESMReader &esm); void save(ESMWriter &esm) const; }; struct Container { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Container"; } enum Flags { Organic = 1, // Objects cannot be placed in this container Respawn = 2, // Respawns after 4 months Unknown = 8 }; std::string mId, mName, mModel, mScript; float mWeight; // Not sure, might be max total weight allowed? int mFlags; InventoryList mInventory; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadcrea.cpp000066400000000000000000000125241264522266000216520ustar00rootroot00000000000000#include "loadcrea.hpp" #include #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Creature::sRecordId = REC_CREA; void Creature::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mPersistent = (esm.getRecordFlags() & 0x0400) != 0; mAiPackage.mList.clear(); mInventory.mList.clear(); mSpells.mList.clear(); mTransport.mList.clear(); mScale = 1.f; mHasAI = false; bool hasName = false; bool hasNpdt = false; bool hasFlags = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'C','N','A','M'>::value: mOriginal = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'N','P','D','T'>::value: esm.getHT(mData, 96); hasNpdt = true; break; case ESM::FourCC<'F','L','A','G'>::value: esm.getHT(mFlags); hasFlags = true; break; case ESM::FourCC<'X','S','C','L'>::value: esm.getHT(mScale); break; case ESM::FourCC<'N','P','C','O'>::value: mInventory.add(esm); break; case ESM::FourCC<'N','P','C','S'>::value: mSpells.add(esm); break; case ESM::FourCC<'A','I','D','T'>::value: esm.getHExact(&mAiData, sizeof(mAiData)); mHasAI = true; break; case ESM::FourCC<'D','O','D','T'>::value: case ESM::FourCC<'D','N','A','M'>::value: mTransport.add(esm); break; case AI_Wander: case AI_Activate: case AI_Escort: case AI_Follow: case AI_Travel: case AI_CNDT: mAiPackage.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; case ESM::FourCC<'I','N','D','X'>::value: // seems to occur only in .ESS files, unsure of purpose int index; esm.getHT(index); std::cerr << "Creature::load: Unhandled INDX " << index << std::endl; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasNpdt && !isDeleted) esm.fail("Missing NPDT subrecord"); if (!hasFlags && !isDeleted) esm.fail("Missing FLAG subrecord"); } void Creature::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("CNAM", mOriginal); esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); esm.writeHNT("NPDT", mData, 96); esm.writeHNT("FLAG", mFlags); if (mScale != 1.0) { esm.writeHNT("XSCL", mScale); } mInventory.save(esm); mSpells.save(esm); if (mAiData.mHello != 0 || mAiData.mFight != 0 || mAiData.mFlee != 0 || mAiData.mAlarm != 0 || mAiData.mServices != 0) { esm.writeHNT("AIDT", mAiData, sizeof(mAiData)); } mTransport.save(esm); mAiPackage.save(esm); } void Creature::blank() { mData.mType = 0; mData.mLevel = 0; mData.mStrength = mData.mIntelligence = mData.mWillpower = mData.mAgility = mData.mSpeed = mData.mEndurance = mData.mPersonality = mData.mLuck = 0; mData.mHealth = mData.mMana = mData.mFatigue = 0; mData.mSoul = 0; mData.mCombat = mData.mMagic = mData.mStealth = 0; for (int i=0; i<6; ++i) mData.mAttack[i] = 0; mData.mGold = 0; mFlags = 0; mScale = 1.f; mModel.clear(); mName.clear(); mScript.clear(); mOriginal.clear(); mInventory.mList.clear(); mSpells.mList.clear(); mHasAI = false; mAiData.blank(); mAiData.mServices = 0; mAiPackage.mList.clear(); mTransport.mList.clear(); } const std::vector& Creature::getTransport() const { return mTransport.mList; } } openmw-openmw-0.38.0/components/esm/loadcrea.hpp000066400000000000000000000051171264522266000216570ustar00rootroot00000000000000#ifndef OPENMW_ESM_CREA_H #define OPENMW_ESM_CREA_H #include #include "loadcont.hpp" #include "spelllist.hpp" #include "aipackage.hpp" #include "transport.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Creature definition * */ struct Creature { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Creature"; } // Default is 0x48? enum Flags { // Movement types Bipedal = 0x001, Swims = 0x010, Flies = 0x020, // Don't know what happens if several Walks = 0x040, // of these are set Respawn = 0x002, Weapon = 0x004, // Has weapon and shield None = 0x008, // ?? This flag appears set for every creature in Morrowind.esm Essential = 0x080, // Blood types Skeleton = 0x400, Metal = 0x800 }; enum Type { Creatures = 0, Daedra = 1, Undead = 2, Humanoid = 3 }; struct NPDTstruct { int mType; // For creatures we obviously have to use ints, not shorts and // bytes like we use for NPCs.... this file format just makes so // much sense! (Still, _much_ easier to decode than the NIFs.) int mLevel; int mStrength, mIntelligence, mWillpower, mAgility, mSpeed, mEndurance, mPersonality, mLuck; int mHealth, mMana, mFatigue; // Stats int mSoul; // The creatures soul value (used with soul gems.) // Creatures have generalized combat, magic and stealth stats which substitute for // the specific skills (in the same way as specializations). int mCombat, mMagic, mStealth; int mAttack[6]; // AttackMin1, AttackMax1, ditto2, ditto3 int mGold; }; // 96 byte NPDTstruct mData; int mFlags; bool mPersistent; float mScale; std::string mId, mModel, mName, mScript; std::string mOriginal; // Base creature that this is a modification of InventoryList mInventory; SpellList mSpells; bool mHasAI; AIData mAiData; AIPackageList mAiPackage; Transport mTransport; const std::vector& getTransport() const; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loaddial.cpp000066400000000000000000000077211264522266000216540ustar00rootroot00000000000000#include "loaddial.hpp" #include #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Dialogue::sRecordId = REC_DIAL; void Dialogue::load(ESMReader &esm, bool &isDeleted) { loadId(esm); loadData(esm, isDeleted); } void Dialogue::loadId(ESMReader &esm) { mId = esm.getHNString("NAME"); } void Dialogue::loadData(ESMReader &esm, bool &isDeleted) { isDeleted = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'D','A','T','A'>::value: { esm.getSubHeader(); int size = esm.getSubSize(); if (size == 1) { esm.getT(mType); } else { esm.skip(size); } break; } case ESM::SREC_DELE: esm.skipHSub(); mType = Unknown; isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } } void Dialogue::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); } else { esm.writeHNT("DATA", mType); } } void Dialogue::blank() { mInfo.clear(); } void Dialogue::readInfo(ESMReader &esm, bool merge) { ESM::DialInfo info; info.loadId(esm); bool isDeleted = false; if (!merge || mInfo.empty()) { info.loadData(esm, isDeleted); mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.end(), info), isDeleted); return; } InfoContainer::iterator it = mInfo.end(); LookupMap::iterator lookup; lookup = mLookup.find(info.mId); if (lookup != mLookup.end()) { it = lookup->second.first; // Merge with existing record. Only the subrecords that are present in // the new record will be overwritten. it->loadData(esm, isDeleted); info = *it; // Since the record merging may have changed the next/prev linked list connection, we need to re-insert the record mInfo.erase(it); mLookup.erase(lookup); } else { info.loadData(esm, isDeleted); } if (info.mNext.empty()) { mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.end(), info), isDeleted); return; } if (info.mPrev.empty()) { mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.begin(), info), isDeleted); return; } lookup = mLookup.find(info.mPrev); if (lookup != mLookup.end()) { it = lookup->second.first; mLookup[info.mId] = std::make_pair(mInfo.insert(++it, info), isDeleted); return; } lookup = mLookup.find(info.mNext); if (lookup != mLookup.end()) { it = lookup->second.first; mLookup[info.mId] = std::make_pair(mInfo.insert(it, info), isDeleted); return; } std::cerr << "Failed to insert info " << info.mId << std::endl; } void Dialogue::clearDeletedInfos() { LookupMap::const_iterator current = mLookup.begin(); LookupMap::const_iterator end = mLookup.end(); for (; current != end; ++current) { if (current->second.second) { mInfo.erase(current->second.first); } } mLookup.clear(); } } openmw-openmw-0.38.0/components/esm/loaddial.hpp000066400000000000000000000034361264522266000216600ustar00rootroot00000000000000#ifndef OPENMW_ESM_DIAL_H #define OPENMW_ESM_DIAL_H #include #include #include #include "loadinfo.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Dialogue topic and journal entries. The actual data is contained in * the INFO records following the DIAL. */ struct Dialogue { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Dialogue"; } enum Type { Topic = 0, Voice = 1, Greeting = 2, Persuasion = 3, Journal = 4, Unknown = -1 // Used for deleted dialogues }; std::string mId; signed char mType; typedef std::list InfoContainer; // Parameters: Info ID, (Info iterator, Deleted flag) typedef std::map > LookupMap; InfoContainer mInfo; // This is only used during the loading phase to speed up DialInfo merging. LookupMap mLookup; void load(ESMReader &esm, bool &isDeleted); ///< Loads all sub-records of Dialogue record void loadId(ESMReader &esm); ///< Loads NAME sub-record of Dialogue record void loadData(ESMReader &esm, bool &isDeleted); ///< Loads all sub-records of Dialogue record, except NAME sub-record void save(ESMWriter &esm, bool isDeleted = false) const; /// Remove all INFOs that are deleted void clearDeletedInfos(); /// Read the next info record /// @param merge Merge with existing list, or just push each record to the end of the list? void readInfo (ESM::ESMReader& esm, bool merge); void blank(); ///< Set record to default state (does not touch the ID and does not change the type). }; } #endif openmw-openmw-0.38.0/components/esm/loaddoor.cpp000066400000000000000000000042051264522266000217000ustar00rootroot00000000000000#include "loaddoor.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Door::sRecordId = REC_DOOR; void Door::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'S','N','A','M'>::value: mOpenSound = esm.getHString(); break; case ESM::FourCC<'A','N','A','M'>::value: mCloseSound = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); } void Door::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("SNAM", mOpenSound); esm.writeHNOCString("ANAM", mCloseSound); } void Door::blank() { mName.clear(); mModel.clear(); mScript.clear(); mOpenSound.clear(); mCloseSound.clear(); } } openmw-openmw-0.38.0/components/esm/loaddoor.hpp000066400000000000000000000011421264522266000217020ustar00rootroot00000000000000#ifndef OPENMW_ESM_DOOR_H #define OPENMW_ESM_DOOR_H #include namespace ESM { class ESMReader; class ESMWriter; struct Door { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Door"; } std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadench.cpp000066400000000000000000000034331264522266000216540ustar00rootroot00000000000000#include "loadench.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Enchantment::sRecordId = REC_ENCH; void Enchantment::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mEffects.mList.clear(); bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'E','N','D','T'>::value: esm.getHT(mData, 16); hasData = true; break; case ESM::FourCC<'E','N','A','M'>::value: mEffects.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing ENDT subrecord"); } void Enchantment::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNT("ENDT", mData, 16); mEffects.save(esm); } void Enchantment::blank() { mData.mType = 0; mData.mCost = 0; mData.mCharge = 0; mData.mAutocalc = 0; mEffects.mList.clear(); } } openmw-openmw-0.38.0/components/esm/loadench.hpp000066400000000000000000000017311264522266000216600ustar00rootroot00000000000000#ifndef OPENMW_ESM_ENCH_H #define OPENMW_ESM_ENCH_H #include #include "effectlist.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Enchantments */ struct Enchantment { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Enchantment"; } enum Type { CastOnce = 0, WhenStrikes = 1, WhenUsed = 2, ConstantEffect = 3 }; struct ENDTstruct { int mType; int mCost; int mCharge; int mAutocalc; // Guessing this is 1 if we are supposed to auto // calculate }; std::string mId; ENDTstruct mData; EffectList mEffects; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadfact.cpp000066400000000000000000000072731264522266000216620ustar00rootroot00000000000000#include "loadfact.hpp" #include #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Faction::sRecordId = REC_FACT; int& Faction::FADTstruct::getSkill (int index, bool ignored) { if (index<0 || index>=7) throw std::logic_error ("skill index out of range"); return mSkills[index]; } int Faction::FADTstruct::getSkill (int index, bool ignored) const { if (index<0 || index>=7) throw std::logic_error ("skill index out of range"); return mSkills[index]; } void Faction::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mReactions.clear(); for (int i=0;i<10;++i) mRanks[i].clear(); int rankCounter = 0; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'R','N','A','M'>::value: if (rankCounter >= 10) esm.fail("Rank out of range"); mRanks[rankCounter++] = esm.getHString(); break; case ESM::FourCC<'F','A','D','T'>::value: esm.getHT(mData, 240); if (mData.mIsHidden > 1) esm.fail("Unknown flag!"); hasData = true; break; case ESM::FourCC<'A','N','A','M'>::value: { std::string faction = esm.getHString(); int reaction; esm.getHNT(reaction, "INTV"); mReactions[faction] = reaction; break; } case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing FADT subrecord"); } void Faction::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNOCString("FNAM", mName); for (int i = 0; i < 10; i++) { if (mRanks[i].empty()) break; esm.writeHNString("RNAM", mRanks[i], 32); } esm.writeHNT("FADT", mData, 240); for (std::map::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it) { esm.writeHNString("ANAM", it->first); esm.writeHNT("INTV", it->second); } } void Faction::blank() { mName.clear(); mData.mAttribute[0] = mData.mAttribute[1] = 0; mData.mIsHidden = 0; for (int i=0; i<10; ++i) { mData.mRankData[i].mAttribute1 = mData.mRankData[i].mAttribute2 = 0; mData.mRankData[i].mSkill1 = mData.mRankData[i].mSkill2 = 0; mData.mRankData[i].mFactReaction = 0; mRanks[i].clear(); } for (int i=0; i<7; ++i) mData.mSkills[i] = 0; mReactions.clear(); } } openmw-openmw-0.38.0/components/esm/loadfact.hpp000066400000000000000000000033541264522266000216630ustar00rootroot00000000000000#ifndef OPENMW_ESM_FACT_H #define OPENMW_ESM_FACT_H #include #include namespace ESM { class ESMReader; class ESMWriter; /* * Faction definitions */ // Requirements for each rank struct RankData { int mAttribute1, mAttribute2; // Attribute level int mSkill1, mSkill2; // Skill level (faction skills given in // skillID below.) You need one skill at // level 'skill1' and two skills at level // 'skill2' to advance to this rank. int mFactReaction; // Reaction from faction members }; struct Faction { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Faction"; } std::string mId, mName; struct FADTstruct { // Which attributes we like int mAttribute[2]; RankData mRankData[10]; int mSkills[7]; // IDs of skills this faction require // Each element will either contain an ESM::Skill index, or -1. int mIsHidden; // 1 - hidden from player int& getSkill (int index, bool ignored = false); ///< Throws an exception for invalid values of \a index. int getSkill (int index, bool ignored = false) const; ///< Throws an exception for invalid values of \a index. }; // 240 bytes FADTstruct mData; // std::map mReactions; // Name of faction ranks (may be empty for NPC factions) std::string mRanks[10]; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). }; } #endif openmw-openmw-0.38.0/components/esm/loadglob.cpp000066400000000000000000000017761264522266000216720ustar00rootroot00000000000000#include "loadglob.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Global::sRecordId = REC_GLOB; void Global::load (ESMReader &esm, bool &isDeleted) { isDeleted = false; mId = esm.getHNString ("NAME"); if (esm.isNextSub ("DELE")) { esm.skipHSub(); isDeleted = true; } else { mValue.read (esm, ESM::Variant::Format_Global); } } void Global::save (ESMWriter &esm, bool isDeleted) const { esm.writeHNCString ("NAME", mId); if (isDeleted) { esm.writeHNCString ("DELE", ""); } else { mValue.write (esm, ESM::Variant::Format_Global); } } void Global::blank() { mValue.setType (ESM::VT_None); } bool operator== (const Global& left, const Global& right) { return left.mId==right.mId && left.mValue==right.mValue; } } openmw-openmw-0.38.0/components/esm/loadglob.hpp000066400000000000000000000013011264522266000216570ustar00rootroot00000000000000#ifndef OPENMW_ESM_GLOB_H #define OPENMW_ESM_GLOB_H #include #include "variant.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Global script variables */ struct Global { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Global"; } std::string mId; Variant mValue; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; bool operator== (const Global& left, const Global& right); } #endif openmw-openmw-0.38.0/components/esm/loadgmst.cpp000066400000000000000000000021021264522266000217010ustar00rootroot00000000000000#include "loadgmst.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int GameSetting::sRecordId = REC_GMST; void GameSetting::load (ESMReader &esm, bool &isDeleted) { isDeleted = false; // GameSetting record can't be deleted now (may be changed in the future) mId = esm.getHNString("NAME"); mValue.read (esm, ESM::Variant::Format_Gmst); } void GameSetting::save (ESMWriter &esm, bool /*isDeleted*/) const { esm.writeHNCString("NAME", mId); mValue.write (esm, ESM::Variant::Format_Gmst); } int GameSetting::getInt() const { return mValue.getInteger(); } float GameSetting::getFloat() const { return mValue.getFloat(); } std::string GameSetting::getString() const { return mValue.getString(); } void GameSetting::blank() { mValue.setType (ESM::VT_None); } bool operator== (const GameSetting& left, const GameSetting& right) { return left.mValue==right.mValue; } } openmw-openmw-0.38.0/components/esm/loadgmst.hpp000066400000000000000000000021171264522266000217140ustar00rootroot00000000000000#ifndef OPENMW_ESM_GMST_H #define OPENMW_ESM_GMST_H #include #include "variant.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Game setting * */ struct GameSetting { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "GameSetting"; } std::string mId; Variant mValue; void load(ESMReader &esm, bool &isDeleted); /// \todo remove the get* functions (redundant, since mValue has equivalent functions now). int getInt() const; ///< Throws an exception if GMST is not of type int or float. float getFloat() const; ///< Throws an exception if GMST is not of type int or float. std::string getString() const; ///< Throwns an exception if GMST is not of type string. void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; bool operator== (const GameSetting& left, const GameSetting& right); } #endif openmw-openmw-0.38.0/components/esm/loadinfo.cpp000066400000000000000000000123051264522266000216700ustar00rootroot00000000000000#include "loadinfo.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int DialInfo::sRecordId = REC_INFO; void DialInfo::load(ESMReader &esm, bool &isDeleted) { loadId(esm); loadData(esm, isDeleted); } void DialInfo::loadId(ESMReader &esm) { mId = esm.getHNString("INAM"); } void DialInfo::loadData(ESMReader &esm, bool &isDeleted) { isDeleted = false; mQuestStatus = QS_None; mFactionLess = false; mPrev = esm.getHNString("PNAM"); mNext = esm.getHNString("NNAM"); // Since there's no way to mark selects as "deleted", we have to clear the SelectStructs from all previous loadings mSelects.clear(); while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mData, 12); break; case ESM::FourCC<'O','N','A','M'>::value: mActor = esm.getHString(); break; case ESM::FourCC<'R','N','A','M'>::value: mRace = esm.getHString(); break; case ESM::FourCC<'C','N','A','M'>::value: mClass = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: { mFaction = esm.getHString(); if (mFaction == "FFFF") { mFactionLess = true; } break; } case ESM::FourCC<'A','N','A','M'>::value: mCell = esm.getHString(); break; case ESM::FourCC<'D','N','A','M'>::value: mPcFaction = esm.getHString(); break; case ESM::FourCC<'S','N','A','M'>::value: mSound = esm.getHString(); break; case ESM::SREC_NAME: mResponse = esm.getHString(); break; case ESM::FourCC<'S','C','V','R'>::value: { SelectStruct ss; ss.mSelectRule = esm.getHString(); ss.mValue.read(esm, Variant::Format_Info); mSelects.push_back(ss); break; } case ESM::FourCC<'B','N','A','M'>::value: mResultScript = esm.getHString(); break; case ESM::FourCC<'Q','S','T','N'>::value: mQuestStatus = QS_Name; esm.skipRecord(); break; case ESM::FourCC<'Q','S','T','F'>::value: mQuestStatus = QS_Finished; esm.skipRecord(); break; case ESM::FourCC<'Q','S','T','R'>::value: mQuestStatus = QS_Restart; esm.skipRecord(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } } void DialInfo::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("INAM", mId); esm.writeHNCString("PNAM", mPrev); esm.writeHNCString("NNAM", mNext); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNT("DATA", mData, 12); esm.writeHNOCString("ONAM", mActor); esm.writeHNOCString("RNAM", mRace); esm.writeHNOCString("CNAM", mClass); esm.writeHNOCString("FNAM", mFaction); esm.writeHNOCString("ANAM", mCell); esm.writeHNOCString("DNAM", mPcFaction); esm.writeHNOCString("SNAM", mSound); esm.writeHNOString("NAME", mResponse); for (std::vector::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it) { esm.writeHNString("SCVR", it->mSelectRule); it->mValue.write (esm, Variant::Format_Info); } esm.writeHNOString("BNAM", mResultScript); switch(mQuestStatus) { case QS_Name: esm.writeHNT("QSTN",'\1'); break; case QS_Finished: esm.writeHNT("QSTF", '\1'); break; case QS_Restart: esm.writeHNT("QSTR", '\1'); break; default: break; } } void DialInfo::blank() { mData.mUnknown1 = 0; mData.mDisposition = 0; mData.mRank = 0; mData.mGender = 0; mData.mPCrank = 0; mData.mUnknown2 = 0; mSelects.clear(); mPrev.clear(); mNext.clear(); mActor.clear(); mRace.clear(); mClass.clear(); mFaction.clear(); mPcFaction.clear(); mCell.clear(); mSound.clear(); mResponse.clear(); mResultScript.clear(); mFactionLess = false; mQuestStatus = QS_None; } } openmw-openmw-0.38.0/components/esm/loadinfo.hpp000066400000000000000000000061011264522266000216720ustar00rootroot00000000000000#ifndef OPENMW_ESM_INFO_H #define OPENMW_ESM_INFO_H #include #include #include "defs.hpp" #include "variant.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Dialogue information. A series of these follow after DIAL records, * and form a linked list of dialogue items. */ struct DialInfo { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "DialInfo"; } enum Gender { Male = 0, Female = 1, NA = -1 }; struct DATAstruct { int mUnknown1; union { int mDisposition; // Used for dialogue responses int mJournalIndex; // Used for journal entries }; signed char mRank; // Rank of NPC signed char mGender; // See Gender enum signed char mPCrank; // Player rank signed char mUnknown2; }; // 12 bytes DATAstruct mData; // The rules for whether or not we will select this dialog item. struct SelectStruct { std::string mSelectRule; // This has a complicated format Variant mValue; }; // Journal quest indices (introduced with the quest system in Tribunal) enum QuestStatus { QS_None = 0, QS_Name = 1, QS_Finished = 2, QS_Restart = 3 }; // Rules for when to include this item in the final list of options // visible to the player. std::vector mSelects; // Id of this, previous and next INFO items std::string mId, mPrev, mNext; // Various references used in determining when to select this item. std::string mActor, mRace, mClass, mFaction, mPcFaction, mCell; // Sound and text associated with this item std::string mSound, mResponse; // Result script (uncompiled) to run whenever this dialog item is // selected std::string mResultScript; // ONLY include this item the NPC is not part of any faction. bool mFactionLess; // Status of this quest item QuestStatus mQuestStatus; // Hexadecimal versions of the various subrecord names. enum SubNames { REC_ONAM = 0x4d414e4f, REC_RNAM = 0x4d414e52, REC_CNAM = 0x4d414e43, REC_FNAM = 0x4d414e46, REC_ANAM = 0x4d414e41, REC_DNAM = 0x4d414e44, REC_SNAM = 0x4d414e53, REC_NAME = 0x454d414e, REC_SCVR = 0x52564353, REC_BNAM = 0x4d414e42, REC_QSTN = 0x4e545351, REC_QSTF = 0x46545351, REC_QSTR = 0x52545351, REC_DELE = 0x454c4544 }; void load(ESMReader &esm, bool &isDeleted); ///< Loads all sub-records of Info record void loadId(ESMReader &esm); ///< Loads only Id of Info record (INAM sub-record) void loadData(ESMReader &esm, bool &isDeleted); ///< Loads all sub-records of Info record, except INAM sub-record void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadingr.cpp000066400000000000000000000062531264522266000217010ustar00rootroot00000000000000#include "loadingr.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Ingredient::sRecordId = REC_INGR; void Ingredient::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'I','R','D','T'>::value: esm.getHT(mData, 56); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing IRDT subrecord"); // horrible hack to fix broken data in records for (int i=0; i<4; ++i) { if (mData.mEffectID[i] != 85 && mData.mEffectID[i] != 22 && mData.mEffectID[i] != 17 && mData.mEffectID[i] != 79 && mData.mEffectID[i] != 74) { mData.mAttributes[i] = -1; } // is this relevant in cycle from 0 to 4? if (mData.mEffectID[i] != 89 && mData.mEffectID[i] != 26 && mData.mEffectID[i] != 21 && mData.mEffectID[i] != 83 && mData.mEffectID[i] != 78) { mData.mSkills[i] = -1; } } } void Ingredient::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("IRDT", mData, 56); esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); } void Ingredient::blank() { mData.mWeight = 0; mData.mValue = 0; for (int i=0; i<4; ++i) { mData.mEffectID[i] = 0; mData.mSkills[i] = 0; mData.mAttributes[i] = 0; } mName.clear(); mModel.clear(); mIcon.clear(); mScript.clear(); } } openmw-openmw-0.38.0/components/esm/loadingr.hpp000066400000000000000000000016111264522266000216770ustar00rootroot00000000000000#ifndef OPENMW_ESM_INGR_H #define OPENMW_ESM_INGR_H #include namespace ESM { class ESMReader; class ESMWriter; /* * Alchemy ingredient */ struct Ingredient { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Ingredient"; } struct IRDTstruct { float mWeight; int mValue; int mEffectID[4]; // Effect, 0 or -1 means none int mSkills[4]; // SkillEnum related to effect int mAttributes[4]; // Attribute related to effect }; IRDTstruct mData; std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadland.cpp000066400000000000000000000222541264522266000216570ustar00rootroot00000000000000#include "loadland.hpp" #include #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Land::sRecordId = REC_LAND; void Land::LandData::save(ESMWriter &esm) const { if (mDataTypes & Land::DATA_VNML) { esm.writeHNT("VNML", mNormals, sizeof(mNormals)); } if (mDataTypes & Land::DATA_VHGT) { VHGT offsets; offsets.mHeightOffset = mHeights[0] / HEIGHT_SCALE; offsets.mUnk1 = mUnk1; offsets.mUnk2 = mUnk2; float prevY = mHeights[0]; int number = 0; // avoid multiplication for (int i = 0; i < LAND_SIZE; ++i) { float diff = (mHeights[number] - prevY) / HEIGHT_SCALE; offsets.mHeightData[number] = (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5); float prevX = prevY = mHeights[number]; ++number; for (int j = 1; j < LAND_SIZE; ++j) { diff = (mHeights[number] - prevX) / HEIGHT_SCALE; offsets.mHeightData[number] = (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5); prevX = mHeights[number]; ++number; } } esm.writeHNT("VHGT", offsets, sizeof(VHGT)); } if (mDataTypes & Land::DATA_WNAM) { esm.writeHNT("WNAM", mWnam, 81); } if (mDataTypes & Land::DATA_VCLR) { esm.writeHNT("VCLR", mColours, 3*LAND_NUM_VERTS); } if (mDataTypes & Land::DATA_VTEX) { static uint16_t vtex[LAND_NUM_TEXTURES]; transposeTextureData(mTextures, vtex); esm.writeHNT("VTEX", vtex, sizeof(vtex)); } } Land::Land() : mFlags(0) , mX(0) , mY(0) , mPlugin(0) , mEsm(NULL) , mDataTypes(0) , mDataLoaded(false) , mLandData(NULL) { } void Land::LandData::transposeTextureData(const uint16_t *in, uint16_t *out) { int readPos = 0; //bit ugly, but it works for ( int y1 = 0; y1 < 4; y1++ ) for ( int x1 = 0; x1 < 4; x1++ ) for ( int y2 = 0; y2 < 4; y2++) for ( int x2 = 0; x2 < 4; x2++ ) out[(y1*4+y2)*16+(x1*4+x2)] = in[readPos++]; } Land::~Land() { delete mLandData; } void Land::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mEsm = &esm; mPlugin = mEsm->getIndex(); bool hasLocation = false; bool isLoaded = false; while (!isLoaded && esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'I','N','T','V'>::value: esm.getSubHeaderIs(8); esm.getT(mX); esm.getT(mY); hasLocation = true; break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mFlags); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.cacheSubName(); isLoaded = true; break; } } if (!hasLocation) esm.fail("Missing INTV subrecord"); mContext = esm.getContext(); // Skip the land data here. Load it when the cell is loaded. while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'V','N','M','L'>::value: esm.skipHSub(); mDataTypes |= DATA_VNML; break; case ESM::FourCC<'V','H','G','T'>::value: esm.skipHSub(); mDataTypes |= DATA_VHGT; break; case ESM::FourCC<'W','N','A','M'>::value: esm.skipHSub(); mDataTypes |= DATA_WNAM; break; case ESM::FourCC<'V','C','L','R'>::value: esm.skipHSub(); mDataTypes |= DATA_VCLR; break; case ESM::FourCC<'V','T','E','X'>::value: esm.skipHSub(); mDataTypes |= DATA_VTEX; break; default: esm.fail("Unknown subrecord"); break; } } mDataLoaded = 0; mLandData = NULL; } void Land::save(ESMWriter &esm, bool isDeleted) const { esm.startSubRecord("INTV"); esm.writeT(mX); esm.writeT(mY); esm.endRecord("INTV"); esm.writeHNT("DATA", mFlags); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } if (mLandData) { mLandData->save(esm); } } void Land::loadData(int flags) const { // Try to load only available data flags = flags & mDataTypes; // Return if all required data is loaded if ((mDataLoaded & flags) == flags) { return; } // Create storage if nothing is loaded if (mLandData == NULL) { mLandData = new LandData; mLandData->mDataTypes = mDataTypes; } mEsm->restoreContext(mContext); if (mEsm->isNextSub("VNML")) { condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); } if (mEsm->isNextSub("VHGT")) { static VHGT vhgt; if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) { float rowOffset = vhgt.mHeightOffset; for (int y = 0; y < LAND_SIZE; y++) { rowOffset += vhgt.mHeightData[y * LAND_SIZE]; mLandData->mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE; float colOffset = rowOffset; for (int x = 1; x < LAND_SIZE; x++) { colOffset += vhgt.mHeightData[y * LAND_SIZE + x]; mLandData->mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE; } } mLandData->mUnk1 = vhgt.mUnk1; mLandData->mUnk2 = vhgt.mUnk2; } } if (mEsm->isNextSub("WNAM")) { condLoad(flags, DATA_WNAM, mLandData->mWnam, 81); } if (mEsm->isNextSub("VCLR")) condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); if (mEsm->isNextSub("VTEX")) { static uint16_t vtex[LAND_NUM_TEXTURES]; if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) { LandData::transposeTextureData(vtex, mLandData->mTextures); } } } void Land::unloadData() { if (mDataLoaded) { delete mLandData; mLandData = NULL; mDataLoaded = 0; } } bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const { if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) { mEsm->getHExact(ptr, size); mDataLoaded |= dataFlag; return true; } mEsm->skipHSubSize(size); return false; } bool Land::isDataLoaded(int flags) const { return (mDataLoaded & flags) == (flags & mDataTypes); } Land::Land (const Land& land) : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), mEsm (land.mEsm), mContext (land.mContext), mDataTypes (land.mDataTypes), mDataLoaded (land.mDataLoaded), mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) {} Land& Land::operator= (Land land) { swap (land); return *this; } void Land::swap (Land& land) { std::swap (mFlags, land.mFlags); std::swap (mX, land.mX); std::swap (mY, land.mY); std::swap (mPlugin, land.mPlugin); std::swap (mEsm, land.mEsm); std::swap (mContext, land.mContext); std::swap (mDataTypes, land.mDataTypes); std::swap (mDataLoaded, land.mDataLoaded); std::swap (mLandData, land.mLandData); } const Land::LandData *Land::getLandData (int flags) const { if (!(flags & mDataTypes)) return 0; loadData (flags); return mLandData; } const Land::LandData *Land::getLandData() const { return mLandData; } Land::LandData *Land::getLandData() { return mLandData; } void Land::add (int flags) { if (!mLandData) mLandData = new LandData; mDataTypes |= flags; mDataLoaded |= flags; } void Land::remove (int flags) { mDataTypes &= ~flags; mDataLoaded &= ~flags; if (!mDataLoaded) { delete mLandData; mLandData = 0; } } } openmw-openmw-0.38.0/components/esm/loadland.hpp000066400000000000000000000107601264522266000216630ustar00rootroot00000000000000#ifndef OPENMW_ESM_LAND_H #define OPENMW_ESM_LAND_H #include #include "esmcommon.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Landscape data. */ struct Land { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Land"; } Land(); ~Land(); int mFlags; // Only first four bits seem to be used, don't know what // they mean. int mX, mY; // Map coordinates. int mPlugin; // Plugin index, used to reference the correct material palette. // File context. This allows the ESM reader to be 'reset' to this // location later when we are ready to load the full data set. ESMReader* mEsm; ESM_Context mContext; int mDataTypes; enum { DATA_VNML = 1, DATA_VHGT = 2, DATA_WNAM = 4, DATA_VCLR = 8, DATA_VTEX = 16 }; // number of vertices per side static const int LAND_SIZE = 65; // cell terrain size in world coords static const int REAL_SIZE = 8192; // total number of vertices static const int LAND_NUM_VERTS = LAND_SIZE * LAND_SIZE; static const int HEIGHT_SCALE = 8; //number of textures per side of land static const int LAND_TEXTURE_SIZE = 16; //total number of textures per land static const int LAND_NUM_TEXTURES = LAND_TEXTURE_SIZE * LAND_TEXTURE_SIZE; #pragma pack(push,1) struct VHGT { float mHeightOffset; int8_t mHeightData[LAND_NUM_VERTS]; short mUnk1; char mUnk2; }; #pragma pack(pop) typedef signed char VNML; struct LandData { // Initial reference height for the first vertex, only needed for filling mHeights float mHeightOffset; // Height in world space for each vertex float mHeights[LAND_NUM_VERTS]; // 24-bit normals, these aren't always correct though. Edge and corner normals may be garbage. VNML mNormals[LAND_NUM_VERTS * 3]; // 2D array of texture indices. An index can be used to look up an ESM::LandTexture, // but to do so you must subtract 1 from the index first! // An index of 0 indicates the default texture. uint16_t mTextures[LAND_NUM_TEXTURES]; // 24-bit RGB color for each vertex unsigned char mColours[3 * LAND_NUM_VERTS]; // DataTypes available in this LandData, accessing data that is not available is an undefined operation int mDataTypes; // low-LOD heightmap (used for rendering the global map) signed char mWnam[81]; // ??? short mUnk1; uint8_t mUnk2; void save(ESMWriter &esm) const; static void transposeTextureData(const uint16_t *in, uint16_t *out); }; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank() {} /** * Actually loads data */ void loadData(int flags) const; /** * Frees memory allocated for land data */ void unloadData(); /// Check if given data type is loaded /// @note We only check data types that *can* be loaded (present in mDataTypes) bool isDataLoaded(int flags) const; Land (const Land& land); Land& operator= (Land land); void swap (Land& land); /// Return land data with at least the data types specified in \a flags loaded (if they /// are available). Will return a 0-pointer if there is no data for any of the /// specified types. const LandData *getLandData (int flags) const; /// Return land data without loading first anything. Can return a 0-pointer. const LandData *getLandData() const; /// Return land data without loading first anything. Can return a 0-pointer. LandData *getLandData(); /// \attention Must not be called on objects that aren't fully loaded. /// /// \note Added data fields will be uninitialised void add (int flags); /// \attention Must not be called on objects that aren't fully loaded. void remove (int flags); private: /// Loads data and marks it as loaded /// \return true if data is actually loaded from file, false otherwise /// including the case when data is already loaded bool condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const; mutable int mDataLoaded; mutable LandData *mLandData; }; } #endif openmw-openmw-0.38.0/components/esm/loadlevlist.cpp000066400000000000000000000065631264522266000224300ustar00rootroot00000000000000#include "loadlevlist.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { void LevelledListBase::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasList = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mFlags); break; case ESM::FourCC<'N','N','A','M'>::value: esm.getHT(mChanceNone); break; case ESM::FourCC<'I','N','D','X'>::value: { int length = 0; esm.getHT(length); mList.resize(length); // If this levelled list was already loaded by a previous content file, // we overwrite the list. Merging lists should probably be left to external tools, // with the limited amount of information there is in the records, all merging methods // will be flawed in some way. For a proper fix the ESM format would have to be changed // to actually track list changes instead of including the whole list for every file // that does something with that list. for (size_t i = 0; i < mList.size(); i++) { LevelItem &li = mList[i]; li.mId = esm.getHNString(mRecName); esm.getHNT(li.mLevel, "INTV"); } hasList = true; break; } case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: { if (!hasList) { // Original engine ignores rest of the record, even if there are items following mList.clear(); esm.skipRecord(); } else { esm.fail("Unknown subrecord"); } break; } } } if (!hasName) esm.fail("Missing NAME subrecord"); } void LevelledListBase::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNT("DATA", mFlags); esm.writeHNT("NNAM", mChanceNone); esm.writeHNT("INDX", mList.size()); for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNCString(mRecName, it->mId); esm.writeHNT("INTV", it->mLevel); } } void LevelledListBase::blank() { mFlags = 0; mChanceNone = 0; mList.clear(); } unsigned int CreatureLevList::sRecordId = REC_LEVC; unsigned int ItemLevList::sRecordId = REC_LEVI; } openmw-openmw-0.38.0/components/esm/loadlevlist.hpp000066400000000000000000000043411264522266000224250ustar00rootroot00000000000000#ifndef OPENMW_ESM_LEVLISTS_H #define OPENMW_ESM_LEVLISTS_H #include #include namespace ESM { class ESMReader; class ESMWriter; /* * Levelled lists. Since these have identical layout, I only bothered * to implement it once. * * We should later implement the ability to merge levelled lists from * several files. */ struct LevelledListBase { int mFlags; unsigned char mChanceNone; // Chance that none are selected (0-100) std::string mId; // Record name used to read references. Must be set before load() is // called. const char *mRecName; struct LevelItem { std::string mId; short mLevel; }; std::vector mList; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; struct CreatureLevList: LevelledListBase { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "CreatureLevList"; } enum Flags { AllLevels = 0x01 // Calculate from all levels <= player // level, not just the closest below // player. }; CreatureLevList() { mRecName = "CNAM"; } }; struct ItemLevList: LevelledListBase { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "ItemLevList"; } enum Flags { Each = 0x01, // Select a new item each time this // list is instantiated, instead of // giving several identical items // (used when a container has more // than one instance of one levelled // list.) AllLevels = 0x02 // Calculate from all levels <= player // level, not just the closest below // player. }; ItemLevList() { mRecName = "INAM"; } }; } #endif openmw-openmw-0.38.0/components/esm/loadligh.cpp000066400000000000000000000051051264522266000216600ustar00rootroot00000000000000#include "loadligh.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Light::sRecordId = REC_LIGH; void Light::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::FourCC<'L','H','D','T'>::value: esm.getHT(mData, 24); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'S','N','A','M'>::value: mSound = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing LHDT subrecord"); } void Light::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("ITEX", mIcon); esm.writeHNT("LHDT", mData, 24); esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("SNAM", mSound); } void Light::blank() { mData.mWeight = 0; mData.mValue = 0; mData.mTime = 0; mData.mRadius = 0; mData.mColor = 0; mData.mFlags = 0; mSound.clear(); mScript.clear(); mModel.clear(); mIcon.clear(); mName.clear(); } } openmw-openmw-0.38.0/components/esm/loadligh.hpp000066400000000000000000000025351264522266000216710ustar00rootroot00000000000000#ifndef OPENMW_ESM_LIGH_H #define OPENMW_ESM_LIGH_H #include namespace ESM { class ESMReader; class ESMWriter; /* * Lights. Includes static light sources and also carryable candles * and torches. */ struct Light { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Light"; } enum Flags { Dynamic = 0x001, Carry = 0x002, // Can be carried Negative = 0x004, // Negative light - i.e. darkness Flicker = 0x008, Fire = 0x010, OffDefault = 0x020, // Off by default - does not burn while placed in a cell, but can burn when equipped by an NPC FlickerSlow = 0x040, Pulse = 0x080, PulseSlow = 0x100 }; struct LHDTstruct { float mWeight; int mValue; int mTime; // Duration int mRadius; unsigned int mColor; // 4-byte rgba value int mFlags; }; // Size = 24 bytes LHDTstruct mData; std::string mSound, mScript, mModel, mIcon, mName, mId; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadlock.cpp000066400000000000000000000045261264522266000216730ustar00rootroot00000000000000#include "loadlock.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Lockpick::sRecordId = REC_LOCK; void Lockpick::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'L','K','D','T'>::value: esm.getHT(mData, 16); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing LKDT subrecord"); } void Lockpick::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("LKDT", mData, 16); esm.writeHNOString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); } void Lockpick::blank() { mData.mWeight = 0; mData.mValue = 0; mData.mQuality = 0; mData.mUses = 0; mName.clear(); mModel.clear(); mIcon.clear(); mScript.clear(); } } openmw-openmw-0.38.0/components/esm/loadlock.hpp000066400000000000000000000013531264522266000216730ustar00rootroot00000000000000#ifndef OPENMW_ESM_LOCK_H #define OPENMW_ESM_LOCK_H #include namespace ESM { class ESMReader; class ESMWriter; struct Lockpick { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Lockpick"; } struct Data { float mWeight; int mValue; float mQuality; int mUses; }; // Size = 16 Data mData; std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadltex.cpp000066400000000000000000000032241264522266000217110ustar00rootroot00000000000000#include "loadltex.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int LandTexture::sRecordId = REC_LTEX; void LandTexture::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasIndex = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'I','N','T','V'>::value: esm.getHT(mIndex); hasIndex = true; break; case ESM::FourCC<'D','A','T','A'>::value: mTexture = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasIndex) esm.fail("Missing INTV subrecord"); } void LandTexture::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); esm.writeHNT("INTV", mIndex); esm.writeHNCString("DATA", mTexture); if (isDeleted) { esm.writeHNCString("DELE", ""); } } void LandTexture::blank() { mTexture.clear(); mIndex = -1; } } openmw-openmw-0.38.0/components/esm/loadltex.hpp000066400000000000000000000023741264522266000217230ustar00rootroot00000000000000#ifndef OPENMW_ESM_LTEX_H #define OPENMW_ESM_LTEX_H #include namespace ESM { class ESMReader; class ESMWriter; /* * Texture used for texturing landscape. * * They are probably indexed by 'num', not 'id', but I don't know for * sure. And num is not unique between files, so one option is to keep * a separate list for each input file (that has LTEX records, of * course.) We also need to resolve references to already existing * land textures to save space. * I'm not sure if it is even possible to override existing land * textures, probably not. I'll have to try it, and have to mimic the * behaviour of morrowind. First, check what you are allowed to do in * the editor. Then make an esp which changes a commonly used land * texture, and see if it affects the game. */ struct LandTexture { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "LandTexture"; } std::string mId, mTexture; int mIndex; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadmgef.cpp000066400000000000000000000461431264522266000216620ustar00rootroot00000000000000#include "loadmgef.hpp" #include #include #include #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace { static const char *sIds[ESM::MagicEffect::Length] = { "WaterBreathing", "SwiftSwim", "WaterWalking", "Shield", "FireShield", "LightningShield", "FrostShield", "Burden", "Feather", "Jump", "Levitate", "SlowFall", "Lock", "Open", "FireDamage", "ShockDamage", "FrostDamage", "DrainAttribute", "DrainHealth", "DrainMagicka", "DrainFatigue", "DrainSkill", "DamageAttribute", "DamageHealth", "DamageMagicka", "DamageFatigue", "DamageSkill", "Poison", "WeaknessToFire", "WeaknessToFrost", "WeaknessToShock", "WeaknessToMagicka", "WeaknessToCommonDisease", "WeaknessToBlightDisease", "WeaknessToCorprusDisease", "WeaknessToPoison", "WeaknessToNormalWeapons", "DisintegrateWeapon", "DisintegrateArmor", "Invisibility", "Chameleon", "Light", "Sanctuary", "NightEye", "Charm", "Paralyze", "Silence", "Blind", "Sound", "CalmHumanoid", "CalmCreature", "FrenzyHumanoid", "FrenzyCreature", "DemoralizeHumanoid", "DemoralizeCreature", "RallyHumanoid", "RallyCreature", "Dispel", "Soultrap", "Telekinesis", "Mark", "Recall", "DivineIntervention", "AlmsiviIntervention", "DetectAnimal", "DetectEnchantment", "DetectKey", "SpellAbsorption", "Reflect", "CureCommonDisease", "CureBlightDisease", "CureCorprusDisease", "CurePoison", "CureParalyzation", "RestoreAttribute", "RestoreHealth", "RestoreMagicka", "RestoreFatigue", "RestoreSkill", "FortifyAttribute", "FortifyHealth", "FortifyMagicka", "FortifyFatigue", "FortifySkill", "FortifyMaximumMagicka", "AbsorbAttribute", "AbsorbHealth", "AbsorbMagicka", "AbsorbFatigue", "AbsorbSkill", "ResistFire", "ResistFrost", "ResistShock", "ResistMagicka", "ResistCommonDisease", "ResistBlightDisease", "ResistCorprusDisease", "ResistPoison", "ResistNormalWeapons", "ResistParalysis", "RemoveCurse", "TurnUndead", "SummonScamp", "SummonClannfear", "SummonDaedroth", "SummonDremora", "SummonAncestralGhost", "SummonSkeletalMinion", "SummonBonewalker", "SummonGreaterBonewalker", "SummonBonelord", "SummonWingedTwilight", "SummonHunger", "SummonGoldenSaint", "SummonFlameAtronach", "SummonFrostAtronach", "SummonStormAtronach", "FortifyAttack", "CommandCreature", "CommandHumanoid", "BoundDagger", "BoundLongsword", "BoundMace", "BoundBattleAxe", "BoundSpear", "BoundLongbow", "ExtraSpell", "BoundCuirass", "BoundHelm", "BoundBoots", "BoundShield", "BoundGloves", "Corprus", "Vampirism", "SummonCenturionSphere", "SunDamage", "StuntedMagicka", // Tribunal only "SummonFabricant", // Bloodmoon only "SummonWolf", "SummonBear", "SummonBonewolf", "SummonCreature04", "SummonCreature05" }; const int NumberOfHardcodedFlags = 143; const int HardcodedFlags[NumberOfHardcodedFlags] = { 0x11c8, 0x11c0, 0x11c8, 0x11e0, 0x11e0, 0x11e0, 0x11e0, 0x11d0, 0x11c0, 0x11c0, 0x11e0, 0x11c0, 0x11184, 0x11184, 0x1f0, 0x1f0, 0x1f0, 0x11d2, 0x11f0, 0x11d0, 0x11d0, 0x11d1, 0x1d2, 0x1f0, 0x1d0, 0x1d0, 0x1d1, 0x1f0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x1d0, 0x1d0, 0x11c8, 0x31c0, 0x11c0, 0x11c0, 0x11c0, 0x1180, 0x11d8, 0x11d8, 0x11d0, 0x11d0, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11c4, 0x111b8, 0x1040, 0x104c, 0x104c, 0x104c, 0x104c, 0x1040, 0x1040, 0x1040, 0x11c0, 0x11c0, 0x1cc, 0x1cc, 0x1cc, 0x1cc, 0x1cc, 0x1c2, 0x1c0, 0x1c0, 0x1c0, 0x1c1, 0x11c2, 0x11c0, 0x11c0, 0x11c0, 0x11c1, 0x11c0, 0x21192, 0x20190, 0x20190, 0x20190, 0x21191, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x1c0, 0x11190, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x11c0, 0x1180, 0x1180, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x1188, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x1048, 0x104c, 0x1048, 0x40, 0x11c8, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048 }; } namespace ESM { unsigned int MagicEffect::sRecordId = REC_MGEF; void MagicEffect::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; // MagicEffect record can't be deleted now (may be changed in the future) esm.getHNT(mIndex, "INDX"); mId = indexToId (mIndex); esm.getHNT(mData, "MEDT", 36); if (esm.getFormat() == 0) { // don't allow mods to change fixed flags in the legacy format mData.mFlags &= (AllowSpellmaking | AllowEnchanting | NegativeLight); if (mIndex>=0 && mIndex::value: mIcon = esm.getHString(); break; case ESM::FourCC<'P','T','E','X'>::value: mParticle = esm.getHString(); break; case ESM::FourCC<'B','S','N','D'>::value: mBoltSound = esm.getHString(); break; case ESM::FourCC<'C','S','N','D'>::value: mCastSound = esm.getHString(); break; case ESM::FourCC<'H','S','N','D'>::value: mHitSound = esm.getHString(); break; case ESM::FourCC<'A','S','N','D'>::value: mAreaSound = esm.getHString(); break; case ESM::FourCC<'C','V','F','X'>::value: mCasting = esm.getHString(); break; case ESM::FourCC<'B','V','F','X'>::value: mBolt = esm.getHString(); break; case ESM::FourCC<'H','V','F','X'>::value: mHit = esm.getHString(); break; case ESM::FourCC<'A','V','F','X'>::value: mArea = esm.getHString(); break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; default: esm.fail("Unknown subrecord"); } } } void MagicEffect::save(ESMWriter &esm, bool /*isDeleted*/) const { esm.writeHNT("INDX", mIndex); esm.writeHNT("MEDT", mData, 36); esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("PTEX", mParticle); esm.writeHNOCString("BSND", mBoltSound); esm.writeHNOCString("CSND", mCastSound); esm.writeHNOCString("HSND", mHitSound); esm.writeHNOCString("ASND", mAreaSound); esm.writeHNOCString("CVFX", mCasting); esm.writeHNOCString("BVFX", mBolt); esm.writeHNOCString("HVFX", mHit); esm.writeHNOCString("AVFX", mArea); esm.writeHNOString("DESC", mDescription); } short MagicEffect::getResistanceEffect(short effect) { // Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute // std::map effects; effects[DisintegrateArmor] = Sanctuary; effects[DisintegrateWeapon] = Sanctuary; for (int i=0; i<5; ++i) effects[DrainAttribute+i] = ResistMagicka; for (int i=0; i<5; ++i) effects[DamageAttribute+i] = ResistMagicka; for (int i=0; i<5; ++i) effects[AbsorbAttribute+i] = ResistMagicka; for (int i=0; i<10; ++i) effects[WeaknessToFire+i] = ResistMagicka; effects[Burden] = ResistMagicka; effects[Charm] = ResistMagicka; effects[Silence] = ResistMagicka; effects[Blind] = ResistMagicka; effects[Sound] = ResistMagicka; for (int i=0; i<2; ++i) { effects[CalmHumanoid] = ResistMagicka; effects[FrenzyHumanoid] = ResistMagicka; effects[DemoralizeHumanoid] = ResistMagicka; effects[RallyHumanoid] = ResistMagicka; } effects[TurnUndead] = ResistMagicka; effects[FireDamage] = ResistFire; effects[FrostDamage] = ResistFrost; effects[ShockDamage] = ResistShock; effects[Vampirism] = ResistCommonDisease; effects[Corprus] = ResistCorprusDisease; effects[Poison] = ResistPoison; effects[Paralyze] = ResistParalysis; if (effects.find(effect) != effects.end()) return effects[effect]; else return -1; } short MagicEffect::getWeaknessEffect(short effect) { std::map effects; for (int i=0; i<5; ++i) effects[DrainAttribute+i] = WeaknessToMagicka; for (int i=0; i<5; ++i) effects[DamageAttribute+i] = WeaknessToMagicka; for (int i=0; i<5; ++i) effects[AbsorbAttribute+i] = WeaknessToMagicka; for (int i=0; i<10; ++i) effects[WeaknessToFire+i] = WeaknessToMagicka; effects[Burden] = WeaknessToMagicka; effects[Charm] = WeaknessToMagicka; effects[Silence] = WeaknessToMagicka; effects[Blind] = WeaknessToMagicka; effects[Sound] = WeaknessToMagicka; for (int i=0; i<2; ++i) { effects[CalmHumanoid] = WeaknessToMagicka; effects[FrenzyHumanoid] = WeaknessToMagicka; effects[DemoralizeHumanoid] = WeaknessToMagicka; effects[RallyHumanoid] = WeaknessToMagicka; } effects[TurnUndead] = WeaknessToMagicka; effects[FireDamage] = WeaknessToFire; effects[FrostDamage] = WeaknessToFrost; effects[ShockDamage] = WeaknessToShock; effects[Vampirism] = WeaknessToCommonDisease; effects[Corprus] = WeaknessToCorprusDisease; effects[Poison] = WeaknessToPoison; // Weakness to magicka or -1 ? effects[Paralyze] = WeaknessToMagicka; if (effects.find(effect) != effects.end()) return effects[effect]; else return -1; } static std::map genNameMap() { // Map effect ID to GMST name // http://www.uesp.net/morrow/hints/mweffects.shtml std::map names; names[85] ="sEffectAbsorbAttribute"; names[88] ="sEffectAbsorbFatigue"; names[86] ="sEffectAbsorbHealth"; names[87] ="sEffectAbsorbSpellPoints"; names[89] ="sEffectAbsorbSkill"; names[63] ="sEffectAlmsiviIntervention"; names[47] ="sEffectBlind"; names[123] ="sEffectBoundBattleAxe"; names[129] ="sEffectBoundBoots"; names[127] ="sEffectBoundCuirass"; names[120] ="sEffectBoundDagger"; names[131] ="sEffectBoundGloves"; names[128] ="sEffectBoundHelm"; names[125] ="sEffectBoundLongbow"; names[121] ="sEffectBoundLongsword"; names[122] ="sEffectBoundMace"; names[130] ="sEffectBoundShield"; names[124] ="sEffectBoundSpear"; names[7] ="sEffectBurden"; names[50] ="sEffectCalmCreature"; names[49] ="sEffectCalmHumanoid"; names[40] ="sEffectChameleon"; names[44] ="sEffectCharm"; names[118] ="sEffectCommandCreatures"; names[119] ="sEffectCommandHumanoids"; names[132] ="sEffectCorpus"; // NB this typo. (bethesda made it) names[70] ="sEffectCureBlightDisease"; names[69] ="sEffectCureCommonDisease"; names[71] ="sEffectCureCorprusDisease"; names[73] ="sEffectCureParalyzation"; names[72] ="sEffectCurePoison"; names[22] ="sEffectDamageAttribute"; names[25] ="sEffectDamageFatigue"; names[23] ="sEffectDamageHealth"; names[24] ="sEffectDamageMagicka"; names[26] ="sEffectDamageSkill"; names[54] ="sEffectDemoralizeCreature"; names[53] ="sEffectDemoralizeHumanoid"; names[64] ="sEffectDetectAnimal"; names[65] ="sEffectDetectEnchantment"; names[66] ="sEffectDetectKey"; names[38] ="sEffectDisintegrateArmor"; names[37] ="sEffectDisintegrateWeapon"; names[57] ="sEffectDispel"; names[62] ="sEffectDivineIntervention"; names[17] ="sEffectDrainAttribute"; names[20] ="sEffectDrainFatigue"; names[18] ="sEffectDrainHealth"; names[19] ="sEffectDrainSpellpoints"; names[21] ="sEffectDrainSkill"; names[8] ="sEffectFeather"; names[14] ="sEffectFireDamage"; names[4] ="sEffectFireShield"; names[117] ="sEffectFortifyAttackBonus"; names[79] ="sEffectFortifyAttribute"; names[82] ="sEffectFortifyFatigue"; names[80] ="sEffectFortifyHealth"; names[81] ="sEffectFortifySpellpoints"; names[84] ="sEffectFortifyMagickaMultiplier"; names[83] ="sEffectFortifySkill"; names[52] ="sEffectFrenzyCreature"; names[51] ="sEffectFrenzyHumanoid"; names[16] ="sEffectFrostDamage"; names[6] ="sEffectFrostShield"; names[39] ="sEffectInvisibility"; names[9] ="sEffectJump"; names[10] ="sEffectLevitate"; names[41] ="sEffectLight"; names[5] ="sEffectLightningShield"; names[12] ="sEffectLock"; names[60] ="sEffectMark"; names[43] ="sEffectNightEye"; names[13] ="sEffectOpen"; names[45] ="sEffectParalyze"; names[27] ="sEffectPoison"; names[56] ="sEffectRallyCreature"; names[55] ="sEffectRallyHumanoid"; names[61] ="sEffectRecall"; names[68] ="sEffectReflect"; names[100] ="sEffectRemoveCurse"; names[95] ="sEffectResistBlightDisease"; names[94] ="sEffectResistCommonDisease"; names[96] ="sEffectResistCorprusDisease"; names[90] ="sEffectResistFire"; names[91] ="sEffectResistFrost"; names[93] ="sEffectResistMagicka"; names[98] ="sEffectResistNormalWeapons"; names[99] ="sEffectResistParalysis"; names[97] ="sEffectResistPoison"; names[92] ="sEffectResistShock"; names[74] ="sEffectRestoreAttribute"; names[77] ="sEffectRestoreFatigue"; names[75] ="sEffectRestoreHealth"; names[76] ="sEffectRestoreSpellPoints"; names[78] ="sEffectRestoreSkill"; names[42] ="sEffectSanctuary"; names[3] ="sEffectShield"; names[15] ="sEffectShockDamage"; names[46] ="sEffectSilence"; names[11] ="sEffectSlowFall"; names[58] ="sEffectSoultrap"; names[48] ="sEffectSound"; names[67] ="sEffectSpellAbsorption"; names[136] ="sEffectStuntedMagicka"; names[106] ="sEffectSummonAncestralGhost"; names[110] ="sEffectSummonBonelord"; names[108] ="sEffectSummonLeastBonewalker"; names[134] ="sEffectSummonCenturionSphere"; names[103] ="sEffectSummonClannfear"; names[104] ="sEffectSummonDaedroth"; names[105] ="sEffectSummonDremora"; names[114] ="sEffectSummonFlameAtronach"; names[115] ="sEffectSummonFrostAtronach"; names[113] ="sEffectSummonGoldenSaint"; names[109] ="sEffectSummonGreaterBonewalker"; names[112] ="sEffectSummonHunger"; names[102] ="sEffectSummonScamp"; names[107] ="sEffectSummonSkeletalMinion"; names[116] ="sEffectSummonStormAtronach"; names[111] ="sEffectSummonWingedTwilight"; names[135] ="sEffectSunDamage"; names[1] ="sEffectSwiftSwim"; names[59] ="sEffectTelekinesis"; names[101] ="sEffectTurnUndead"; names[133] ="sEffectVampirism"; names[0] ="sEffectWaterBreathing"; names[2] ="sEffectWaterWalking"; names[33] ="sEffectWeaknesstoBlightDisease"; names[32] ="sEffectWeaknesstoCommonDisease"; names[34] ="sEffectWeaknesstoCorprusDisease"; names[28] ="sEffectWeaknesstoFire"; names[29] ="sEffectWeaknesstoFrost"; names[31] ="sEffectWeaknesstoMagicka"; names[36] ="sEffectWeaknesstoNormalWeapons"; names[35] ="sEffectWeaknesstoPoison"; names[30] ="sEffectWeaknesstoShock"; // bloodmoon names[138] ="sEffectSummonCreature01"; names[139] ="sEffectSummonCreature02"; names[140] ="sEffectSummonCreature03"; names[141] ="sEffectSummonCreature04"; names[142] ="sEffectSummonCreature05"; // tribunal names[137] ="sEffectSummonFabricant"; return names; } const std::map MagicEffect::sNames = genNameMap(); const std::string &MagicEffect::effectIdToString(short effectID) { std::map::const_iterator name = sNames.find(effectID); if(name == sNames.end()) throw std::runtime_error(std::string("Unimplemented effect ID ")+boost::lexical_cast(effectID)); return name->second; } class FindSecond { const std::string &mName; public: FindSecond(const std::string &name) : mName(name) { } bool operator()(const std::pair &item) const { if(Misc::StringUtils::ciEqual(item.second, mName)) return true; return false; } }; short MagicEffect::effectStringToId(const std::string &effect) { std::map::const_iterator name; name = std::find_if(sNames.begin(), sNames.end(), FindSecond(effect)); if(name == sNames.end()) throw std::runtime_error(std::string("Unimplemented effect ")+effect); return name->first; } MagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const { if ( mData.mFlags & NoMagnitude ) return MDT_None; if ( mIndex == 84 ) return MDT_TimesInt; if ( mIndex == 59 || ( mIndex >= 64 && mIndex <= 66) ) return MDT_Feet; if ( mIndex == 118 || mIndex == 119 ) return MDT_Level; if ( ( mIndex >= 28 && mIndex <= 36 ) || ( mIndex >= 90 && mIndex <= 99 ) || mIndex == 40 || mIndex == 47 || mIndex == 57 || mIndex == 68 ) return MDT_Percentage; return MDT_Points; } void MagicEffect::blank() { mData.mSchool = 0; mData.mBaseCost = 0; mData.mFlags = 0; mData.mRed = 0; mData.mGreen = 0; mData.mBlue = 0; mData.mSpeed = 0; mIcon.clear(); mParticle.clear(); mCasting.clear(); mHit.clear(); mArea.clear(); mBolt.clear(); mCastSound.clear(); mBoltSound.clear(); mHitSound.clear(); mAreaSound.clear(); mDescription.clear(); } std::string MagicEffect::indexToId (int index) { std::ostringstream stream; if (index!=-1) { stream << "#"; if (index<100) { stream << "0"; if (index<10) stream << "0"; } stream << index; if (index>=0 && index #include namespace ESM { class ESMReader; class ESMWriter; struct MagicEffect { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "MagicEffect"; } std::string mId; enum Flags { // Originally fixed flags (HardcodedFlags array consists of just these) TargetSkill = 0x1, // Affects a specific skill, which is specified elsewhere in the effect structure. TargetAttribute = 0x2, // Affects a specific attribute, which is specified elsewhere in the effect structure. NoDuration = 0x4, // Has no duration. Only runs effect once on cast. NoMagnitude = 0x8, // Has no magnitude. Harmful = 0x10, // Counts as a negative effect. Interpreted as useful for attack, and is treated as a bad effect in alchemy. ContinuousVfx = 0x20, // The effect's hit particle VFX repeats for the full duration of the spell, rather than occuring once on hit. CastSelf = 0x40, // Allows range - cast on self. CastTouch = 0x80, // Allows range - cast on touch. CastTarget = 0x100, // Allows range - cast on target. UncappedDamage = 0x1000, // Negates multiple cap behaviours. Allows an effect to reduce an attribute below zero; removes the normal minimum effect duration of 1 second. NonRecastable = 0x4000, // Does not land if parent spell is already affecting target. Shows "you cannot re-cast" message for self target. Unreflectable = 0x10000, // Cannot be reflected, the effect always lands normally. CasterLinked = 0x20000, // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells. // Originally modifiable flags AllowSpellmaking = 0x200, // Can be used for spellmaking AllowEnchanting = 0x400, // Can be used for enchanting NegativeLight = 0x800 // Negative light source }; enum MagnitudeDisplayType { MDT_None, MDT_Feet, MDT_Level, MDT_Percentage, MDT_Points, MDT_TimesInt }; struct MEDTstruct { int mSchool; // SpellSchool, see defs.hpp float mBaseCost; int mFlags; // Glow color for enchanted items with this effect int mRed, mGreen, mBlue; float mUnknown1; // Called "Size X" in CS float mSpeed; // Speed of fired projectile float mUnknown2; // Called "Size Cap" in CS }; // 36 bytes static const std::map sNames; static const std::string &effectIdToString(short effectID); static short effectStringToId(const std::string &effect); /// Returns the effect that provides resistance against \a effect (or -1 if there's none) static short getResistanceEffect(short effect); /// Returns the effect that induces weakness against \a effect (or -1 if there's none) static short getWeaknessEffect(short effect); MagnitudeDisplayType getMagnitudeDisplayType() const; MEDTstruct mData; std::string mIcon, mParticle; // Textures std::string mCasting, mHit, mArea; // ESM::Static std::string mBolt; // ESM::Weapon std::string mCastSound, mBoltSound, mHitSound, mAreaSound; // Sounds std::string mDescription; // Index of this magical effect. Corresponds to one of the // hard-coded effects in the original engine: // 0-136 in Morrowind // 137 in Tribunal // 138-140 in Bloodmoon (also changes 64?) // 141-142 are summon effects introduced in bloodmoon, but not used // there. They can be redefined in mods by setting the name in GMST // sEffectSummonCreature04/05 creature id in // sMagicCreature04ID/05ID. int mIndex; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; /// Set record to default state (does not touch the ID/index). void blank(); enum Effects { WaterBreathing = 0, SwiftSwim = 1, WaterWalking = 2, Shield = 3, FireShield = 4, LightningShield = 5, FrostShield = 6, Burden = 7, Feather = 8, Jump = 9, Levitate = 10, SlowFall = 11, Lock = 12, Open = 13, FireDamage = 14, ShockDamage = 15, FrostDamage = 16, DrainAttribute = 17, DrainHealth = 18, DrainMagicka = 19, DrainFatigue = 20, DrainSkill = 21, DamageAttribute = 22, DamageHealth = 23, DamageMagicka = 24, DamageFatigue = 25, DamageSkill = 26, Poison = 27, WeaknessToFire = 28, WeaknessToFrost = 29, WeaknessToShock = 30, WeaknessToMagicka = 31, WeaknessToCommonDisease = 32, WeaknessToBlightDisease = 33, WeaknessToCorprusDisease = 34, WeaknessToPoison = 35, WeaknessToNormalWeapons = 36, DisintegrateWeapon = 37, DisintegrateArmor = 38, Invisibility = 39, Chameleon = 40, Light = 41, Sanctuary = 42, NightEye = 43, Charm = 44, Paralyze = 45, Silence = 46, Blind = 47, Sound = 48, CalmHumanoid = 49, CalmCreature = 50, FrenzyHumanoid = 51, FrenzyCreature = 52, DemoralizeHumanoid = 53, DemoralizeCreature = 54, RallyHumanoid = 55, RallyCreature = 56, Dispel = 57, Soultrap = 58, Telekinesis = 59, Mark = 60, Recall = 61, DivineIntervention = 62, AlmsiviIntervention = 63, DetectAnimal = 64, DetectEnchantment = 65, DetectKey = 66, SpellAbsorption = 67, Reflect = 68, CureCommonDisease = 69, CureBlightDisease = 70, CureCorprusDisease = 71, CurePoison = 72, CureParalyzation = 73, RestoreAttribute = 74, RestoreHealth = 75, RestoreMagicka = 76, RestoreFatigue = 77, RestoreSkill = 78, FortifyAttribute = 79, FortifyHealth = 80, FortifyMagicka= 81, FortifyFatigue = 82, FortifySkill = 83, FortifyMaximumMagicka = 84, AbsorbAttribute = 85, AbsorbHealth = 86, AbsorbMagicka = 87, AbsorbFatigue = 88, AbsorbSkill = 89, ResistFire = 90, ResistFrost = 91, ResistShock = 92, ResistMagicka = 93, ResistCommonDisease = 94, ResistBlightDisease = 95, ResistCorprusDisease = 96, ResistPoison = 97, ResistNormalWeapons = 98, ResistParalysis = 99, RemoveCurse = 100, TurnUndead = 101, SummonScamp = 102, SummonClannfear = 103, SummonDaedroth = 104, SummonDremora = 105, SummonAncestralGhost = 106, SummonSkeletalMinion = 107, SummonBonewalker = 108, SummonGreaterBonewalker = 109, SummonBonelord = 110, SummonWingedTwilight = 111, SummonHunger = 112, SummonGoldenSaint = 113, SummonFlameAtronach = 114, SummonFrostAtronach = 115, SummonStormAtronach = 116, FortifyAttack = 117, CommandCreature = 118, CommandHumanoid = 119, BoundDagger = 120, BoundLongsword = 121, BoundMace = 122, BoundBattleAxe = 123, BoundSpear = 124, BoundLongbow = 125, ExtraSpell = 126, BoundCuirass = 127, BoundHelm = 128, BoundBoots = 129, BoundShield = 130, BoundGloves = 131, Corprus = 132, Vampirism = 133, SummonCenturionSphere = 134, SunDamage = 135, StuntedMagicka = 136, // Tribunal only SummonFabricant = 137, // Bloodmoon only SummonWolf = 138, SummonBear = 139, SummonBonewolf = 140, SummonCreature04 = 141, SummonCreature05 = 142, Length }; static std::string indexToId (int index); }; } #endif openmw-openmw-0.38.0/components/esm/loadmisc.cpp000066400000000000000000000045171264522266000216760ustar00rootroot00000000000000#include "loadmisc.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Miscellaneous::sRecordId = REC_MISC; void Miscellaneous::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'M','C','D','T'>::value: esm.getHT(mData, 12); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing MCDT subrecord"); } void Miscellaneous::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("MCDT", mData, 12); esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); } void Miscellaneous::blank() { mData.mWeight = 0; mData.mValue = 0; mData.mIsKey = 0; mName.clear(); mModel.clear(); mIcon.clear(); mScript.clear(); } } openmw-openmw-0.38.0/components/esm/loadmisc.hpp000066400000000000000000000020031264522266000216670ustar00rootroot00000000000000#ifndef OPENMW_ESM_MISC_H #define OPENMW_ESM_MISC_H #include namespace ESM { class ESMReader; class ESMWriter; /* * Misc inventory items, basically things that have no use but can be * carried, bought and sold. It also includes keys. */ struct Miscellaneous { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Miscellaneous"; } struct MCDTstruct { float mWeight; int mValue; int mIsKey; // There are many keys in Morrowind.esm that has this // set to 0. TODO: Check what this field corresponds to // in the editor. }; MCDTstruct mData; std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadnpc.cpp000066400000000000000000000156741264522266000215310ustar00rootroot00000000000000#include "loadnpc.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int NPC::sRecordId = REC_NPC_; void NPC::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mPersistent = (esm.getRecordFlags() & 0x0400) != 0; mSpells.mList.clear(); mInventory.mList.clear(); mTransport.mList.clear(); mAiPackage.mList.clear(); mHasAI = false; bool hasName = false; bool hasNpdt = false; bool hasFlags = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'R','N','A','M'>::value: mRace = esm.getHString(); break; case ESM::FourCC<'C','N','A','M'>::value: mClass = esm.getHString(); break; case ESM::FourCC<'A','N','A','M'>::value: mFaction = esm.getHString(); break; case ESM::FourCC<'B','N','A','M'>::value: mHead = esm.getHString(); break; case ESM::FourCC<'K','N','A','M'>::value: mHair = esm.getHString(); break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'N','P','D','T'>::value: hasNpdt = true; esm.getSubHeader(); if (esm.getSubSize() == 52) { mNpdtType = NPC_DEFAULT; esm.getExact(&mNpdt52, 52); } else if (esm.getSubSize() == 12) { mNpdtType = NPC_WITH_AUTOCALCULATED_STATS; esm.getExact(&mNpdt12, 12); } else esm.fail("NPC_NPDT must be 12 or 52 bytes long"); break; case ESM::FourCC<'F','L','A','G'>::value: hasFlags = true; esm.getHT(mFlags); break; case ESM::FourCC<'N','P','C','S'>::value: mSpells.add(esm); break; case ESM::FourCC<'N','P','C','O'>::value: mInventory.add(esm); break; case ESM::FourCC<'A','I','D','T'>::value: esm.getHExact(&mAiData, sizeof(mAiData)); mHasAI= true; break; case ESM::FourCC<'D','O','D','T'>::value: case ESM::FourCC<'D','N','A','M'>::value: mTransport.add(esm); break; case AI_Wander: case AI_Activate: case AI_Escort: case AI_Follow: case AI_Travel: case AI_CNDT: mAiPackage.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasNpdt && !isDeleted) esm.fail("Missing NPDT subrecord"); if (!hasFlags && !isDeleted) esm.fail("Missing FLAG subrecord"); } void NPC::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNOCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNCString("RNAM", mRace); esm.writeHNCString("CNAM", mClass); esm.writeHNCString("ANAM", mFaction); esm.writeHNCString("BNAM", mHead); esm.writeHNCString("KNAM", mHair); esm.writeHNOCString("SCRI", mScript); if (mNpdtType == NPC_DEFAULT) esm.writeHNT("NPDT", mNpdt52, 52); else if (mNpdtType == NPC_WITH_AUTOCALCULATED_STATS) esm.writeHNT("NPDT", mNpdt12, 12); esm.writeHNT("FLAG", mFlags); mInventory.save(esm); mSpells.save(esm); if (mAiData.mHello != 0 || mAiData.mFight != 0 || mAiData.mFlee != 0 || mAiData.mAlarm != 0 || mAiData.mServices != 0) { esm.writeHNT("AIDT", mAiData, sizeof(mAiData)); } mTransport.save(esm); mAiPackage.save(esm); } bool NPC::isMale() const { return (mFlags & Female) == 0; } void NPC::setIsMale(bool value) { mFlags |= Female; if (value) { mFlags ^= Female; } } void NPC::blank() { mNpdtType = NPC_DEFAULT; mNpdt52.mLevel = 0; mNpdt52.mStrength = mNpdt52.mIntelligence = mNpdt52.mWillpower = mNpdt52.mAgility = mNpdt52.mSpeed = mNpdt52.mEndurance = mNpdt52.mPersonality = mNpdt52.mLuck = 0; for (int i=0; i< Skill::Length; ++i) mNpdt52.mSkills[i] = 0; mNpdt52.mReputation = 0; mNpdt52.mHealth = mNpdt52.mMana = mNpdt52.mFatigue = 0; mNpdt52.mDisposition = 0; mNpdt52.mFactionID = 0; mNpdt52.mRank = 0; mNpdt52.mUnknown = 0; mNpdt52.mGold = 0; mNpdt12.mLevel = 0; mNpdt12.mDisposition = 0; mNpdt12.mReputation = 0; mNpdt12.mRank = 0; mNpdt12.mUnknown1 = 0; mNpdt12.mUnknown2 = 0; mNpdt12.mUnknown3 = 0; mNpdt12.mGold = 0; mFlags = 0; mInventory.mList.clear(); mSpells.mList.clear(); mAiData.blank(); mHasAI = false; mTransport.mList.clear(); mAiPackage.mList.clear(); mName.clear(); mModel.clear(); mRace.clear(); mClass.clear(); mFaction.clear(); mScript.clear(); mHair.clear(); mHead.clear(); } int NPC::getFactionRank() const { if (mFaction.empty()) return -1; else if (mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) return mNpdt12.mRank; else // NPC_DEFAULT return mNpdt52.mRank; } const std::vector& NPC::getTransport() const { return mTransport.mList; } } openmw-openmw-0.38.0/components/esm/loadnpc.hpp000066400000000000000000000062001264522266000215170ustar00rootroot00000000000000#ifndef OPENMW_ESM_NPC_H #define OPENMW_ESM_NPC_H #include #include #include "defs.hpp" #include "loadcont.hpp" #include "aipackage.hpp" #include "spelllist.hpp" #include "loadskil.hpp" #include "transport.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * NPC definition */ struct NPC { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "NPC"; } // Services enum Services { // This merchant buys: Weapon = 0x00001, Armor = 0x00002, Clothing = 0x00004, Books = 0x00008, Ingredients = 0x00010, Picks = 0x00020, Probes = 0x00040, Lights = 0x00080, Apparatus = 0x00100, RepairItem = 0x00200, Misc = 0x00400, Potions = 0x02000, // Other services Spells = 0x00800, MagicItems = 0x01000, Training = 0x04000, // What skills? Spellmaking = 0x08000, Enchanting = 0x10000, Repair = 0x20000 }; enum Flags { Female = 0x0001, Essential = 0x0002, Respawn = 0x0004, Autocalc = 0x0010, Skeleton = 0x0400, // Skeleton blood effect (white) Metal = 0x0800 // Metal blood effect (golden?) }; enum NpcType { NPC_WITH_AUTOCALCULATED_STATS = 12, NPC_DEFAULT = 52 }; #pragma pack(push) #pragma pack(1) struct NPDTstruct52 { short mLevel; unsigned char mStrength, mIntelligence, mWillpower, mAgility, mSpeed, mEndurance, mPersonality, mLuck; // mSkill can grow up to 200, it must be unsigned unsigned char mSkills[Skill::Length]; char mFactionID; unsigned short mHealth, mMana, mFatigue; signed char mDisposition, mReputation, mRank; char mUnknown; int mGold; }; // 52 bytes struct NPDTstruct12 { short mLevel; // see above signed char mDisposition, mReputation, mRank; char mUnknown1, mUnknown2, mUnknown3; int mGold; }; // 12 bytes #pragma pack(pop) unsigned char mNpdtType; NPDTstruct52 mNpdt52; NPDTstruct12 mNpdt12; //for autocalculated characters int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank int mFlags; bool mPersistent; InventoryList mInventory; SpellList mSpells; AIData mAiData; bool mHasAI; Transport mTransport; const std::vector& getTransport() const; AIPackageList mAiPackage; std::string mId, mName, mModel, mRace, mClass, mFaction, mScript; // body parts std::string mHair, mHead; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; bool isMale() const; void setIsMale(bool value); void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadpgrd.cpp000066400000000000000000000125251264522266000216750ustar00rootroot00000000000000#include "loadpgrd.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Pathgrid::sRecordId = REC_PGRD; Pathgrid::Point& Pathgrid::Point::operator=(const float rhs[3]) { mX = static_cast(rhs[0]); mY = static_cast(rhs[1]); mZ = static_cast(rhs[2]); mAutogenerated = 0; mConnectionNum = 0; mUnknown = 0; return *this; } Pathgrid::Point::Point(const float rhs[3]) : mX(static_cast(rhs[0])), mY(static_cast(rhs[1])), mZ(static_cast(rhs[2])), mAutogenerated(0), mConnectionNum(0), mUnknown(0) { } Pathgrid::Point::Point():mX(0),mY(0),mZ(0),mAutogenerated(0), mConnectionNum(0),mUnknown(0) { } void Pathgrid::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mPoints.clear(); mEdges.clear(); // keep track of total connections so we can reserve edge vector size int edgeCount = 0; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mCell = esm.getHString(); break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mData, 12); hasData = true; break; case ESM::FourCC<'P','G','R','P'>::value: { esm.getSubHeader(); int size = esm.getSubSize(); // Check that the sizes match up. Size = 16 * s2 (path points) if (size != static_cast (sizeof(Point) * mData.mS2)) esm.fail("Path point subrecord size mismatch"); else { int pointCount = mData.mS2; mPoints.reserve(pointCount); for (int i = 0; i < pointCount; ++i) { Point p; esm.getExact(&p, sizeof(Point)); mPoints.push_back(p); edgeCount += p.mConnectionNum; } } break; } case ESM::FourCC<'P','G','R','C'>::value: { esm.getSubHeader(); int size = esm.getSubSize(); if (size % sizeof(int) != 0) esm.fail("PGRC size not a multiple of 4"); else { int rawConnNum = size / sizeof(int); std::vector rawConnections; rawConnections.reserve(rawConnNum); for (int i = 0; i < rawConnNum; ++i) { int currentValue; esm.getT(currentValue); rawConnections.push_back(currentValue); } std::vector::const_iterator rawIt = rawConnections.begin(); int pointIndex = 0; mEdges.reserve(edgeCount); for(PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it, ++pointIndex) { unsigned char connectionNum = (*it).mConnectionNum; for (int i = 0; i < connectionNum; ++i) { Edge edge; edge.mV0 = pointIndex; edge.mV1 = *rawIt; ++rawIt; mEdges.push_back(edge); } } } break; } case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasData) esm.fail("Missing DATA subrecord"); } void Pathgrid::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mCell); esm.writeHNT("DATA", mData, 12); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } if (!mPoints.empty()) { esm.startSubRecord("PGRP"); for (PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it) { esm.writeT(*it); } esm.endRecord("PGRP"); } if (!mEdges.empty()) { esm.startSubRecord("PGRC"); for (std::vector::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) { esm.writeT(it->mV1); } esm.endRecord("PGRC"); } } void Pathgrid::blank() { mCell.clear(); mData.mX = 0; mData.mY = 0; mData.mS1 = 0; mData.mS2 = 0; mPoints.clear(); mEdges.clear(); } } openmw-openmw-0.38.0/components/esm/loadpgrd.hpp000066400000000000000000000030471264522266000217010ustar00rootroot00000000000000#ifndef OPENMW_ESM_PGRD_H #define OPENMW_ESM_PGRD_H #include #include namespace ESM { class ESMReader; class ESMWriter; /* * Path grid. */ struct Pathgrid { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Pathgrid"; } struct DATAstruct { int mX, mY; // Grid location, matches cell for exterior cells short mS1; // ?? Usually but not always a power of 2. Doesn't seem // to have any relation to the size of PGRC. short mS2; // Number of path points. }; // 12 bytes struct Point // path grid point { int mX, mY, mZ; // Location of point unsigned char mAutogenerated; // autogenerated vs. user coloring flag? unsigned char mConnectionNum; // number of connections for this point short mUnknown; Point& operator=(const float[3]); Point(const float[3]); Point(); Point(int x, int y, int z) : mX(x), mY(y), mZ(z) {} }; // 16 bytes struct Edge // path grid edge { int mV0, mV1; // index of points connected with this edge }; // 8 bytes std::string mCell; // Cell name DATAstruct mData; typedef std::vector PointList; PointList mPoints; typedef std::vector EdgeList; EdgeList mEdges; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); }; } #endif openmw-openmw-0.38.0/components/esm/loadprob.cpp000066400000000000000000000045121264522266000217000ustar00rootroot00000000000000#include "loadprob.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Probe::sRecordId = REC_PROB; void Probe::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'P','B','D','T'>::value: esm.getHT(mData, 16); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing PBDT subrecord"); } void Probe::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("PBDT", mData, 16); esm.writeHNOString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); } void Probe::blank() { mData.mWeight = 0; mData.mValue = 0; mData.mQuality = 0; mData.mUses = 0; mName.clear(); mModel.clear(); mIcon.clear(); mScript.clear(); } } openmw-openmw-0.38.0/components/esm/loadprob.hpp000066400000000000000000000013471264522266000217100ustar00rootroot00000000000000#ifndef OPENMW_ESM_PROBE_H #define OPENMW_ESM_PROBE_H #include namespace ESM { class ESMReader; class ESMWriter; struct Probe { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Probe"; } struct Data { float mWeight; int mValue; float mQuality; int mUses; }; // Size = 16 Data mData; std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadrace.cpp000066400000000000000000000052071264522266000216520ustar00rootroot00000000000000#include "loadrace.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Race::sRecordId = REC_RACE; int Race::MaleFemale::getValue (bool male) const { return male ? mMale : mFemale; } int Race::MaleFemaleF::getValue (bool male) const { return static_cast(male ? mMale : mFemale); } void Race::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mPowers.mList.clear(); bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'R','A','D','T'>::value: esm.getHT(mData, 140); hasData = true; break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; case ESM::FourCC<'N','P','C','S'>::value: mPowers.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing RADT subrecord"); } void Race::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNOCString("FNAM", mName); esm.writeHNT("RADT", mData, 140); mPowers.save(esm); esm.writeHNOString("DESC", mDescription); } void Race::blank() { mName.clear(); mDescription.clear(); mPowers.mList.clear(); for (int i=0; i<7; ++i) { mData.mBonus[i].mSkill = -1; mData.mBonus[i].mBonus = 0; } for (int i=0; i<8; ++i) mData.mAttributeValues[i].mMale = mData.mAttributeValues[i].mFemale = 1; mData.mHeight.mMale = mData.mHeight.mFemale = 1; mData.mWeight.mMale = mData.mWeight.mFemale = 1; mData.mFlags = 0; } } openmw-openmw-0.38.0/components/esm/loadrace.hpp000066400000000000000000000027531264522266000216620ustar00rootroot00000000000000#ifndef OPENMW_ESM_RACE_H #define OPENMW_ESM_RACE_H #include #include "spelllist.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Race definition */ struct Race { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Race"; } struct SkillBonus { int mSkill; // SkillEnum int mBonus; }; struct MaleFemale { int mMale, mFemale; int getValue (bool male) const; }; struct MaleFemaleF { float mMale, mFemale; int getValue (bool male) const; }; enum Flags { Playable = 0x01, Beast = 0x02 }; struct RADTstruct { // List of skills that get a bonus SkillBonus mBonus[7]; // Attribute values for male/female MaleFemale mAttributeValues[8]; // The actual eye level height (in game units) is (probably) given // as 'height' times 128. This has not been tested yet. MaleFemaleF mHeight, mWeight; int mFlags; // 0x1 - playable, 0x2 - beast race }; // Size = 140 bytes RADTstruct mData; std::string mId, mName, mDescription; SpellList mPowers; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). }; } #endif openmw-openmw-0.38.0/components/esm/loadregn.cpp000066400000000000000000000071021264522266000216670ustar00rootroot00000000000000#include "loadregn.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Region::sRecordId = REC_REGN; void Region::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'W','E','A','T'>::value: { esm.getSubHeader(); if (esm.getVer() == VER_12) { mData.mA = 0; mData.mB = 0; esm.getExact(&mData, sizeof(mData) - 2); } else if (esm.getVer() == VER_13) { // May include the additional two bytes (but not necessarily) if (esm.getSubSize() == sizeof(mData)) { esm.getExact(&mData, sizeof(mData)); } else { mData.mA = 0; mData.mB = 0; esm.getExact(&mData, sizeof(mData)-2); } } else { esm.fail("Don't know what to do in this version"); } break; } case ESM::FourCC<'B','N','A','M'>::value: mSleepList = esm.getHString(); break; case ESM::FourCC<'C','N','A','M'>::value: esm.getHT(mMapColor); break; case ESM::FourCC<'S','N','A','M'>::value: SoundRef sr; esm.getHT(sr, 33); mSoundList.push_back(sr); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); } void Region::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNOCString("FNAM", mName); if (esm.getVersion() == VER_12) esm.writeHNT("WEAT", mData, sizeof(mData) - 2); else esm.writeHNT("WEAT", mData); esm.writeHNOCString("BNAM", mSleepList); esm.writeHNT("CNAM", mMapColor); for (std::vector::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) { esm.writeHNT("SNAM", *it); } } void Region::blank() { mName.clear(); mData.mClear = mData.mCloudy = mData.mFoggy = mData.mOvercast = mData.mRain = mData.mThunder = mData.mAsh, mData.mBlight = mData.mA = mData.mB = 0; mMapColor = 0; mName.clear(); mSleepList.clear(); mSoundList.clear(); } } openmw-openmw-0.38.0/components/esm/loadregn.hpp000066400000000000000000000026601264522266000217000ustar00rootroot00000000000000#ifndef OPENMW_ESM_REGN_H #define OPENMW_ESM_REGN_H #include #include #include "esmcommon.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Region data */ struct Region { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Region"; } #pragma pack(push) #pragma pack(1) struct WEATstruct { // These are probabilities that add up to 100 unsigned char mClear, mCloudy, mFoggy, mOvercast, mRain, mThunder, mAsh, mBlight, // Unknown weather, probably snow and something. Only // present in file version 1.3. // the engine uses mA as "snow" and mB as "blizard" mA, mB; }; // 10 bytes // Reference to a sound that is played randomly in this region struct SoundRef { NAME32 mSound; unsigned char mChance; }; // 33 bytes #pragma pack(pop) WEATstruct mData; int mMapColor; // RGBA // sleepList refers to a leveled list of creatures you can meet if // you sleep outside in this region. std::string mId, mName, mSleepList; std::vector mSoundList; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). }; } #endif openmw-openmw-0.38.0/components/esm/loadrepa.cpp000066400000000000000000000045161264522266000216710ustar00rootroot00000000000000#include "loadrepa.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Repair::sRecordId = REC_REPA; void Repair::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'R','I','D','T'>::value: esm.getHT(mData, 16); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing RIDT subrecord"); } void Repair::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("RIDT", mData, 16); esm.writeHNOString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); } void Repair::blank() { mData.mWeight = 0; mData.mValue = 0; mData.mQuality = 0; mData.mUses = 0; mName.clear(); mModel.clear(); mIcon.clear(); mScript.clear(); } } openmw-openmw-0.38.0/components/esm/loadrepa.hpp000066400000000000000000000013471264522266000216750ustar00rootroot00000000000000#ifndef OPENMW_ESM_REPA_H #define OPENMW_ESM_REPA_H #include namespace ESM { class ESMReader; class ESMWriter; struct Repair { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Repair"; } struct Data { float mWeight; int mValue; int mUses; float mQuality; }; // Size = 16 Data mData; std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadscpt.cpp000066400000000000000000000116271264522266000217140ustar00rootroot00000000000000#include "loadscpt.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" #include namespace ESM { unsigned int Script::sRecordId = REC_SCPT; void Script::loadSCVR(ESMReader &esm) { int s = mData.mStringTableSize; std::vector tmp (s); // not using getHExact, vanilla doesn't seem to mind unused bytes at the end esm.getSubHeader(); int left = esm.getSubSize(); if (left < s) esm.fail("SCVR string list is smaller than specified"); esm.getExact(&tmp[0], s); if (left > s) esm.skip(left-s); // skip the leftover junk // Set up the list of variable names mVarNames.resize(mData.mNumShorts + mData.mNumLongs + mData.mNumFloats); // The tmp buffer is a null-byte separated string list, we // just have to pick out one string at a time. char* str = &tmp[0]; for (size_t i = 0; i < mVarNames.size(); i++) { // Support '\r' terminated strings like vanilla. See Bug #1324. char *termsym = strchr(str, '\r'); if(termsym) *termsym = '\0'; mVarNames[i] = std::string(str); str += mVarNames[i].size() + 1; if (str - &tmp[0] > s) { // Apparently SCVR subrecord is not used and variable names are // determined on the fly from the script text. Therefore don't throw // an exeption, just log an error and continue. std::stringstream ss; ss << "ESM Error: " << "String table overflow"; ss << "\n File: " << esm.getName(); ss << "\n Record: " << esm.getContext().recName.toString(); ss << "\n Subrecord: " << "SCVR"; ss << "\n Offset: 0x" << std::hex << esm.getFileOffset(); std::cerr << ss.str() << std::endl; break; } } } void Script::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mVarNames.clear(); bool hasHeader = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'S','C','H','D'>::value: SCHD data; esm.getHT(data, 52); mData = data.mData; mId = data.mName.toString(); hasHeader = true; break; case ESM::FourCC<'S','C','V','R'>::value: // list of local variables loadSCVR(esm); break; case ESM::FourCC<'S','C','D','T'>::value: // compiled script mScriptData.resize(mData.mScriptDataSize); esm.getHExact(&mScriptData[0], mScriptData.size()); break; case ESM::FourCC<'S','C','T','X'>::value: mScriptText = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasHeader) esm.fail("Missing SCHD subrecord"); } void Script::save(ESMWriter &esm, bool isDeleted) const { std::string varNameString; if (!mVarNames.empty()) for (std::vector::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) varNameString.append(*it); SCHD data; memset(&data, 0, sizeof(data)); data.mData = mData; memcpy(data.mName.name, mId.c_str(), mId.size()); esm.writeHNT("SCHD", data, 52); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } if (!mVarNames.empty()) { esm.startSubRecord("SCVR"); for (std::vector::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) { esm.writeHCString(*it); } esm.endRecord("SCVR"); } esm.startSubRecord("SCDT"); esm.write(reinterpret_cast(&mScriptData[0]), mData.mScriptDataSize); esm.endRecord("SCDT"); esm.writeHNOString("SCTX", mScriptText); } void Script::blank() { mData.mNumShorts = mData.mNumLongs = mData.mNumFloats = 0; mData.mScriptDataSize = 0; mData.mStringTableSize = 0; mVarNames.clear(); mScriptData.clear(); if (mId.find ("::")!=std::string::npos) mScriptText = "Begin \"" + mId + "\"\n\nEnd " + mId + "\n"; else mScriptText = "Begin " + mId + "\n\nEnd " + mId + "\n"; } } openmw-openmw-0.38.0/components/esm/loadscpt.hpp000066400000000000000000000030061264522266000217110ustar00rootroot00000000000000#ifndef OPENMW_ESM_SCPT_H #define OPENMW_ESM_SCPT_H #include #include #include "esmcommon.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Script definitions */ class Script { public: static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Script"; } struct SCHDstruct { /// Data from script-precompling in the editor. /// \warning Do not use them. OpenCS currently does not precompile scripts. int mNumShorts, mNumLongs, mNumFloats, mScriptDataSize, mStringTableSize; }; struct SCHD { NAME32 mName; Script::SCHDstruct mData; }; // 52 bytes std::string mId; SCHDstruct mData; /// Variable names generated by script-precompiling in the editor. /// \warning Do not use this field. OpenCS currently does not precompile scripts. std::vector mVarNames; /// Bytecode generated from script-precompiling in the editor. /// \warning Do not use this field. OpenCS currently does not precompile scripts. std::vector mScriptData; /// Script source code std::string mScriptText; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). private: void loadSCVR(ESMReader &esm); }; } #endif openmw-openmw-0.38.0/components/esm/loadskil.cpp000066400000000000000000000120201264522266000216710ustar00rootroot00000000000000#include "loadskil.hpp" #include #include #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { const std::string Skill::sSkillNames[Length] = { "Block", "Armorer", "Mediumarmor", "Heavyarmor", "Bluntweapon", "Longblade", "Axe", "Spear", "Athletics", "Enchant", "Destruction", "Alteration", "Illusion", "Conjuration", "Mysticism", "Restoration", "Alchemy", "Unarmored", "Security", "Sneak", "Acrobatics", "Lightarmor", "Shortblade", "Marksman", "Mercantile", "Speechcraft", "Handtohand", }; const std::string Skill::sSkillNameIds[Length] = { "sSkillBlock", "sSkillArmorer", "sSkillMediumarmor", "sSkillHeavyarmor", "sSkillBluntweapon", "sSkillLongblade", "sSkillAxe", "sSkillSpear", "sSkillAthletics", "sSkillEnchant", "sSkillDestruction", "sSkillAlteration", "sSkillIllusion", "sSkillConjuration", "sSkillMysticism", "sSkillRestoration", "sSkillAlchemy", "sSkillUnarmored", "sSkillSecurity", "sSkillSneak", "sSkillAcrobatics", "sSkillLightarmor", "sSkillShortblade", "sSkillMarksman", "sSkillMercantile", "sSkillSpeechcraft", "sSkillHandtohand", }; const std::string Skill::sIconNames[Length] = { "combat_block.dds", "combat_armor.dds", "combat_mediumarmor.dds", "combat_heavyarmor.dds", "combat_blunt.dds", "combat_longblade.dds", "combat_axe.dds", "combat_spear.dds", "combat_athletics.dds", "magic_enchant.dds", "magic_destruction.dds", "magic_alteration.dds", "magic_illusion.dds", "magic_conjuration.dds", "magic_mysticism.dds", "magic_restoration.dds", "magic_alchemy.dds", "magic_unarmored.dds", "stealth_security.dds", "stealth_sneak.dds", "stealth_acrobatics.dds", "stealth_lightarmor.dds", "stealth_shortblade.dds", "stealth_marksman.dds", "stealth_mercantile.dds", "stealth_speechcraft.dds", "stealth_handtohand.dds", }; const boost::array Skill::sSkillIds = {{ Block, Armorer, MediumArmor, HeavyArmor, BluntWeapon, LongBlade, Axe, Spear, Athletics, Enchant, Destruction, Alteration, Illusion, Conjuration, Mysticism, Restoration, Alchemy, Unarmored, Security, Sneak, Acrobatics, LightArmor, ShortBlade, Marksman, Mercantile, Speechcraft, HandToHand }}; unsigned int Skill::sRecordId = REC_SKIL; void Skill::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; // Skill record can't be deleted now (may be changed in the future) bool hasIndex = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'I','N','D','X'>::value: esm.getHT(mIndex); hasIndex = true; break; case ESM::FourCC<'S','K','D','T'>::value: esm.getHT(mData, 24); hasData = true; break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; default: esm.fail("Unknown subrecord"); } } if (!hasIndex) esm.fail("Missing INDX"); if (!hasData) esm.fail("Missing SKDT"); // create an ID from the index and the name (only used in the editor and likely to change in the // future) mId = indexToId (mIndex); } void Skill::save(ESMWriter &esm, bool /*isDeleted*/) const { esm.writeHNT("INDX", mIndex); esm.writeHNT("SKDT", mData, 24); esm.writeHNOString("DESC", mDescription); } void Skill::blank() { mData.mAttribute = 0; mData.mSpecialization = 0; mData.mUseValue[0] = mData.mUseValue[1] = mData.mUseValue[2] = mData.mUseValue[3] = 1.0; mDescription.clear(); } std::string Skill::indexToId (int index) { std::ostringstream stream; if (index!=-1) { stream << "#"; if (index<10) stream << "0"; stream << index; if (index>=0 && index #include #include "defs.hpp" namespace ESM { class ESMReader; class ESMWriter; /* * Skill information * */ struct Skill { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Skill"; } std::string mId; struct SKDTstruct { int mAttribute; // see defs.hpp int mSpecialization;// 0 - Combat, 1 - Magic, 2 - Stealth float mUseValue[4]; // How much skill improves through use. Meaning // of each field depends on what skill this // is. We should document this better later. }; // Total size: 24 bytes SKDTstruct mData; // Skill index. Skils don't have an id ("NAME") like most records, // they only have a numerical index that matches one of the // hard-coded skills in the game. int mIndex; std::string mDescription; enum SkillEnum { Block = 0, Armorer = 1, MediumArmor = 2, HeavyArmor = 3, BluntWeapon = 4, LongBlade = 5, Axe = 6, Spear = 7, Athletics = 8, Enchant = 9, Destruction = 10, Alteration = 11, Illusion = 12, Conjuration = 13, Mysticism = 14, Restoration = 15, Alchemy = 16, Unarmored = 17, Security = 18, Sneak = 19, Acrobatics = 20, LightArmor = 21, ShortBlade = 22, Marksman = 23, Mercantile = 24, Speechcraft = 25, HandToHand = 26, Length }; static const std::string sSkillNames[Length]; static const std::string sSkillNameIds[Length]; static const std::string sIconNames[Length]; static const boost::array sSkillIds; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). static std::string indexToId (int index); }; } #endif openmw-openmw-0.38.0/components/esm/loadsndg.cpp000066400000000000000000000036371264522266000217000ustar00rootroot00000000000000#include "loadsndg.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int SoundGenerator::sRecordId = REC_SNDG; void SoundGenerator::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mType, 4); hasData = true; break; case ESM::FourCC<'C','N','A','M'>::value: mCreature = esm.getHString(); break; case ESM::FourCC<'S','N','A','M'>::value: mSound = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing DATA subrecord"); } void SoundGenerator::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNT("DATA", mType, 4); esm.writeHNOCString("CNAM", mCreature); esm.writeHNOCString("SNAM", mSound); } void SoundGenerator::blank() { mType = LeftFoot; mCreature.clear(); mSound.clear(); } } openmw-openmw-0.38.0/components/esm/loadsndg.hpp000066400000000000000000000014741264522266000217020ustar00rootroot00000000000000#ifndef OPENMW_ESM_SNDG_H #define OPENMW_ESM_SNDG_H #include namespace ESM { class ESMReader; class ESMWriter; /* * Sound generator. This describes the sounds a creature make. */ struct SoundGenerator { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "SoundGenerator"; } enum Type { LeftFoot = 0, RightFoot = 1, SwimLeft = 2, SwimRight = 3, Moan = 4, Roar = 5, Scream = 6, Land = 7 }; // Type int mType; std::string mId, mCreature, mSound; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); }; } #endif openmw-openmw-0.38.0/components/esm/loadsoun.cpp000066400000000000000000000033411264522266000217210ustar00rootroot00000000000000#include "loadsoun.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Sound::sRecordId = REC_SOUN; void Sound::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mSound = esm.getHString(); break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mData, 3); hasData = true; break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing DATA subrecord"); } void Sound::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNOCString("FNAM", mSound); esm.writeHNT("DATA", mData, 3); } void Sound::blank() { mSound.clear(); mData.mVolume = 128; mData.mMinRange = 0; mData.mMaxRange = 255; } } openmw-openmw-0.38.0/components/esm/loadsoun.hpp000066400000000000000000000012401264522266000217220ustar00rootroot00000000000000#ifndef OPENMW_ESM_SOUN_H #define OPENMW_ESM_SOUN_H #include namespace ESM { class ESMReader; class ESMWriter; struct SOUNstruct { unsigned char mVolume, mMinRange, mMaxRange; }; struct Sound { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Sound"; } SOUNstruct mData; std::string mId, mSound; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). }; } #endif openmw-openmw-0.38.0/components/esm/loadspel.cpp000066400000000000000000000037761264522266000217140ustar00rootroot00000000000000#include "loadspel.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Spell::sRecordId = REC_SPEL; void Spell::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mEffects.mList.clear(); bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'S','P','D','T'>::value: esm.getHT(mData, 12); hasData = true; break; case ESM::FourCC<'E','N','A','M'>::value: ENAMstruct s; esm.getHT(s, 24); mEffects.mList.push_back(s); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing SPDT subrecord"); } void Spell::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNOCString("FNAM", mName); esm.writeHNT("SPDT", mData, 12); mEffects.save(esm); } void Spell::blank() { mData.mType = 0; mData.mCost = 0; mData.mFlags = 0; mName.clear(); mEffects.mList.clear(); } } openmw-openmw-0.38.0/components/esm/loadspel.hpp000066400000000000000000000024571264522266000217140ustar00rootroot00000000000000#ifndef OPENMW_ESM_SPEL_H #define OPENMW_ESM_SPEL_H #include #include "effectlist.hpp" namespace ESM { class ESMReader; class ESMWriter; struct Spell { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Spell"; } enum SpellType { ST_Spell = 0, // Normal spell, must be cast and costs mana ST_Ability = 1, // Inert ability, always in effect ST_Blight = 2, // Blight disease ST_Disease = 3, // Common disease ST_Curse = 4, // Curse (?) ST_Power = 5 // Power, can use once a day }; enum Flags { F_Autocalc = 1, // Can be selected by NPC spells auto-calc F_PCStart = 2, // Can be selected by player spells auto-calc F_Always = 4 // Casting always succeeds }; struct SPDTstruct { int mType; // SpellType int mCost; // Mana cost int mFlags; // Flags }; SPDTstruct mData; std::string mId, mName; EffectList mEffects; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID/index). }; } #endif openmw-openmw-0.38.0/components/esm/loadsscr.cpp000066400000000000000000000027561264522266000217200ustar00rootroot00000000000000#include "loadsscr.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int StartScript::sRecordId = REC_SSCR; void StartScript::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasData = false; bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'D','A','T','A'>::value: mData = esm.getHString(); hasData = true; break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME"); if (!hasData && !isDeleted) esm.fail("Missing DATA"); } void StartScript::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); } else { esm.writeHNString("DATA", mData); } } void StartScript::blank() { mData.clear(); } } openmw-openmw-0.38.0/components/esm/loadsscr.hpp000066400000000000000000000015461264522266000217210ustar00rootroot00000000000000#ifndef OPENMW_ESM_SSCR_H #define OPENMW_ESM_SSCR_H #include namespace ESM { class ESMReader; class ESMWriter; /* Startup script. I think this is simply a 'main' script that is run from the begining. The SSCR records contain a DATA identifier which is totally useless (TODO: don't remember what it contains exactly, document it below later.), and a NAME which is simply a script reference. */ struct StartScript { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "StartScript"; } std::string mData; std::string mId; // Load a record and add it to the list void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); }; } #endif openmw-openmw-0.38.0/components/esm/loadstat.cpp000066400000000000000000000025341264522266000217130ustar00rootroot00000000000000#include "loadstat.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Static::sRecordId = REC_STAT; void Static::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); } void Static::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); } else { esm.writeHNCString("MODL", mModel); } } void Static::blank() { mModel.clear(); } } openmw-openmw-0.38.0/components/esm/loadstat.hpp000066400000000000000000000021101264522266000217060ustar00rootroot00000000000000#ifndef OPENMW_ESM_STAT_H #define OPENMW_ESM_STAT_H #include namespace ESM { class ESMReader; class ESMWriter; /* * Definition of static object. * * A stat record is basically just a reference to a nif file. Some * esps seem to contain copies of the STAT entries from the esms, and * the esms themselves contain several identical entries. Perhaps all * statics referenced in a file is also put in the file? Since we are * only reading files it doesn't much matter to us, but it would if we * were writing our own ESM/ESPs. You can check some files later when * you decode the CELL blocks, if you want to test this hypothesis. */ struct Static { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Static"; } std::string mId, mModel; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/loadtes3.cpp000066400000000000000000000040201264522266000216060ustar00rootroot00000000000000#include "loadtes3.hpp" #include "esmcommon.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" void ESM::Header::blank() { mData.version = ESM::VER_13; mData.type = 0; mData.author.assign (""); mData.desc.assign (""); mData.records = 0; mFormat = CurrentFormat; mMaster.clear(); } void ESM::Header::load (ESMReader &esm) { if (esm.isNextSub ("FORM")) { esm.getHT (mFormat); if (mFormat<0) esm.fail ("invalid format code"); } else mFormat = 0; if (esm.isNextSub("HEDR")) { esm.getSubHeader(); esm.getT(mData.version); esm.getT(mData.type); mData.author.assign(esm.getString(sizeof(mData.author.name))); mData.desc.assign(esm.getString(sizeof(mData.desc.name))); esm.getT(mData.records); } while (esm.isNextSub ("MAST")) { MasterData m; m.name = esm.getHString(); m.size = esm.getHNLong ("DATA"); mMaster.push_back (m); } if (esm.isNextSub("GMDT")) { esm.getHT(mGameData); } if (esm.isNextSub("SCRD")) { esm.getSubHeader(); mSCRD.resize(esm.getSubSize()); if (mSCRD.size()) esm.getExact(&mSCRD[0], mSCRD.size()); } if (esm.isNextSub("SCRS")) { esm.getSubHeader(); mSCRS.resize(esm.getSubSize()); if (mSCRS.size()) esm.getExact(&mSCRS[0], mSCRS.size()); } } void ESM::Header::save (ESMWriter &esm) { if (mFormat>0) esm.writeHNT ("FORM", mFormat); esm.startSubRecord("HEDR"); esm.writeT(mData.version); esm.writeT(mData.type); esm.writeFixedSizeString(mData.author.toString(), 32); esm.writeFixedSizeString(mData.desc.toString(), 256); esm.writeT(mData.records); esm.endRecord("HEDR"); for (std::vector::iterator iter = mMaster.begin(); iter != mMaster.end(); ++iter) { esm.writeHNCString ("MAST", iter->name); esm.writeHNT ("DATA", iter->size); } } openmw-openmw-0.38.0/components/esm/loadtes3.hpp000066400000000000000000000034311264522266000216200ustar00rootroot00000000000000#ifndef COMPONENT_ESM_TES3_H #define COMPONENT_ESM_TES3_H #include #include "esmcommon.hpp" namespace ESM { class ESMReader; class ESMWriter; #pragma pack(push) #pragma pack(1) /// \brief File header record struct Header { static const int CurrentFormat = 0; // most recent known format struct Data { /* File format version. This is actually a float, the supported versions are 1.2 and 1.3. These correspond to: 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 */ unsigned int version; int type; // 0=esp, 1=esm, 32=ess (unused) NAME32 author; // Author's name NAME256 desc; // File description int records; // Number of records }; // Defines another files (esm or esp) that this file depends upon. struct MasterData { std::string name; uint64_t size; int index; // Position of the parent file in the global list of loaded files }; struct GMDT { float mCurrentHealth; float mMaximumHealth; float mHour; unsigned char unknown1[12]; NAME64 mCurrentCell; unsigned char unknown2[4]; NAME32 mPlayerName; }; GMDT mGameData; // Used in .ess savegames only std::vector mSCRD; // Used in .ess savegames only, unknown std::vector mSCRS; // Used in .ess savegames only, screenshot Data mData; int mFormat; std::vector mMaster; void blank(); void load (ESMReader &esm); void save (ESMWriter &esm); }; #pragma pack(pop) } #endif openmw-openmw-0.38.0/components/esm/loadweap.cpp000066400000000000000000000053701264522266000216750ustar00rootroot00000000000000#include "loadweap.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace ESM { unsigned int Weapon::sRecordId = REC_WEAP; void Weapon::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'W','P','D','T'>::value: esm.getHT(mData, 32); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::FourCC<'E','N','A','M'>::value: mEnchant = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing WPDT subrecord"); } void Weapon::save(ESMWriter &esm, bool isDeleted) const { esm.writeHNCString("NAME", mId); if (isDeleted) { esm.writeHNCString("DELE", ""); return; } esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); esm.writeHNT("WPDT", mData, 32); esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("ENAM", mEnchant); } void Weapon::blank() { mData.mWeight = 0; mData.mValue = 0; mData.mType = 0; mData.mHealth = 0; mData.mSpeed = 0; mData.mReach = 0; mData.mEnchant = 0; mData.mChop[0] = mData.mChop[1] = 0; mData.mSlash[0] = mData.mSlash[1] = 0; mData.mThrust[0] = mData.mThrust[1] = 0; mData.mFlags = 0; mName.clear(); mModel.clear(); mIcon.clear(); mEnchant.clear(); mScript.clear(); } } openmw-openmw-0.38.0/components/esm/loadweap.hpp000066400000000000000000000031021264522266000216710ustar00rootroot00000000000000#ifndef OPENMW_ESM_WEAP_H #define OPENMW_ESM_WEAP_H #include namespace ESM { class ESMReader; class ESMWriter; /* * Weapon definition */ struct Weapon { static unsigned int sRecordId; /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "Weapon"; } enum Type { ShortBladeOneHand = 0, LongBladeOneHand = 1, LongBladeTwoHand = 2, BluntOneHand = 3, BluntTwoClose = 4, BluntTwoWide = 5, SpearTwoWide = 6, AxeOneHand = 7, AxeTwoHand = 8, MarksmanBow = 9, MarksmanCrossbow = 10, MarksmanThrown = 11, Arrow = 12, Bolt = 13 }; enum AttackType { AT_Chop, AT_Slash, AT_Thrust }; enum Flags { Magical = 0x01, Silver = 0x02 }; #pragma pack(push) #pragma pack(1) struct WPDTstruct { float mWeight; int mValue; short mType; short mHealth; float mSpeed, mReach; short mEnchant; // Enchantment points. The real value is mEnchant/10.f unsigned char mChop[2], mSlash[2], mThrust[2]; // Min and max int mFlags; }; // 32 bytes #pragma pack(pop) WPDTstruct mData; std::string mId, mName, mModel, mIcon, mEnchant, mScript; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; void blank(); ///< Set record to default state (does not touch the ID). }; } #endif openmw-openmw-0.38.0/components/esm/locals.cpp000066400000000000000000000012101264522266000213430ustar00rootroot00000000000000#include "locals.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::Locals::load (ESMReader &esm) { while (esm.isNextSub ("LOCA")) { std::string id = esm.getHString(); Variant value; value.read (esm, Variant::Format_Local); mVariables.push_back (std::make_pair (id, value)); } } void ESM::Locals::save (ESMWriter &esm) const { for (std::vector >::const_iterator iter (mVariables.begin()); iter!=mVariables.end(); ++iter) { esm.writeHNString ("LOCA", iter->first); iter->second.write (esm, Variant::Format_Local); } } openmw-openmw-0.38.0/components/esm/locals.hpp000066400000000000000000000007621264522266000213630ustar00rootroot00000000000000#ifndef OPENMW_ESM_LOCALS_H #define OPENMW_ESM_LOCALS_H #include #include #include "variant.hpp" namespace ESM { class ESMReader; class ESMWriter; /// \brief Storage structure for local variables (only used in saved games) /// /// \note This is not a top-level record. struct Locals { std::vector > mVariables; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/magiceffects.cpp000066400000000000000000000010611264522266000225120ustar00rootroot00000000000000#include "magiceffects.hpp" #include "esmwriter.hpp" #include "esmreader.hpp" namespace ESM { void MagicEffects::save(ESMWriter &esm) const { for (std::map::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { esm.writeHNT("EFID", it->first); esm.writeHNT("BASE", it->second); } } void MagicEffects::load(ESMReader &esm) { while (esm.isNextSub("EFID")) { int id, base; esm.getHT(id); esm.getHNT(base, "BASE"); mEffects.insert(std::make_pair(id, base)); } } } openmw-openmw-0.38.0/components/esm/magiceffects.hpp000066400000000000000000000006011264522266000225160ustar00rootroot00000000000000#ifndef COMPONENTS_ESM_MAGICEFFECTS_H #define COMPONENTS_ESM_MAGICEFFECTS_H #include namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only struct MagicEffects { // std::map mEffects; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/npcstate.cpp000066400000000000000000000011571264522266000217210ustar00rootroot00000000000000#include "npcstate.hpp" void ESM::NpcState::load (ESMReader &esm) { ObjectState::load (esm); if (mHasCustomState) { mInventory.load (esm); mNpcStats.load (esm); mCreatureStats.load (esm); } } void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const { ObjectState::save (esm, inInventory); if (mHasCustomState) { mInventory.save (esm); mNpcStats.save (esm); mCreatureStats.save (esm); } } void ESM::NpcState::blank() { ObjectState::blank(); mNpcStats.blank(); mCreatureStats.blank(); mHasCustomState = true; } openmw-openmw-0.38.0/components/esm/npcstate.hpp000066400000000000000000000010741264522266000217240ustar00rootroot00000000000000#ifndef OPENMW_ESM_NPCSTATE_H #define OPENMW_ESM_NPCSTATE_H #include "objectstate.hpp" #include "inventorystate.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" namespace ESM { // format 0, saved games only struct NpcState : public ObjectState { InventoryState mInventory; NpcStats mNpcStats; CreatureStats mCreatureStats; /// Initialize to default state void blank(); virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; }; } #endif openmw-openmw-0.38.0/components/esm/npcstats.cpp000066400000000000000000000106551264522266000217420ustar00rootroot00000000000000#include "npcstats.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" ESM::NpcStats::Faction::Faction() : mExpelled (false), mRank (-1), mReputation (0) {} void ESM::NpcStats::load (ESMReader &esm) { while (esm.isNextSub ("FACT")) { std::string id = esm.getHString(); Faction faction; int expelled = 0; esm.getHNOT (expelled, "FAEX"); if (expelled) faction.mExpelled = true; esm.getHNOT (faction.mRank, "FARA"); esm.getHNOT (faction.mReputation, "FARE"); mFactions.insert (std::make_pair (id, faction)); } mDisposition = 0; esm.getHNOT (mDisposition, "DISP"); for (int i=0; i<27; ++i) mSkills[i].load (esm); mWerewolfDeprecatedData = false; if (esm.peekNextSub("STBA")) { // we have deprecated werewolf skills, stored interleaved // Load into one big vector, then remove every 2nd value mWerewolfDeprecatedData = true; std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); for (int i=0; i<27; ++i) { ESM::StatState skill; skill.load(esm); skills.push_back(skill); } int i=0; for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) { if (i%2 == 1) it = skills.erase(it); else ++it; } assert(skills.size() == 27); std::copy(skills.begin(), skills.end(), mSkills); } // No longer used bool hasWerewolfAttributes = false; esm.getHNOT (hasWerewolfAttributes, "HWAT"); if (hasWerewolfAttributes) { ESM::StatState dummy; for (int i=0; i<8; ++i) dummy.load(esm); mWerewolfDeprecatedData = true; } mIsWerewolf = false; esm.getHNOT (mIsWerewolf, "WOLF"); mBounty = 0; esm.getHNOT (mBounty, "BOUN"); mReputation = 0; esm.getHNOT (mReputation, "REPU"); mWerewolfKills = 0; esm.getHNOT (mWerewolfKills, "WKIL"); // No longer used if (esm.isNextSub("PROF")) esm.skipHSub(); // int profit // No longer used if (esm.isNextSub("ASTR")) esm.skipHSub(); // attackStrength mLevelProgress = 0; esm.getHNOT (mLevelProgress, "LPRO"); esm.getHNT (mSkillIncrease, "INCR"); while (esm.isNextSub ("USED")) mUsedIds.push_back (esm.getHString()); mTimeToStartDrowning = 0; esm.getHNOT (mTimeToStartDrowning, "DRTI"); // No longer used float lastDrowningHit = 0; esm.getHNOT (lastDrowningHit, "DRLH"); // No longer used float levelHealthBonus = 0; esm.getHNOT (levelHealthBonus, "LVLH"); mCrimeId = -1; esm.getHNOT (mCrimeId, "CRID"); } void ESM::NpcStats::save (ESMWriter &esm) const { for (std::map::const_iterator iter (mFactions.begin()); iter!=mFactions.end(); ++iter) { esm.writeHNString ("FACT", iter->first); if (iter->second.mExpelled) { int expelled = 1; esm.writeHNT ("FAEX", expelled); } if (iter->second.mRank >= 0) esm.writeHNT ("FARA", iter->second.mRank); if (iter->second.mReputation) esm.writeHNT ("FARE", iter->second.mReputation); } if (mDisposition) esm.writeHNT ("DISP", mDisposition); for (int i=0; i<27; ++i) mSkills[i].save (esm); if (mIsWerewolf) esm.writeHNT ("WOLF", mIsWerewolf); if (mBounty) esm.writeHNT ("BOUN", mBounty); if (mReputation) esm.writeHNT ("REPU", mReputation); if (mWerewolfKills) esm.writeHNT ("WKIL", mWerewolfKills); if (mLevelProgress) esm.writeHNT ("LPRO", mLevelProgress); esm.writeHNT ("INCR", mSkillIncrease); for (std::vector::const_iterator iter (mUsedIds.begin()); iter!=mUsedIds.end(); ++iter) esm.writeHNString ("USED", *iter); if (mTimeToStartDrowning) esm.writeHNT ("DRTI", mTimeToStartDrowning); if (mCrimeId != -1) esm.writeHNT ("CRID", mCrimeId); } void ESM::NpcStats::blank() { mWerewolfDeprecatedData = false; mIsWerewolf = false; mDisposition = 0; mBounty = 0; mReputation = 0; mWerewolfKills = 0; mLevelProgress = 0; for (int i=0; i<8; ++i) mSkillIncrease[i] = 0; mTimeToStartDrowning = 20; mCrimeId = -1; } openmw-openmw-0.38.0/components/esm/npcstats.hpp000066400000000000000000000017671264522266000217530ustar00rootroot00000000000000#ifndef OPENMW_ESM_NPCSTATS_H #define OPENMW_ESM_NPCSTATS_H #include #include #include #include "statstate.hpp" namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only struct NpcStats { struct Faction { bool mExpelled; int mRank; int mReputation; Faction(); }; bool mIsWerewolf; bool mWerewolfDeprecatedData; std::map mFactions; // lower case IDs int mDisposition; StatState mSkills[27]; int mBounty; int mReputation; int mWerewolfKills; int mLevelProgress; int mSkillIncrease[8]; std::vector mUsedIds; // lower case IDs float mTimeToStartDrowning; int mCrimeId; /// Initialize to default state void blank(); void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/objectstate.cpp000066400000000000000000000030531264522266000224040ustar00rootroot00000000000000#include "objectstate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::ObjectState::load (ESMReader &esm) { mVersion = esm.getFormat(); bool isDeleted; mRef.loadData(esm, isDeleted); mHasLocals = 0; esm.getHNOT (mHasLocals, "HLOC"); if (mHasLocals) mLocals.load (esm); mEnabled = 1; esm.getHNOT (mEnabled, "ENAB"); mCount = 1; esm.getHNOT (mCount, "COUN"); esm.getHNOT (mPosition, "POS_", 24); if (esm.isNextSub("LROT")) esm.skipHSub(); // local rotation, no longer used // obsolete int unused; esm.getHNOT(unused, "LTIM"); // FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files mHasCustomState = true; esm.getHNOT (mHasCustomState, "HCUS"); } void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const { mRef.save (esm, true, inInventory); if (mHasLocals) { esm.writeHNT ("HLOC", mHasLocals); mLocals.save (esm); } if (!mEnabled && !inInventory) esm.writeHNT ("ENAB", mEnabled); if (mCount!=1) esm.writeHNT ("COUN", mCount); if (!inInventory) esm.writeHNT ("POS_", mPosition, 24); if (!mHasCustomState) esm.writeHNT ("HCUS", false); } void ESM::ObjectState::blank() { mRef.blank(); mHasLocals = 0; mEnabled = false; mCount = 1; for (int i=0;i<3;++i) { mPosition.pos[i] = 0; mPosition.rot[i] = 0; } mHasCustomState = true; } ESM::ObjectState::~ObjectState() {} openmw-openmw-0.38.0/components/esm/objectstate.hpp000066400000000000000000000020321264522266000224050ustar00rootroot00000000000000#ifndef OPENMW_ESM_OBJECTSTATE_H #define OPENMW_ESM_OBJECTSTATE_H #include #include #include "cellref.hpp" #include "locals.hpp" namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only ///< \brief Save state for objects, that do not use custom data struct ObjectState { CellRef mRef; unsigned char mHasLocals; Locals mLocals; unsigned char mEnabled; int mCount; ESM::Position mPosition; // Is there any class-specific state following the ObjectState bool mHasCustomState; unsigned int mVersion; ObjectState() : mHasCustomState(true), mVersion(0) {} /// @note Does not load the CellRef ID, it should already be loaded before calling this method virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; /// Initialize to default state void blank(); virtual ~ObjectState(); }; } #endif openmw-openmw-0.38.0/components/esm/player.cpp000066400000000000000000000027721264522266000214000ustar00rootroot00000000000000#include "player.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::Player::load (ESMReader &esm) { mObject.mRef.loadId(esm, true); mObject.load (esm); mCellId.load (esm); esm.getHNT (mLastKnownExteriorPosition, "LKEP", 12); if (esm.isNextSub ("MARK")) { mHasMark = true; esm.getHT (mMarkedPosition, 24); mMarkedCell.load (esm); } else mHasMark = false; mAutoMove = 0; esm.getHNOT (mAutoMove, "AMOV"); mBirthsign = esm.getHNString ("SIGN"); mCurrentCrimeId = -1; esm.getHNOT (mCurrentCrimeId, "CURD"); mPaidCrimeId = -1; esm.getHNOT (mPaidCrimeId, "PAYD"); if (esm.hasMoreSubs()) { for (int i=0; i #include "npcstate.hpp" #include "cellid.hpp" #include "defs.hpp" #include "loadskil.hpp" #include "attr.hpp" namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only struct Player { NpcState mObject; CellId mCellId; float mLastKnownExteriorPosition[3]; unsigned char mHasMark; ESM::Position mMarkedPosition; CellId mMarkedCell; unsigned char mAutoMove; std::string mBirthsign; int mCurrentCrimeId; int mPaidCrimeId; StatState mSaveAttributes[ESM::Attribute::Length]; StatState mSaveSkills[ESM::Skill::Length]; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/projectilestate.cpp000066400000000000000000000033351264522266000233010ustar00rootroot00000000000000#include "projectilestate.hpp" #include "esmwriter.hpp" #include "esmreader.hpp" namespace ESM { void BaseProjectileState::save(ESMWriter &esm) const { esm.writeHNString ("ID__", mId); esm.writeHNT ("VEC3", mPosition); esm.writeHNT ("QUAT", mOrientation); esm.writeHNT ("ACTO", mActorId); } void BaseProjectileState::load(ESMReader &esm) { mId = esm.getHNString("ID__"); esm.getHNT (mPosition, "VEC3"); esm.getHNT (mOrientation, "QUAT"); esm.getHNT (mActorId, "ACTO"); } void MagicBoltState::save(ESMWriter &esm) const { BaseProjectileState::save(esm); esm.writeHNString ("SPEL", mSpellId); esm.writeHNString ("SRCN", mSourceName); mEffects.save(esm); esm.writeHNT ("SPED", mSpeed); esm.writeHNT ("STCK", mStack); esm.writeHNString ("SOUN", mSound); } void MagicBoltState::load(ESMReader &esm) { BaseProjectileState::load(esm); mSpellId = esm.getHNString("SPEL"); mSourceName = esm.getHNString ("SRCN"); mEffects.load(esm); esm.getHNT (mSpeed, "SPED"); esm.getHNT (mStack, "STCK"); mSound = esm.getHNString ("SOUN"); } void ProjectileState::save(ESMWriter &esm) const { BaseProjectileState::save(esm); esm.writeHNString ("BOW_", mBowId); esm.writeHNT ("VEL_", mVelocity); esm.writeHNT ("STR_", mAttackStrength); } void ProjectileState::load(ESMReader &esm) { BaseProjectileState::load(esm); mBowId = esm.getHNString ("BOW_"); esm.getHNT (mVelocity, "VEL_"); mAttackStrength = 1.f; esm.getHNOT(mAttackStrength, "STR_"); } } openmw-openmw-0.38.0/components/esm/projectilestate.hpp000066400000000000000000000020041264522266000232760ustar00rootroot00000000000000#ifndef OPENMW_ESM_PROJECTILESTATE_H #define OPENMW_ESM_PROJECTILESTATE_H #include #include #include #include "effectlist.hpp" #include "util.hpp" namespace ESM { // format 0, savegames only struct BaseProjectileState { std::string mId; Vector3 mPosition; Quaternion mOrientation; int mActorId; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; struct MagicBoltState : public BaseProjectileState { std::string mSpellId; std::string mSourceName; ESM::EffectList mEffects; float mSpeed; bool mStack; std::string mSound; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; struct ProjectileState : public BaseProjectileState { std::string mBowId; Vector3 mVelocity; float mAttackStrength; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/queststate.cpp000066400000000000000000000006241264522266000223000ustar00rootroot00000000000000#include "queststate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" void ESM::QuestState::load (ESMReader &esm) { mTopic = esm.getHNString ("YETO"); esm.getHNOT (mState, "QSTA"); esm.getHNOT (mFinished, "QFIN"); } void ESM::QuestState::save (ESMWriter &esm) const { esm.writeHNString ("YETO", mTopic); esm.writeHNT ("QSTA", mState); esm.writeHNT ("QFIN", mFinished); } openmw-openmw-0.38.0/components/esm/queststate.hpp000066400000000000000000000006201264522266000223010ustar00rootroot00000000000000#ifndef OPENMW_ESM_QUESTSTATE_H #define OPENMW_ESM_QUESTSTATE_H #include namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only struct QuestState { std::string mTopic; // lower case id int mState; unsigned char mFinished; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/quickkeys.cpp000066400000000000000000000020151264522266000221020ustar00rootroot00000000000000#include "quickkeys.hpp" #include "esmwriter.hpp" #include "esmreader.hpp" namespace ESM { void QuickKeys::load(ESMReader &esm) { if (esm.isNextSub("KEY_")) esm.getSubHeader(); // no longer used, because sub-record hierachies do not work properly in esmreader while (esm.isNextSub("TYPE")) { int keyType; esm.getHT(keyType); std::string id; id = esm.getHNString("ID__"); QuickKey key; key.mType = keyType; key.mId = id; mKeys.push_back(key); if (esm.isNextSub("KEY_")) esm.getSubHeader(); // no longer used, because sub-record hierachies do not work properly in esmreader } } void QuickKeys::save(ESMWriter &esm) const { for (std::vector::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it) { esm.writeHNT("TYPE", it->mType); esm.writeHNString("ID__", it->mId); } } } openmw-openmw-0.38.0/components/esm/quickkeys.hpp000066400000000000000000000007141264522266000221130ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_ESM_QUICKKEYS_H #define OPENMW_COMPONENTS_ESM_QUICKKEYS_H #include #include namespace ESM { class ESMReader; class ESMWriter; struct QuickKeys { struct QuickKey { int mType; std::string mId; // Spell or Item ID }; std::vector mKeys; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/records.hpp000066400000000000000000000021661264522266000215470ustar00rootroot00000000000000#ifndef OPENMW_ESM_RECORDS_H #define OPENMW_ESM_RECORDS_H #include "defs.hpp" #include "loadacti.hpp" #include "loadalch.hpp" #include "loadappa.hpp" #include "loadarmo.hpp" #include "loadbody.hpp" #include "loadbook.hpp" #include "loadbsgn.hpp" #include "loadcell.hpp" #include "loadclas.hpp" #include "loadclot.hpp" #include "loadcont.hpp" #include "loadcrea.hpp" #include "loadinfo.hpp" #include "loaddial.hpp" #include "loaddoor.hpp" #include "loadench.hpp" #include "loadfact.hpp" #include "loadglob.hpp" #include "loadgmst.hpp" #include "loadingr.hpp" #include "loadland.hpp" #include "loadlevlist.hpp" #include "loadligh.hpp" #include "loadlock.hpp" #include "loadrepa.hpp" #include "loadprob.hpp" #include "loadltex.hpp" #include "loadmgef.hpp" #include "loadmisc.hpp" #include "loadnpc.hpp" #include "loadpgrd.hpp" #include "loadrace.hpp" #include "loadregn.hpp" #include "loadscpt.hpp" #include "loadskil.hpp" #include "loadsndg.hpp" #include "loadsoun.hpp" #include "loadspel.hpp" #include "loadsscr.hpp" #include "loadstat.hpp" #include "loadweap.hpp" // Special records which are not loaded from ESM #include "attr.hpp" #endif openmw-openmw-0.38.0/components/esm/savedgame.cpp000066400000000000000000000030501264522266000220260ustar00rootroot00000000000000#include "savedgame.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; int ESM::SavedGame::sCurrentFormat = 2; void ESM::SavedGame::load (ESMReader &esm) { mPlayerName = esm.getHNString("PLNA"); esm.getHNOT (mPlayerLevel, "PLLE"); mPlayerClassId = esm.getHNOString("PLCL"); mPlayerClassName = esm.getHNOString("PLCN"); mPlayerCell = esm.getHNString("PLCE"); esm.getHNT (mInGameTime, "TSTM", 16); esm.getHNT (mTimePlayed, "TIME"); mDescription = esm.getHNString ("DESC"); while (esm.isNextSub ("DEPE")) mContentFiles.push_back (esm.getHString()); esm.getSubNameIs("SCRN"); esm.getSubHeader(); mScreenshot.resize(esm.getSubSize()); esm.getExact(&mScreenshot[0], mScreenshot.size()); } void ESM::SavedGame::save (ESMWriter &esm) const { esm.writeHNString ("PLNA", mPlayerName); esm.writeHNT ("PLLE", mPlayerLevel); if (!mPlayerClassId.empty()) esm.writeHNString ("PLCL", mPlayerClassId); else esm.writeHNString ("PLCN", mPlayerClassName); esm.writeHNString ("PLCE", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); esm.writeHNString ("DESC", mDescription); for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) esm.writeHNString ("DEPE", *iter); esm.startSubRecord("SCRN"); esm.write(&mScreenshot[0], mScreenshot.size()); esm.endRecord("SCRN"); } openmw-openmw-0.38.0/components/esm/savedgame.hpp000066400000000000000000000021121264522266000220310ustar00rootroot00000000000000#ifndef OPENMW_ESM_SAVEDGAME_H #define OPENMW_ESM_SAVEDGAME_H #include #include namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only struct SavedGame { static unsigned int sRecordId; static int sCurrentFormat; struct TimeStamp { float mGameHour; int mDay; int mMonth; int mYear; }; std::vector mContentFiles; std::string mPlayerName; int mPlayerLevel; // ID of class std::string mPlayerClassId; // Name of the class. When using a custom class, the ID is not really meaningful prior // to loading the savegame, so the name is stored separately. std::string mPlayerClassName; std::string mPlayerCell; TimeStamp mInGameTime; double mTimePlayed; std::string mDescription; std::vector mScreenshot; // raw jpg-encoded data void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/spelllist.cpp000066400000000000000000000011551264522266000221110ustar00rootroot00000000000000#include "spelllist.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { void SpellList::add(ESMReader &esm) { mList.push_back(esm.getHString()); } void SpellList::save(ESMWriter &esm) const { for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNString("NPCS", *it, 32); } } bool SpellList::exists(const std::string &spell) const { for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) if (Misc::StringUtils::ciEqual(*it, spell)) return true; return false; } } openmw-openmw-0.38.0/components/esm/spelllist.hpp000066400000000000000000000011711264522266000221140ustar00rootroot00000000000000#ifndef OPENMW_ESM_SPELLLIST_H #define OPENMW_ESM_SPELLLIST_H #include #include namespace ESM { class ESMReader; class ESMWriter; /** A list of references to spells and spell effects. This is shared between the records BSGN, NPC and RACE. NPCS subrecord. */ struct SpellList { std::vector mList; /// Is this spell ID in mList? bool exists(const std::string& spell) const; /// Load one spell, assumes the subrecord name was already read void add(ESMReader &esm); void save(ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/spellstate.cpp000066400000000000000000000067461264522266000222710ustar00rootroot00000000000000#include "spellstate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { void SpellState::load(ESMReader &esm) { while (esm.isNextSub("SPEL")) { std::string id = esm.getHString(); std::map random; while (esm.isNextSub("INDX")) { int index; esm.getHT(index); float magnitude; esm.getHNT(magnitude, "RAND"); random[index] = magnitude; } mSpells[id] = random; } while (esm.isNextSub("PERM")) { std::string spellId = esm.getHString(); std::vector permEffectList; while (esm.isNextSub("EFID")) { PermanentSpellEffectInfo info; esm.getHT(info.mId); esm.getHNT(info.mArg, "ARG_"); esm.getHNT(info.mMagnitude, "MAGN"); permEffectList.push_back(info); } mPermanentSpellEffects[spellId] = permEffectList; } while (esm.isNextSub("CORP")) { std::string id = esm.getHString(); CorprusStats stats; esm.getHNT(stats.mWorsenings, "WORS"); esm.getHNT(stats.mNextWorsening, "TIME"); mCorprusSpells[id] = stats; } while (esm.isNextSub("USED")) { std::string id = esm.getHString(); TimeStamp time; esm.getHNT(time, "TIME"); mUsedPowers[id] = time; } mSelectedSpell = esm.getHNOString("SLCT"); } void SpellState::save(ESMWriter &esm) const { for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it) { esm.writeHNString("SPEL", it->first); const std::map& random = it->second; for (std::map::const_iterator rIt = random.begin(); rIt != random.end(); ++rIt) { esm.writeHNT("INDX", rIt->first); esm.writeHNT("RAND", rIt->second); } } for (std::map >::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) { esm.writeHNString("PERM", it->first); const std::vector & effects = it->second; for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) { esm.writeHNT("EFID", effectIt->mId); esm.writeHNT("ARG_", effectIt->mArg); esm.writeHNT("MAGN", effectIt->mMagnitude); } } for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) { esm.writeHNString("CORP", it->first); const CorprusStats & stats = it->second; esm.writeHNT("WORS", stats.mWorsenings); esm.writeHNT("TIME", stats.mNextWorsening); } for (std::map::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it) { esm.writeHNString("USED", it->first); esm.writeHNT("TIME", it->second); } if (!mSelectedSpell.empty()) esm.writeHNString("SLCT", mSelectedSpell); } } openmw-openmw-0.38.0/components/esm/spellstate.hpp000066400000000000000000000017131264522266000222630ustar00rootroot00000000000000#ifndef OPENMW_ESM_SPELLSTATE_H #define OPENMW_ESM_SPELLSTATE_H #include #include #include #include "defs.hpp" namespace ESM { class ESMReader; class ESMWriter; // NOTE: spell ids must be lower case struct SpellState { struct CorprusStats { int mWorsenings; TimeStamp mNextWorsening; }; struct PermanentSpellEffectInfo { int mId; int mArg; float mMagnitude; }; typedef std::map > TContainer; TContainer mSpells; std::map > mPermanentSpellEffects; std::map mCorprusSpells; std::map mUsedPowers; std::string mSelectedSpell; void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/statstate.cpp000066400000000000000000000022631264522266000221130ustar00rootroot00000000000000#include "statstate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { template StatState::StatState() : mBase(0), mMod(0), mCurrent(0), mDamage(0), mProgress(0) {} template void StatState::load(ESMReader &esm) { esm.getHNT(mBase, "STBA"); mMod = 0; esm.getHNOT(mMod, "STMO"); mCurrent = 0; esm.getHNOT(mCurrent, "STCU"); // mDamage was changed to a float; ensure backwards compatibility T oldDamage = 0; esm.getHNOT(oldDamage, "STDA"); mDamage = static_cast(oldDamage); esm.getHNOT(mDamage, "STDF"); mProgress = 0; esm.getHNOT(mProgress, "STPR"); } template void StatState::save(ESMWriter &esm) const { esm.writeHNT("STBA", mBase); if (mMod != 0) esm.writeHNT("STMO", mMod); if (mCurrent) esm.writeHNT("STCU", mCurrent); if (mDamage) esm.writeHNT("STDF", mDamage); if (mProgress) esm.writeHNT("STPR", mProgress); } } template struct ESM::StatState; template struct ESM::StatState; openmw-openmw-0.38.0/components/esm/statstate.hpp000066400000000000000000000011051264522266000221120ustar00rootroot00000000000000#ifndef OPENMW_ESM_STATSTATE_H #define OPENMW_ESM_STATSTATE_H namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only template struct StatState { T mBase; T mMod; // Note: can either be the modifier, or the modified value. // A bit inconsistent, but we can't fix this without breaking compatibility. T mCurrent; float mDamage; float mProgress; StatState(); void load (ESMReader &esm); void save (ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/stolenitems.cpp000066400000000000000000000027611264522266000224500ustar00rootroot00000000000000#include "stolenitems.hpp" #include #include namespace ESM { void StolenItems::write(ESMWriter &esm) const { for (StolenItemsMap::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it) { esm.writeHNString("NAME", it->first); for (std::map, int>::const_iterator ownerIt = it->second.begin(); ownerIt != it->second.end(); ++ownerIt) { if (ownerIt->first.second) esm.writeHNString("FNAM", ownerIt->first.first); else esm.writeHNString("ONAM", ownerIt->first.first); esm.writeHNT("COUN", ownerIt->second); } } } void StolenItems::load(ESMReader &esm) { while (esm.isNextSub("NAME")) { std::string itemid = esm.getHString(); std::map, int> ownerMap; while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM")) { std::string subname = esm.retSubName().toString(); std::string owner = esm.getHString(); bool isFaction = (subname == "FNAM"); int count; esm.getHNT(count, "COUN"); ownerMap.insert(std::make_pair(std::make_pair(owner, isFaction), count)); } mStolenItems[itemid] = ownerMap; } } } openmw-openmw-0.38.0/components/esm/stolenitems.hpp000066400000000000000000000007501264522266000224510ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_ESM_STOLENITEMS_H #define OPENMW_COMPONENTS_ESM_STOLENITEMS_H #include #include namespace ESM { class ESMReader; class ESMWriter; // format 0, saved games only struct StolenItems { typedef std::map, int> > StolenItemsMap; StolenItemsMap mStolenItems; void load(ESM::ESMReader& esm); void write(ESM::ESMWriter& esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/transport.cpp000066400000000000000000000015231264522266000221310ustar00rootroot00000000000000#include "transport.hpp" #include #include namespace ESM { void Transport::add(ESMReader &esm) { if (esm.retSubName().val == ESM::FourCC<'D','O','D','T'>::value) { Dest dodt; esm.getHExact(&dodt.mPos, 24); mList.push_back(dodt); } else if (esm.retSubName().val == ESM::FourCC<'D','N','A','M'>::value) { mList.back().mCellName = esm.getHString(); } } void Transport::save(ESMWriter &esm) const { typedef std::vector::const_iterator DestIter; for (DestIter it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("DODT", it->mPos, sizeof(it->mPos)); esm.writeHNOCString("DNAM", it->mCellName); } } } openmw-openmw-0.38.0/components/esm/transport.hpp000066400000000000000000000011561264522266000221400ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_ESM_TRANSPORT_H #define OPENMW_COMPONENTS_ESM_TRANSPORT_H #include #include #include "defs.hpp" namespace ESM { class ESMReader; class ESMWriter; /// List of travel service destination. Shared by CREA and NPC_ records. struct Transport { struct Dest { Position mPos; std::string mCellName; }; std::vector mList; /// Load one destination, assumes the subrecord name was already read void add(ESMReader &esm); void save(ESMWriter &esm) const; }; } #endif openmw-openmw-0.38.0/components/esm/util.hpp000066400000000000000000000014351264522266000210610ustar00rootroot00000000000000#ifndef OPENMW_ESM_UTIL_H #define OPENMW_ESM_UTIL_H #include #include namespace ESM { // format 0, savegames only struct Quaternion { float mValues[4]; Quaternion() {} Quaternion(const osg::Quat& q) { mValues[0] = q.w(); mValues[1] = q.x(); mValues[2] = q.y(); mValues[3] = q.z(); } operator osg::Quat () const { return osg::Quat(mValues[1], mValues[2], mValues[3], mValues[0]); } }; struct Vector3 { float mValues[3]; Vector3() {} Vector3(const osg::Vec3f& v) { mValues[0] = v.x(); mValues[1] = v.y(); mValues[2] = v.z(); } operator osg::Vec3f () const { return osg::Vec3f(mValues[0], mValues[1], mValues[2]); } }; } #endif openmw-openmw-0.38.0/components/esm/variant.cpp000066400000000000000000000156301264522266000215450ustar00rootroot00000000000000#include "variant.hpp" #include #include #include "esmreader.hpp" #include "variantimp.hpp" #include "defs.hpp" namespace { const uint32_t STRV = ESM::FourCC<'S','T','R','V'>::value; const uint32_t INTV = ESM::FourCC<'I','N','T','V'>::value; const uint32_t FLTV = ESM::FourCC<'F','L','T','V'>::value; const uint32_t STTV = ESM::FourCC<'S','T','T','V'>::value; } ESM::Variant::Variant() : mType (VT_None), mData (0) {} ESM::Variant::Variant(const std::string &value) { mData = 0; mType = VT_None; setType(VT_String); setString(value); } ESM::Variant::Variant(int value) { mData = 0; mType = VT_None; setType(VT_Long); setInteger(value); } ESM::Variant::Variant(float value) { mData = 0; mType = VT_None; setType(VT_Float); setFloat(value); } ESM::Variant::~Variant() { delete mData; } ESM::Variant& ESM::Variant::operator= (const Variant& variant) { if (&variant!=this) { VariantDataBase *newData = variant.mData ? variant.mData->clone() : 0; delete mData; mType = variant.mType; mData = newData; } return *this; } ESM::Variant::Variant (const Variant& variant) : mType (variant.mType), mData (variant.mData ? variant.mData->clone() : 0) {} ESM::VarType ESM::Variant::getType() const { return mType; } std::string ESM::Variant::getString() const { if (!mData) throw std::runtime_error ("can not convert empty variant to string"); return mData->getString(); } int ESM::Variant::getInteger() const { if (!mData) throw std::runtime_error ("can not convert empty variant to integer"); return mData->getInteger(); } float ESM::Variant::getFloat() const { if (!mData) throw std::runtime_error ("can not convert empty variant to float"); return mData->getFloat(); } void ESM::Variant::read (ESMReader& esm, Format format) { // type VarType type = VT_Unknown; if (format==Format_Global) { std::string typeId = esm.getHNString ("FNAM"); if (typeId == "s") type = VT_Short; else if (typeId == "l") type = VT_Long; else if (typeId == "f") type = VT_Float; else esm.fail ("illegal global variable type " + typeId); } else if (format==Format_Gmst) { if (!esm.hasMoreSubs()) { type = VT_None; } else { esm.getSubName(); NAME name = esm.retSubName(); if (name==STRV) { type = VT_String; } else if (name==INTV) { type = VT_Int; } else if (name==FLTV) { type = VT_Float; } else esm.fail ("invalid subrecord: " + name.toString()); } } else if (format == Format_Info) { esm.getSubName(); NAME name = esm.retSubName(); if (name==INTV) { type = VT_Int; } else if (name==FLTV) { type = VT_Float; } else esm.fail ("invalid subrecord: " + name.toString()); } else if (format == Format_Local) { esm.getSubName(); NAME name = esm.retSubName(); if (name==INTV) { type = VT_Int; } else if (name==FLTV) { type = VT_Float; } else if (name==STTV) { type = VT_Short; } else esm.fail ("invalid subrecord: " + name.toString()); } setType (type); // data if (mData) mData->read (esm, format, mType); } void ESM::Variant::write (ESMWriter& esm, Format format) const { if (mType==VT_Unknown) { throw std::runtime_error ("can not serialise variant of unknown type"); } else if (mType==VT_None) { if (format==Format_Global) throw std::runtime_error ("can not serialise variant of type none to global format"); if (format==Format_Info) throw std::runtime_error ("can not serialise variant of type none to info format"); if (format==Format_Local) throw std::runtime_error ("can not serialise variant of type none to local format"); // nothing to do here for GMST format } else mData->write (esm, format, mType); } void ESM::Variant::write (std::ostream& stream) const { switch (mType) { case VT_Unknown: stream << "variant unknown"; break; case VT_None: stream << "variant none"; break; case VT_Short: stream << "variant short: " << mData->getInteger(); break; case VT_Int: stream << "variant int: " << mData->getInteger(); break; case VT_Long: stream << "variant long: " << mData->getInteger(); break; case VT_Float: stream << "variant float: " << mData->getFloat(); break; case VT_String: stream << "variant string: \"" << mData->getString() << "\""; break; } } void ESM::Variant::setType (VarType type) { if (type!=mType) { VariantDataBase *newData = 0; switch (type) { case VT_Unknown: case VT_None: break; // no data case VT_Short: case VT_Int: case VT_Long: newData = new VariantIntegerData (mData); break; case VT_Float: newData = new VariantFloatData (mData); break; case VT_String: newData = new VariantStringData (mData); break; } delete mData; mData = newData; mType = type; } } void ESM::Variant::setString (const std::string& value) { if (!mData) throw std::runtime_error ("can not assign string to empty variant"); mData->setString (value); } void ESM::Variant::setInteger (int value) { if (!mData) throw std::runtime_error ("can not assign integer to empty variant"); mData->setInteger (value); } void ESM::Variant::setFloat (float value) { if (!mData) throw std::runtime_error ("can not assign float to empty variant"); mData->setFloat (value); } bool ESM::Variant::isEqual (const Variant& value) const { if (mType!=value.mType) return false; if (!mData) return true; assert (value.mData); return mData->isEqual (*value.mData); } std::ostream& ESM::operator<< (std::ostream& stream, const Variant& value) { value.write (stream); return stream; } bool ESM::operator== (const Variant& left, const Variant& right) { return left.isEqual (right); } bool ESM::operator!= (const Variant& left, const Variant& right) { return !(left==right); } openmw-openmw-0.38.0/components/esm/variant.hpp000066400000000000000000000045231264522266000215510ustar00rootroot00000000000000#ifndef OPENMW_ESM_VARIANT_H #define OPENMW_ESM_VARIANT_H #include #include namespace ESM { class ESMReader; class ESMWriter; enum VarType { VT_Unknown = 0, VT_None, VT_Short, // stored as a float, kinda VT_Int, VT_Long, // stored as a float VT_Float, VT_String }; class VariantDataBase; class Variant { VarType mType; VariantDataBase *mData; public: enum Format { Format_Global, Format_Gmst, Format_Info, Format_Local // local script variables in save game files }; Variant(); Variant (const std::string& value); Variant (int value); Variant (float value); ~Variant(); Variant& operator= (const Variant& variant); Variant (const Variant& variant); VarType getType() const; std::string getString() const; ///< Will throw an exception, if value can not be represented as a string. int getInteger() const; ///< Will throw an exception, if value can not be represented as an integer (implicit /// casting of float values is permitted). float getFloat() const; ///< Will throw an exception, if value can not be represented as a float value. void read (ESMReader& esm, Format format); void write (ESMWriter& esm, Format format) const; void write (std::ostream& stream) const; ///< Write in text format. void setType (VarType type); void setString (const std::string& value); ///< Will throw an exception, if type is not compatible with string. void setInteger (int value); ///< Will throw an exception, if type is not compatible with integer. void setFloat (float value); ///< Will throw an exception, if type is not compatible with float. bool isEqual (const Variant& value) const; }; std::ostream& operator<<(std::ostream& stream, const Variant& value); bool operator== (const Variant& left, const Variant& right); bool operator!= (const Variant& left, const Variant& right); } #endif openmw-openmw-0.38.0/components/esm/variantimp.cpp000066400000000000000000000171571264522266000222610ustar00rootroot00000000000000#include "variantimp.hpp" #include #include "esmreader.hpp" #include "esmwriter.hpp" ESM::VariantDataBase::~VariantDataBase() {} std::string ESM::VariantDataBase::getString (bool default_) const { if (default_) return ""; throw std::runtime_error ("can not convert variant to string"); } int ESM::VariantDataBase::getInteger (bool default_) const { if (default_) return 0; throw std::runtime_error ("can not convert variant to integer"); } float ESM::VariantDataBase::getFloat (bool default_) const { if (default_) return 0; throw std::runtime_error ("can not convert variant to float"); } void ESM::VariantDataBase::setString (const std::string& value) { throw std::runtime_error ("conversion of string to variant not possible"); } void ESM::VariantDataBase::setInteger (int value) { throw std::runtime_error ("conversion of integer to variant not possible"); } void ESM::VariantDataBase::setFloat (float value) { throw std::runtime_error ("conversion of float to variant not possible"); } ESM::VariantStringData::VariantStringData (const VariantDataBase *data) { if (data) mValue = data->getString (true); } ESM::VariantDataBase *ESM::VariantStringData::clone() const { return new VariantStringData (*this); } std::string ESM::VariantStringData::getString (bool default_) const { return mValue; } void ESM::VariantStringData::setString (const std::string& value) { mValue = value; } void ESM::VariantStringData::read (ESMReader& esm, Variant::Format format, VarType type) { if (type!=VT_String) throw std::logic_error ("not a string type"); if (format==Variant::Format_Global) esm.fail ("global variables of type string not supported"); if (format==Variant::Format_Info) esm.fail ("info variables of type string not supported"); if (format==Variant::Format_Local) esm.fail ("local variables of type string not supported"); // GMST mValue = esm.getHString(); } void ESM::VariantStringData::write (ESMWriter& esm, Variant::Format format, VarType type) const { if (type!=VT_String) throw std::logic_error ("not a string type"); if (format==Variant::Format_Global) throw std::runtime_error ("global variables of type string not supported"); if (format==Variant::Format_Info) throw std::runtime_error ("info variables of type string not supported"); // GMST esm.writeHNString ("STRV", mValue); } bool ESM::VariantStringData::isEqual (const VariantDataBase& value) const { return dynamic_cast (value).mValue==mValue; } ESM::VariantIntegerData::VariantIntegerData (const VariantDataBase *data) : mValue (0) { if (data) mValue = data->getInteger (true); } ESM::VariantDataBase *ESM::VariantIntegerData::clone() const { return new VariantIntegerData (*this); } int ESM::VariantIntegerData::getInteger (bool default_) const { return mValue; } float ESM::VariantIntegerData::getFloat (bool default_) const { return static_cast(mValue); } void ESM::VariantIntegerData::setInteger (int value) { mValue = value; } void ESM::VariantIntegerData::setFloat (float value) { mValue = static_cast (value); } void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarType type) { if (type!=VT_Short && type!=VT_Long && type!=VT_Int) throw std::logic_error ("not an integer type"); if (format==Variant::Format_Global) { float value; esm.getHNT (value, "FLTV"); if (type==VT_Short) { if (value!=value) mValue = 0; // nan else mValue = static_cast (value); } else if (type==VT_Long) mValue = static_cast (value); else esm.fail ("unsupported global variable integer type"); } else if (format==Variant::Format_Gmst || format==Variant::Format_Info) { if (type!=VT_Int) { std::ostringstream stream; stream << "unsupported " <<(format==Variant::Format_Gmst ? "gmst" : "info") << " variable integer type"; esm.fail (stream.str()); } esm.getHT (mValue); } else if (format==Variant::Format_Local) { if (type==VT_Short) { short value; esm.getHT(value); mValue = value; } else if (type==VT_Int) { esm.getHT(mValue); } else esm.fail("unsupported local variable integer type"); } } void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, VarType type) const { if (type!=VT_Short && type!=VT_Long && type!=VT_Int) throw std::logic_error ("not an integer type"); if (format==Variant::Format_Global) { if (type==VT_Short || type==VT_Long) { float value = static_cast(mValue); esm.writeHNString ("FNAM", type==VT_Short ? "s" : "l"); esm.writeHNT ("FLTV", value); } else throw std::runtime_error ("unsupported global variable integer type"); } else if (format==Variant::Format_Gmst || format==Variant::Format_Info) { if (type!=VT_Int) { std::ostringstream stream; stream << "unsupported " <<(format==Variant::Format_Gmst ? "gmst" : "info") << " variable integer type"; throw std::runtime_error (stream.str()); } esm.writeHNT ("INTV", mValue); } else if (format==Variant::Format_Local) { if (type==VT_Short) esm.writeHNT ("STTV", (short)mValue); else if (type == VT_Int) esm.writeHNT ("INTV", mValue); else throw std::runtime_error("unsupported local variable integer type"); } } bool ESM::VariantIntegerData::isEqual (const VariantDataBase& value) const { return dynamic_cast (value).mValue==mValue; } ESM::VariantFloatData::VariantFloatData (const VariantDataBase *data) : mValue (0) { if (data) mValue = data->getFloat (true); } ESM::VariantDataBase *ESM::VariantFloatData::clone() const { return new VariantFloatData (*this); } int ESM::VariantFloatData::getInteger (bool default_) const { return static_cast (mValue); } float ESM::VariantFloatData::getFloat (bool default_) const { return mValue; } void ESM::VariantFloatData::setInteger (int value) { mValue = static_cast(value); } void ESM::VariantFloatData::setFloat (float value) { mValue = value; } void ESM::VariantFloatData::read (ESMReader& esm, Variant::Format format, VarType type) { if (type!=VT_Float) throw std::logic_error ("not a float type"); if (format==Variant::Format_Global) { esm.getHNT (mValue, "FLTV"); } else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local) { esm.getHT (mValue); } } void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarType type) const { if (type!=VT_Float) throw std::logic_error ("not a float type"); if (format==Variant::Format_Global) { esm.writeHNString ("FNAM", "f"); esm.writeHNT ("FLTV", mValue); } else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local) { esm.writeHNT ("FLTV", mValue); } } bool ESM::VariantFloatData::isEqual (const VariantDataBase& value) const { return dynamic_cast (value).mValue==mValue; } openmw-openmw-0.38.0/components/esm/variantimp.hpp000066400000000000000000000166201264522266000222600ustar00rootroot00000000000000#ifndef OPENMW_ESM_VARIANTIMP_H #define OPENMW_ESM_VARIANTIMP_H #include #include "variant.hpp" namespace ESM { class VariantDataBase { public: virtual ~VariantDataBase(); virtual VariantDataBase *clone() const = 0; virtual std::string getString (bool default_ = false) const; ///< Will throw an exception, if value can not be represented as a string. /// /// \note Numeric values are not converted to strings. /// /// \param default_ Return a default value instead of throwing an exception. /// /// Default-implementation: throw an exception. virtual int getInteger (bool default_ = false) const; ///< Will throw an exception, if value can not be represented as an integer (implicit /// casting of float values is permitted). /// /// \param default_ Return a default value instead of throwing an exception. /// /// Default-implementation: throw an exception. virtual float getFloat (bool default_ = false) const; ///< Will throw an exception, if value can not be represented as a float value. /// /// \param default_ Return a default value instead of throwing an exception. /// /// Default-implementation: throw an exception. virtual void setString (const std::string& value); ///< Will throw an exception, if type is not compatible with string. /// /// Default-implementation: throw an exception. virtual void setInteger (int value); ///< Will throw an exception, if type is not compatible with integer. /// /// Default-implementation: throw an exception. virtual void setFloat (float value); ///< Will throw an exception, if type is not compatible with float. /// /// Default-implementation: throw an exception. virtual void read (ESMReader& esm, Variant::Format format, VarType type) = 0; ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const = 0; ///< If \a type is not supported by \a format, an exception is thrown. virtual bool isEqual (const VariantDataBase& value) const = 0; ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. }; class VariantStringData : public VariantDataBase { std::string mValue; public: VariantStringData (const VariantDataBase *data = 0); ///< Calling the constructor with an incompatible data type will result in a silent /// default initialisation. virtual VariantDataBase *clone() const; virtual std::string getString (bool default_ = false) const; ///< Will throw an exception, if value can not be represented as a string. /// /// \note Numeric values are not converted to strings. /// /// \param default_ Return a default value instead of throwing an exception. virtual void setString (const std::string& value); ///< Will throw an exception, if type is not compatible with string. virtual void read (ESMReader& esm, Variant::Format format, VarType type); ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const; ///< If \a type is not supported by \a format, an exception is thrown. virtual bool isEqual (const VariantDataBase& value) const; ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. }; class VariantIntegerData : public VariantDataBase { int mValue; public: VariantIntegerData (const VariantDataBase *data = 0); ///< Calling the constructor with an incompatible data type will result in a silent /// default initialisation. virtual VariantDataBase *clone() const; virtual int getInteger (bool default_ = false) const; ///< Will throw an exception, if value can not be represented as an integer (implicit /// casting of float values is permitted). /// /// \param default_ Return a default value instead of throwing an exception. virtual float getFloat (bool default_ = false) const; ///< Will throw an exception, if value can not be represented as a float value. /// /// \param default_ Return a default value instead of throwing an exception. virtual void setInteger (int value); ///< Will throw an exception, if type is not compatible with integer. virtual void setFloat (float value); ///< Will throw an exception, if type is not compatible with float. virtual void read (ESMReader& esm, Variant::Format format, VarType type); ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const; ///< If \a type is not supported by \a format, an exception is thrown. virtual bool isEqual (const VariantDataBase& value) const; ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. }; class VariantFloatData : public VariantDataBase { float mValue; public: VariantFloatData (const VariantDataBase *data = 0); ///< Calling the constructor with an incompatible data type will result in a silent /// default initialisation. virtual VariantDataBase *clone() const; virtual int getInteger (bool default_ = false) const; ///< Will throw an exception, if value can not be represented as an integer (implicit /// casting of float values is permitted). /// /// \param default_ Return a default value instead of throwing an exception. virtual float getFloat (bool default_ = false) const; ///< Will throw an exception, if value can not be represented as a float value. /// /// \param default_ Return a default value instead of throwing an exception. virtual void setInteger (int value); ///< Will throw an exception, if type is not compatible with integer. virtual void setFloat (float value); ///< Will throw an exception, if type is not compatible with float. virtual void read (ESMReader& esm, Variant::Format format, VarType type); ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const; ///< If \a type is not supported by \a format, an exception is thrown. virtual bool isEqual (const VariantDataBase& value) const; ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. }; } #endif openmw-openmw-0.38.0/components/esm/weatherstate.cpp000066400000000000000000000053441264522266000226020ustar00rootroot00000000000000#include "weatherstate.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" namespace { const char* currentRegionRecord = "CREG"; const char* timePassedRecord = "TMPS"; const char* fastForwardRecord = "FAST"; const char* weatherUpdateTimeRecord = "WUPD"; const char* transitionFactorRecord = "TRFC"; const char* currentWeatherRecord = "CWTH"; const char* nextWeatherRecord = "NWTH"; const char* queuedWeatherRecord = "QWTH"; const char* regionNameRecord = "RGNN"; const char* regionWeatherRecord = "RGNW"; const char* regionChanceRecord = "RGNC"; } namespace ESM { void WeatherState::load(ESMReader& esm) { mCurrentRegion = esm.getHNString(currentRegionRecord); esm.getHNT(mTimePassed, timePassedRecord); esm.getHNT(mFastForward, fastForwardRecord); esm.getHNT(mWeatherUpdateTime, weatherUpdateTimeRecord); esm.getHNT(mTransitionFactor, transitionFactorRecord); esm.getHNT(mCurrentWeather, currentWeatherRecord); esm.getHNT(mNextWeather, nextWeatherRecord); esm.getHNT(mQueuedWeather, queuedWeatherRecord); while(esm.peekNextSub(regionNameRecord)) { std::string regionID = esm.getHNString(regionNameRecord); RegionWeatherState region; esm.getHNT(region.mWeather, regionWeatherRecord); while(esm.peekNextSub(regionChanceRecord)) { char chance; esm.getHNT(chance, regionChanceRecord); region.mChances.push_back(chance); } mRegions.insert(std::make_pair(regionID, region)); } } void WeatherState::save(ESMWriter& esm) const { esm.writeHNCString(currentRegionRecord, mCurrentRegion.c_str()); esm.writeHNT(timePassedRecord, mTimePassed); esm.writeHNT(fastForwardRecord, mFastForward); esm.writeHNT(weatherUpdateTimeRecord, mWeatherUpdateTime); esm.writeHNT(transitionFactorRecord, mTransitionFactor); esm.writeHNT(currentWeatherRecord, mCurrentWeather); esm.writeHNT(nextWeatherRecord, mNextWeather); esm.writeHNT(queuedWeatherRecord, mQueuedWeather); std::map::const_iterator it = mRegions.begin(); for(; it != mRegions.end(); ++it) { esm.writeHNCString(regionNameRecord, it->first.c_str()); esm.writeHNT(regionWeatherRecord, it->second.mWeather); for(size_t i = 0; i < it->second.mChances.size(); ++i) { esm.writeHNT(regionChanceRecord, it->second.mChances[i]); } } } } openmw-openmw-0.38.0/components/esm/weatherstate.hpp000066400000000000000000000013461264522266000226050ustar00rootroot00000000000000#ifndef OPENMW_ESM_WEATHERSTATE_H #define OPENMW_ESM_WEATHERSTATE_H #include #include #include namespace ESM { class ESMReader; class ESMWriter; struct RegionWeatherState { int mWeather; std::vector mChances; }; struct WeatherState { std::string mCurrentRegion; float mTimePassed; bool mFastForward; float mWeatherUpdateTime; float mTransitionFactor; int mCurrentWeather; int mNextWeather; int mQueuedWeather; std::map mRegions; void load(ESMReader& esm); void save(ESMWriter& esm) const; }; } #endif openmw-openmw-0.38.0/components/esmterrain/000077500000000000000000000000001264522266000207555ustar00rootroot00000000000000openmw-openmw-0.38.0/components/esmterrain/storage.cpp000066400000000000000000000506421264522266000231340ustar00rootroot00000000000000#include "storage.hpp" #include #include #include #include #include #include #include namespace ESMTerrain { Storage::Storage(const VFS::Manager *vfs) : mVFS(vfs) { } const ESM::Land::LandData *Storage::getLandData (int cellX, int cellY, int flags) { if (const ESM::Land *land = getLand (cellX, cellY)) return land->getLandData (flags); return 0; } bool Storage::getMinMaxHeights(float size, const osg::Vec2f ¢er, float &min, float &max) { assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); int cellX = static_cast(std::floor(origin.x())); int cellY = static_cast(std::floor(origin.y())); int startRow = (origin.x() - cellX) * ESM::Land::LAND_SIZE; int startColumn = (origin.y() - cellY) * ESM::Land::LAND_SIZE; int endRow = startRow + size * (ESM::Land::LAND_SIZE-1) + 1; int endColumn = startColumn + size * (ESM::Land::LAND_SIZE-1) + 1; if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VHGT)) { min = std::numeric_limits::max(); max = -std::numeric_limits::max(); for (int row=startRow; rowmHeights[col*ESM::Land::LAND_SIZE+row]; if (h > max) max = h; if (h < min) min = h; } } return true; } return false; } void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row) { while (col >= ESM::Land::LAND_SIZE-1) { ++cellY; col -= ESM::Land::LAND_SIZE-1; } while (row >= ESM::Land::LAND_SIZE-1) { ++cellX; row -= ESM::Land::LAND_SIZE-1; } while (col < 0) { --cellY; col += ESM::Land::LAND_SIZE-1; } while (row < 0) { --cellX; row += ESM::Land::LAND_SIZE-1; } if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VNML)) { normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; normal.normalize(); } else normal = osg::Vec3f(0,0,1); } void Storage::averageNormal(osg::Vec3f &normal, int cellX, int cellY, int col, int row) { osg::Vec3f n1,n2,n3,n4; fixNormal(n1, cellX, cellY, col+1, row); fixNormal(n2, cellX, cellY, col-1, row); fixNormal(n3, cellX, cellY, col, row+1); fixNormal(n4, cellX, cellY, col, row-1); normal = (n1+n2+n3+n4); normal.normalize(); } void Storage::fixColour (osg::Vec4f& color, int cellX, int cellY, int col, int row) { if (col == ESM::Land::LAND_SIZE-1) { ++cellY; col = 0; } if (row == ESM::Land::LAND_SIZE-1) { ++cellX; row = 0; } if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VCLR)) { color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; } else { color.r() = 1; color.g() = 1; color.b() = 1; } } void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr positions, osg::ref_ptr normals, osg::ref_ptr colours) { // LOD level n means every 2^n-th vertex is kept size_t increment = 1 << lodLevel; osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); int startCellX = static_cast(std::floor(origin.x())); int startCellY = static_cast(std::floor(origin.y())); size_t numVerts = static_cast(size*(ESM::Land::LAND_SIZE - 1) / increment + 1); positions->resize(numVerts*numVerts); normals->resize(numVerts*numVerts); colours->resize(numVerts*numVerts); osg::Vec3f normal; osg::Vec4f color; float vertY = 0; float vertX = 0; float vertY_ = 0; // of current cell corner for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY) { float vertX_ = 0; // of current cell corner for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) { const ESM::Land::LandData *heightData = getLandData (cellX, cellY, ESM::Land::DATA_VHGT); const ESM::Land::LandData *normalData = getLandData (cellX, cellY, ESM::Land::DATA_VNML); const ESM::Land::LandData *colourData = getLandData (cellX, cellY, ESM::Land::DATA_VCLR); int rowStart = 0; int colStart = 0; // Skip the first row / column unless we're at a chunk edge, // since this row / column is already contained in a previous cell // This is only relevant if we're creating a chunk spanning multiple cells if (colStart == 0 && vertY_ != 0) colStart += increment; if (rowStart == 0 && vertX_ != 0) rowStart += increment; // Only relevant for chunks smaller than (contained in) one cell rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE; colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE; int rowEnd = rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1; int colEnd = colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1; vertY = vertY_; for (int col=colStart; col= 0 && row < ESM::Land::LAND_SIZE); assert(col >= 0 && col < ESM::Land::LAND_SIZE); assert (vertX < numVerts); assert (vertY < numVerts); float height = -2048; if (heightData) height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row]; (*positions)[static_cast(vertX*numVerts + vertY)] = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * 8192, (vertY / float(numVerts - 1) - 0.5f) * size * 8192, height); if (normalData) { for (int i=0; i<3; ++i) normal[i] = normalData->mNormals[srcArrayIndex+i]; normal.normalize(); } else normal = osg::Vec3f(0,0,1); // Normals apparently don't connect seamlessly between cells if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) fixNormal(normal, cellX, cellY, col, row); // some corner normals appear to be complete garbage (z < 0) if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) averageNormal(normal, cellX, cellY, col, row); assert(normal.z() > 0); (*normals)[static_cast(vertX*numVerts + vertY)] = normal; if (colourData) { for (int i=0; i<3; ++i) color[i] = colourData->mColours[srcArrayIndex+i] / 255.f; } else { color.r() = 1; color.g() = 1; color.b() = 1; } // Unlike normals, colors mostly connect seamlessly between cells, but not always... if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) fixColour(color, cellX, cellY, col, row); color.a() = 1; (*colours)[static_cast(vertX*numVerts + vertY)] = color; ++vertX; } ++vertY; } vertX_ = vertX; } vertY_ = vertY; assert(vertX_ == numVerts); // Ensure we covered whole area } assert(vertY_ == numVerts); // Ensure we covered whole area } Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, int x, int y) { // For the first/last row/column, we need to get the texture from the neighbour cell // to get consistent blending at the borders --x; if (x < 0) { --cellX; x += ESM::Land::LAND_TEXTURE_SIZE; } if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? { ++cellY; y -= ESM::Land::LAND_TEXTURE_SIZE; } assert(xmTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; if (tex == 0) return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin return std::make_pair(tex, getLand (cellX, cellY)->mPlugin); } else return std::make_pair(0,0); } std::string Storage::getTextureName(UniqueTextureId id) { static const std::string defaultTexture = "textures\\_land_default.dds"; if (id.first == 0) return defaultTexture; // Not sure if the default texture really is hardcoded? // NB: All vtex ids are +1 compared to the ltex ids const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); if (!ltex) { std::cerr << "Unable to find land texture index " << id.first-1 << " in plugin " << id.second << ", using default texture instead" << std::endl; return defaultTexture; } // this is needed due to MWs messed up texture handling std::string texture = Misc::ResourceHelpers::correctTexturePath(ltex->mTexture, mVFS); return texture; } void Storage::getBlendmaps(float chunkSize, const osg::Vec2f &chunkCenter, bool pack, ImageVector &blendmaps, std::vector &layerList) { // TODO - blending isn't completely right yet; the blending radius appears to be // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap // and interpolate the rest of the cell by hand? :/ osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize/2.f, chunkSize/2.f); int cellX = static_cast(std::floor(origin.x())); int cellY = static_cast(std::floor(origin.y())); int realTextureSize = ESM::Land::LAND_TEXTURE_SIZE+1; // add 1 to wrap around next cell int rowStart = (origin.x() - cellX) * realTextureSize; int colStart = (origin.y() - cellY) * realTextureSize; int rowEnd = rowStart + chunkSize * (realTextureSize-1) + 1; int colEnd = colStart + chunkSize * (realTextureSize-1) + 1; assert (rowStart >= 0 && colStart >= 0); assert (rowEnd <= realTextureSize); assert (colEnd <= realTextureSize); // Save the used texture indices so we know the total number of textures // and number of required blend maps std::set textureIndices; // Due to the way the blending works, the base layer will always shine through in between // blend transitions (eg halfway between two texels, both blend values will be 0.5, so 25% of base layer visible). // To get a consistent look, we need to make sure to use the same base layer in all cells. // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell. textureIndices.insert(std::make_pair(0,0)); for (int y=colStart; y textureIndicesMap; for (std::set::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it) { int size = textureIndicesMap.size(); textureIndicesMap[*it] = size; layerList.push_back(getLayerInfo(getTextureName(*it))); } int numTextures = textureIndices.size(); // numTextures-1 since the base layer doesn't need blending int numBlendmaps = pack ? static_cast(std::ceil((numTextures - 1) / 4.f)) : (numTextures - 1); int channels = pack ? 4 : 1; // Second iteration - create and fill in the blend maps const int blendmapSize = (realTextureSize-1) * chunkSize + 1; for (int i=0; i image (new osg::Image); image->allocateImage(blendmapSize, blendmapSize, 1, format, GL_UNSIGNED_BYTE); unsigned char* pData = image->data(); for (int y=0; ysecond; int blendIndex = (pack ? static_cast(std::floor((layerIndex - 1) / 4.f)) : layerIndex - 1); int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0; if (blendIndex == i) pData[y*blendmapSize*channels + x*channels + channel] = 255; else pData[y*blendmapSize*channels + x*channels + channel] = 0; } } blendmaps.push_back(image); } } float Storage::getHeightAt(const osg::Vec3f &worldPos) { int cellX = static_cast(std::floor(worldPos.x() / 8192.f)); int cellY = static_cast(std::floor(worldPos.y() / 8192.f)); const ESM::Land* land = getLand(cellX, cellY); if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) return -2048; // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Normalized position in the cell float nX = (worldPos.x() - (cellX * 8192))/8192.f; float nY = (worldPos.y() - (cellY * 8192))/8192.f; // get left / bottom points (rounded down) float factor = ESM::Land::LAND_SIZE - 1.0f; float invFactor = 1.0f / factor; int startX = static_cast(nX * factor); int startY = static_cast(nY * factor); int endX = startX + 1; int endY = startY + 1; endX = std::min(endX, ESM::Land::LAND_SIZE-1); endY = std::min(endY, ESM::Land::LAND_SIZE-1); // now get points in terrain space (effectively rounding them to boundaries) float startXTS = startX * invFactor; float startYTS = startY * invFactor; float endXTS = endX * invFactor; float endYTS = endY * invFactor; // get parametric from start coord to next point float xParam = (nX - startXTS) * factor; float yParam = (nY - startYTS) * factor; /* For even / odd tri strip rows, triangles are this shape: even odd 3---2 3---2 | / | | \ | 0---1 0---1 */ // Build all 4 positions in normalized cell space, using point-sampled height osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); // define this plane in terrain space osg::Plane plane; // FIXME: deal with differing triangle alignment if (true) { // odd row bool secondTri = ((1.0 - yParam) > xParam); if (secondTri) plane = osg::Plane(v0, v1, v3); else plane = osg::Plane(v1, v2, v3); } /* else { // even row bool secondTri = (yParam > xParam); if (secondTri) plane.redefine(v0, v2, v3); else plane.redefine(v0, v1, v2); } */ // Solve plane equation for z return (-plane.getNormal().x() * nX -plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z() * 8192; } float Storage::getVertexHeight(const ESM::Land *land, int x, int y) { assert(x < ESM::Land::LAND_SIZE); assert(y < ESM::Land::LAND_SIZE); return land->getLandData()->mHeights[y * ESM::Land::LAND_SIZE + x]; } Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) { // Already have this cached? std::map::iterator found = mLayerInfoMap.find(texture); if (found != mLayerInfoMap.end()) return found->second; Terrain::LayerInfo info; info.mParallax = false; info.mSpecular = false; info.mDiffuseMap = texture; std::string texture_ = texture; boost::replace_last(texture_, ".", "_nh."); if (mVFS->exists(texture_)) { info.mNormalMap = texture_; info.mParallax = true; } else { texture_ = texture; boost::replace_last(texture_, ".", "_n."); if (mVFS->exists(texture_)) info.mNormalMap = texture_; } texture_ = texture; boost::replace_last(texture_, ".", "_diffusespec."); if (mVFS->exists(texture_)) { info.mDiffuseMap = texture_; info.mSpecular = true; } mLayerInfoMap[texture] = info; return info; } Terrain::LayerInfo Storage::getDefaultLayer() { Terrain::LayerInfo info; info.mDiffuseMap = "textures\\_land_default.dds"; info.mParallax = false; info.mSpecular = false; return info; } float Storage::getCellWorldSize() { return static_cast(ESM::Land::REAL_SIZE); } int Storage::getCellVertices() { return ESM::Land::LAND_SIZE; } } openmw-openmw-0.38.0/components/esmterrain/storage.hpp000066400000000000000000000127041264522266000231360ustar00rootroot00000000000000#ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H #define COMPONENTS_ESM_TERRAIN_STORAGE_H #include #include #include namespace VFS { class Manager; } namespace ESMTerrain { /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) /// into the terrain component, converting it on the fly as needed. class Storage : public Terrain::Storage { private: // Not implemented in this class, because we need different Store implementations for game and editor virtual const ESM::Land* getLand (int cellX, int cellY)= 0; virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; public: Storage(const VFS::Manager* vfs); /// Data is loaded first, if necessary. Will return a 0-pointer if there is no data for /// any of the data types specified via \a flags. Will also return a 0-pointer if there /// is no land record for the coordinates \a cellX / \a cellY. const ESM::Land::LandData *getLandData (int cellX, int cellY, int flags); // Not implemented in this class, because we need different Store implementations for game and editor /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; /// Get the minimum and maximum heights of a terrain region. /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree. /// Larger chunks can simply merge AABB of children. /// @param size size of the chunk in cell units /// @param center center of the chunk in cell units /// @param min min height will be stored here /// @param max max height will be stored here /// @return true if there was data available for this terrain chunk virtual bool getMinMaxHeights (float size, const osg::Vec2f& center, float& min, float& max); /// Fill vertex buffers for a terrain chunk. /// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @note Vertices should be written in row-major order (a row is defined as parallel to the x-axis). /// The specified positions should be in local space, i.e. relative to the center of the terrain chunk. /// @param lodLevel LOD level, 0 = most detailed /// @param size size of the terrain chunk in cell units /// @param center center of the chunk in cell units /// @param positions buffer to write vertices /// @param normals buffer to write vertex normals /// @param colours buffer to write vertex colours virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr positions, osg::ref_ptr normals, osg::ref_ptr colours); /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. /// @note May be called from background threads. /// @param chunkSize size of the terrain chunk in cell units /// @param chunkCenter center of the chunk in cell units /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - /// otherwise, each texture contains blend values for one layer only. Shader-based rendering /// can utilize packing, FFP can't. /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, bool pack, ImageVector& blendmaps, std::vector& layerList); virtual float getHeightAt (const osg::Vec3f& worldPos); virtual Terrain::LayerInfo getDefaultLayer(); /// Get the transformation factor for mapping cell units to world units. virtual float getCellWorldSize(); /// Get the number of vertices on one side for each cell. Should be (power of two)+1 virtual int getCellVertices(); private: const VFS::Manager* mVFS; void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row); void fixColour (osg::Vec4f& colour, int cellX, int cellY, int col, int row); void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row); float getVertexHeight (const ESM::Land* land, int x, int y); // Since plugins can define new texture palettes, we need to know the plugin index too // in order to retrieve the correct texture name. // pair typedef std::pair UniqueTextureId; UniqueTextureId getVtexIndexAt(int cellX, int cellY, int x, int y); std::string getTextureName (UniqueTextureId id); std::map mLayerInfoMap; Terrain::LayerInfo getLayerInfo(const std::string& texture); }; } #endif openmw-openmw-0.38.0/components/files/000077500000000000000000000000001264522266000177065ustar00rootroot00000000000000openmw-openmw-0.38.0/components/files/androidpath.cpp000066400000000000000000000056111264522266000227120ustar00rootroot00000000000000#include "androidpath.hpp" #if defined(__ANDROID__) #include #include #include #include "androidpath.h" #include #include class Buffer { public: static void setData(char const *data); static char const * getData(); }; static char const *path; void Buffer::setData(char const *data) { path=data; } char const * Buffer::getData() { return path; } JNIEXPORT void JNICALL Java_ui_activity_GameActivity_getPathToJni(JNIEnv *env, jobject obj, jstring prompt) { jboolean iscopy; Buffer::setData((env)->GetStringUTFChars(prompt, &iscopy)); (env)->DeleteLocalRef(prompt); } namespace { boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); if (dir == NULL) { struct passwd* pwd = getpwuid(getuid()); if (pwd != NULL) { dir = pwd->pw_dir; } } if (dir == NULL) return boost::filesystem::path(); else return boost::filesystem::path(dir); } boost::filesystem::path getEnv(const std::string& envVariable, const boost::filesystem::path& fallback) { const char* result = getenv(envVariable.c_str()); if (!result) return fallback; boost::filesystem::path dir(result); if (dir.empty()) return fallback; else return dir; } } /** * \namespace Files */ namespace Files { AndroidPath::AndroidPath(const std::string& application_name) : mName(application_name) { } boost::filesystem::path AndroidPath::getUserConfigPath() const { std::string buffer = ""; buffer = buffer + Buffer::getData() +"/config"; return getEnv("XDG_CONFIG_HOME", buffer) / mName; } boost::filesystem::path AndroidPath::getUserDataPath() const { std::string buffer = ""; buffer = buffer + Buffer::getData() +"/share"; return getEnv("XDG_DATA_HOME", buffer) / mName; } boost::filesystem::path AndroidPath::getCachePath() const { std::string buffer = ""; buffer = buffer + Buffer::getData() +"/cache"; return getEnv("XDG_CACHE_HOME", buffer) / mName; } boost::filesystem::path AndroidPath::getGlobalConfigPath() const { std::string buffer = ""; buffer = buffer + Buffer::getData() +"/"; boost::filesystem::path globalPath(buffer); return globalPath / mName; } boost::filesystem::path AndroidPath::getLocalPath() const { return boost::filesystem::path("./"); } boost::filesystem::path AndroidPath::getGlobalDataPath() const { std::string buffer = ""; buffer = buffer + Buffer::getData() +"/data"; boost::filesystem::path globalDataPath(buffer); return globalDataPath / mName; } boost::filesystem::path AndroidPath::getInstallPath() const { return boost::filesystem::path(); } } /* namespace Files */ #endif /* defined(__Android__) */ openmw-openmw-0.38.0/components/files/androidpath.h000066400000000000000000000007401264522266000223550ustar00rootroot00000000000000/* DO NOT EDIT THIS FILE - it is machine generated */ #include #ifndef _Included_ui_activity_GameActivity_getPathToJni #define _Included_ui_activity_GameActivity_getPathToJni #ifdef __cplusplus extern "C" { #endif /* * Class: Java_org_libsdl_app_SDLActivity_getPathToJni * Method: getPathToJni * Signature: (I)I */ JNIEXPORT void JNICALL Java_ui_activity_GameActivity_getPathToJni(JNIEnv *env, jobject obj, jstring prompt); #ifdef __cplusplus } #endif #endif openmw-openmw-0.38.0/components/files/androidpath.hpp000066400000000000000000000023151264522266000227150ustar00rootroot00000000000000#ifndef COMPONENTS_FILES_ANDROIDPATH_H #define COMPONENTS_FILES_ANDROIDPATH_H #if defined(__ANDROID__) #include /** * \namespace Files */ namespace Files { struct AndroidPath { AndroidPath(const std::string& application_name); /** * \brief Return path to the user directory. */ boost::filesystem::path getUserConfigPath() const; boost::filesystem::path getUserDataPath() const; /** * \brief Return path to the global (system) directory where config files can be placed. */ boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return path to the runtime configuration directory which is the * place where an application was started. */ boost::filesystem::path getLocalPath() const; /** * \brief Return path to the global (system) directory where game files can be placed. */ boost::filesystem::path getGlobalDataPath() const; /** * \brief */ boost::filesystem::path getCachePath() const; boost::filesystem::path getInstallPath() const; std::string mName; }; } /* namespace Files */ #endif /* defined(__Android__) */ #endif /* COMPONENTS_FILES_ANDROIDPATH_H */ openmw-openmw-0.38.0/components/files/collections.cpp000066400000000000000000000050451264522266000227340ustar00rootroot00000000000000#include "collections.hpp" #include namespace Files { Collections::Collections() : mDirectories() , mFoldCase(false) , mCollections() { } Collections::Collections(const Files::PathContainer& directories, bool foldCase) : mDirectories(directories) , mFoldCase(foldCase) , mCollections() { } const MultiDirCollection& Collections::getCollection(const std::string& extension) const { MultiDirCollectionContainer::iterator iter = mCollections.find(extension); if (iter==mCollections.end()) { std::pair result = mCollections.insert(std::make_pair(extension, MultiDirCollection(mDirectories, extension, mFoldCase))); iter = result.first; } return iter->second; } boost::filesystem::path Collections::getPath(const std::string& file) const { for (Files::PathContainer::const_iterator iter = mDirectories.begin(); iter != mDirectories.end(); ++iter) { for (boost::filesystem::directory_iterator iter2 (*iter); iter2!=boost::filesystem::directory_iterator(); ++iter2) { boost::filesystem::path path = *iter2; if (mFoldCase) { if (Misc::StringUtils::ciEqual(file, path.filename().string())) return path.string(); } else if (path.filename().string() == file) return path.string(); } } throw std::runtime_error ("file " + file + " not found"); } bool Collections::doesExist(const std::string& file) const { for (Files::PathContainer::const_iterator iter = mDirectories.begin(); iter != mDirectories.end(); ++iter) { for (boost::filesystem::directory_iterator iter2 (*iter); iter2!=boost::filesystem::directory_iterator(); ++iter2) { boost::filesystem::path path = *iter2; if (mFoldCase) { if (Misc::StringUtils::ciEqual(file, path.filename().string())) return true; } else if (path.filename().string() == file) return true; } } return false; } const Files::PathContainer& Collections::getPaths() const { return mDirectories; } } openmw-openmw-0.38.0/components/files/collections.hpp000066400000000000000000000026001264522266000227330ustar00rootroot00000000000000#ifndef COMPONENTS_FILES_COLLECTION_HPP #define COMPONENTS_FILES_COLLECTION_HPP #include #include "multidircollection.hpp" namespace Files { class Collections { public: Collections(); ///< Directories are listed with increasing priority. Collections(const Files::PathContainer& directories, bool foldCase); ///< Return a file collection for the given extension. Extension must contain the /// leading dot and must be all lower-case. const MultiDirCollection& getCollection(const std::string& extension) const; boost::filesystem::path getPath(const std::string& file) const; ///< Return full path (including filename) of \a file. /// /// If the file does not exist in any of the collection's /// directories, an exception is thrown. \a file must include the /// extension. bool doesExist(const std::string& file) const; ///< \return Does a file with the given name exist? const Files::PathContainer& getPaths() const; private: typedef std::map MultiDirCollectionContainer; Files::PathContainer mDirectories; bool mFoldCase; mutable MultiDirCollectionContainer mCollections; }; } #endif openmw-openmw-0.38.0/components/files/configurationmanager.cpp000066400000000000000000000136331264522266000246220ustar00rootroot00000000000000#include "configurationmanager.hpp" #include #include #include #include #include #include /** * \namespace Files */ namespace Files { static const char* const openmwCfgFile = "openmw.cfg"; #if defined(_WIN32) || defined(__WINDOWS__) static const char* const applicationName = "OpenMW"; #else static const char* const applicationName = "openmw"; #endif const char* const mwToken = "?mw?"; const char* const localToken = "?local?"; const char* const userDataToken = "?userdata?"; const char* const globalToken = "?global?"; ConfigurationManager::ConfigurationManager(bool silent) : mFixedPath(applicationName) , mSilent(silent) { setupTokensMapping(); boost::filesystem::create_directories(mFixedPath.getUserConfigPath()); boost::filesystem::create_directories(mFixedPath.getUserDataPath()); mLogPath = mFixedPath.getUserConfigPath(); } ConfigurationManager::~ConfigurationManager() { } void ConfigurationManager::setupTokensMapping() { mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath)); mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath)); mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath)); mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, boost::program_options::options_description& description, bool quiet) { bool silent = mSilent; mSilent = quiet; loadConfig(mFixedPath.getUserConfigPath(), variables, description); boost::program_options::notify(variables); // read either local or global config depending on type of installation bool loaded = loadConfig(mFixedPath.getLocalPath(), variables, description); boost::program_options::notify(variables); if (!loaded) { loadConfig(mFixedPath.getGlobalConfigPath(), variables, description); boost::program_options::notify(variables); } mSilent = silent; } void ConfigurationManager::processPaths(Files::PathContainer& dataDirs, bool create) { std::string path; for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { path = it->string(); boost::erase_all(path, "\""); *it = boost::filesystem::path(path); // Check if path contains a token if (!path.empty() && *path.begin() == '?') { std::string::size_type pos = path.find('?', 1); if (pos != std::string::npos && pos != 0) { TokensMappingContainer::iterator tokenIt = mTokensMapping.find(path.substr(0, pos + 1)); if (tokenIt != mTokensMapping.end()) { boost::filesystem::path tempPath(((mFixedPath).*(tokenIt->second))()); if (pos < path.length() - 1) { // There is something after the token, so we should // append it to the path tempPath /= path.substr(pos + 1, path.length() - pos); } *it = tempPath; } else { // Clean invalid / unknown token, it will be removed outside the loop (*it).clear(); } } } if (!boost::filesystem::is_directory(*it)) { if (create) { try { boost::filesystem::create_directories (*it); } catch (...) {} if (boost::filesystem::is_directory(*it)) continue; } (*it).clear(); } } dataDirs.erase(std::remove_if(dataDirs.begin(), dataDirs.end(), boost::bind(&boost::filesystem::path::empty, _1)), dataDirs.end()); } bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, boost::program_options::variables_map& variables, boost::program_options::options_description& description) { boost::filesystem::path cfgFile(path); cfgFile /= std::string(openmwCfgFile); if (boost::filesystem::is_regular_file(cfgFile)) { if (!mSilent) std::cout << "Loading config file: " << cfgFile.string() << "... "; boost::filesystem::ifstream configFileStream(cfgFile); if (configFileStream.is_open()) { boost::program_options::store(boost::program_options::parse_config_file( configFileStream, description, true), variables); if (!mSilent) std::cout << "done." << std::endl; return true; } else { if (!mSilent) std::cout << "failed." << std::endl; return false; } } return false; } const boost::filesystem::path& ConfigurationManager::getGlobalPath() const { return mFixedPath.getGlobalConfigPath(); } const boost::filesystem::path& ConfigurationManager::getUserConfigPath() const { return mFixedPath.getUserConfigPath(); } const boost::filesystem::path& ConfigurationManager::getUserDataPath() const { return mFixedPath.getUserDataPath(); } const boost::filesystem::path& ConfigurationManager::getLocalPath() const { return mFixedPath.getLocalPath(); } const boost::filesystem::path& ConfigurationManager::getGlobalDataPath() const { return mFixedPath.getGlobalDataPath(); } const boost::filesystem::path& ConfigurationManager::getCachePath() const { return mFixedPath.getCachePath(); } const boost::filesystem::path& ConfigurationManager::getInstallPath() const { return mFixedPath.getInstallPath(); } const boost::filesystem::path& ConfigurationManager::getLogPath() const { return mLogPath; } } /* namespace Cfg */ openmw-openmw-0.38.0/components/files/configurationmanager.hpp000066400000000000000000000045031264522266000246230ustar00rootroot00000000000000#ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #if defined(_WIN32) && !defined(__MINGW32__) #include #elif defined HAVE_UNORDERED_MAP #include #else #include #endif #include #include #include /** * \namespace Files */ namespace Files { /** * \struct ConfigurationManager */ struct ConfigurationManager { ConfigurationManager(bool silent=false); /// @param silent Emit log messages to cout? virtual ~ConfigurationManager(); void readConfiguration(boost::program_options::variables_map& variables, boost::program_options::options_description& description, bool quiet=false); void processPaths(Files::PathContainer& dataDirs, bool create = false); ///< \param create Try creating the directory, if it does not exist. /**< Fixed paths */ const boost::filesystem::path& getGlobalPath() const; const boost::filesystem::path& getUserConfigPath() const; const boost::filesystem::path& getLocalPath() const; const boost::filesystem::path& getGlobalDataPath() const; const boost::filesystem::path& getUserDataPath() const; const boost::filesystem::path& getLocalDataPath() const; const boost::filesystem::path& getInstallPath() const; const boost::filesystem::path& getCachePath() const; const boost::filesystem::path& getLogPath() const; private: typedef Files::FixedPath<> FixedPathType; typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const; #if defined HAVE_UNORDERED_MAP typedef std::unordered_map TokensMappingContainer; #else typedef std::tr1::unordered_map TokensMappingContainer; #endif bool loadConfig(const boost::filesystem::path& path, boost::program_options::variables_map& variables, boost::program_options::options_description& description); void setupTokensMapping(); FixedPathType mFixedPath; boost::filesystem::path mLogPath; TokensMappingContainer mTokensMapping; bool mSilent; }; } /* namespace Cfg */ #endif /* COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP */ openmw-openmw-0.38.0/components/files/constrainedfilestream.cpp000066400000000000000000000066251264522266000250100ustar00rootroot00000000000000#include "constrainedfilestream.hpp" #include #include "lowlevelfile.hpp" namespace { // somewhat arbitrary though 64KB buffers didn't seem to improve performance any const size_t sBufferSize = 4096; } namespace Files { class ConstrainedFileStreamBuf : public std::streambuf { size_t mOrigin; size_t mSize; LowLevelFile mFile; char mBuffer[sBufferSize]; public: ConstrainedFileStreamBuf(const std::string &fname, size_t start, size_t length) { mFile.open (fname.c_str ()); mSize = length != 0xFFFFFFFF ? length : mFile.size () - start; if (start != 0) mFile.seek(start); setg(0,0,0); mOrigin = start; } virtual int_type underflow() { if(gptr() == egptr()) { size_t toRead = std::min((mOrigin+mSize)-(mFile.tell()), sBufferSize); // Read in the next chunk of data, and set the read pointers on success // Failure will throw exception in LowLevelFile size_t got = mFile.read(mBuffer, toRead); setg(&mBuffer[0], &mBuffer[0], &mBuffer[0]+got); } if(gptr() == egptr()) return traits_type::eof(); return traits_type::to_int_type(*gptr()); } virtual pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) { if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) return traits_type::eof(); // new file position, relative to mOrigin size_t newPos; switch (whence) { case std::ios_base::beg: newPos = offset; break; case std::ios_base::cur: newPos = (mFile.tell() - mOrigin - (egptr() - gptr())) + offset; break; case std::ios_base::end: newPos = mSize + offset; break; default: return traits_type::eof(); } if (newPos > mSize) return traits_type::eof(); mFile.seek(mOrigin+newPos); // Clear read pointers so underflow() gets called on the next read attempt. setg(0, 0, 0); return newPos; } virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) { if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) return traits_type::eof(); if ((size_t)pos > mSize) return traits_type::eof(); mFile.seek(mOrigin + pos); // Clear read pointers so underflow() gets called on the next read attempt. setg(0, 0, 0); return pos; } }; ConstrainedFileStream::ConstrainedFileStream(const char *filename, size_t start, size_t length) : std::istream(new ConstrainedFileStreamBuf(filename, start, length)) { } ConstrainedFileStream::~ConstrainedFileStream() { delete rdbuf(); } IStreamPtr openConstrainedFileStream(const char *filename, size_t start, size_t length) { return IStreamPtr(new ConstrainedFileStream(filename, start, length)); } } openmw-openmw-0.38.0/components/files/constrainedfilestream.hpp000066400000000000000000000012121264522266000250000ustar00rootroot00000000000000#ifndef OPENMW_CONSTRAINEDFILESTREAM_H #define OPENMW_CONSTRAINEDFILESTREAM_H #include #include namespace Files { /// A file stream constrained to a specific region in the file, specified by the 'start' and 'length' parameters. class ConstrainedFileStream : public std::istream { public: ConstrainedFileStream(const char *filename, size_t start=0, size_t length=0xFFFFFFFF); virtual ~ConstrainedFileStream(); }; typedef boost::shared_ptr IStreamPtr; IStreamPtr openConstrainedFileStream(const char *filename, size_t start=0, size_t length=0xFFFFFFFF); } #endif openmw-openmw-0.38.0/components/files/fixedpath.hpp000066400000000000000000000063111264522266000223740ustar00rootroot00000000000000#ifndef COMPONENTS_FILES_FIXEDPATH_HPP #define COMPONENTS_FILES_FIXEDPATH_HPP #include #include #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #ifndef ANDROID #include namespace Files { typedef LinuxPath TargetPathType; } #else #include namespace Files { typedef AndroidPath TargetPathType; } #endif #elif defined(__WIN32) || defined(__WINDOWS__) || defined(_WIN32) #include namespace Files { typedef WindowsPath TargetPathType; } #elif defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) #include namespace Files { typedef MacOsPath TargetPathType; } #else #error "Unknown platform!" #endif /** * \namespace Files */ namespace Files { /** * \struct Path * * \tparam P - Path strategy class type (depends on target system) * */ template < class P = TargetPathType > struct FixedPath { typedef P PathType; /** * \brief Path constructor. * * \param [in] application_name - Name of the application */ FixedPath(const std::string& application_name) : mPath(application_name + "/") , mUserConfigPath(mPath.getUserConfigPath()) , mUserDataPath(mPath.getUserDataPath()) , mGlobalConfigPath(mPath.getGlobalConfigPath()) , mLocalPath(mPath.getLocalPath()) , mGlobalDataPath(mPath.getGlobalDataPath()) , mCachePath(mPath.getCachePath()) , mInstallPath(mPath.getInstallPath()) { } /** * \brief Return path pointing to the user local configuration directory. */ const boost::filesystem::path& getUserConfigPath() const { return mUserConfigPath; } const boost::filesystem::path& getUserDataPath() const { return mUserDataPath; } /** * \brief Return path pointing to the global (system) configuration directory. */ const boost::filesystem::path& getGlobalConfigPath() const { return mGlobalConfigPath; } /** * \brief Return path pointing to the directory where application was started. */ const boost::filesystem::path& getLocalPath() const { return mLocalPath; } const boost::filesystem::path& getInstallPath() const { return mInstallPath; } const boost::filesystem::path& getGlobalDataPath() const { return mGlobalDataPath; } const boost::filesystem::path& getCachePath() const { return mCachePath; } private: PathType mPath; boost::filesystem::path mUserConfigPath; /**< User path */ boost::filesystem::path mUserDataPath; boost::filesystem::path mGlobalConfigPath; /**< Global path */ boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */ boost::filesystem::path mGlobalDataPath; /**< Global application data path */ boost::filesystem::path mCachePath; boost::filesystem::path mInstallPath; }; } /* namespace Files */ #endif /* COMPONENTS_FILES_FIXEDPATH_HPP */ openmw-openmw-0.38.0/components/files/linuxpath.cpp000066400000000000000000000107331264522266000224320ustar00rootroot00000000000000#include "linuxpath.hpp" #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include #include #include #include #include #include namespace { boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); if (dir == NULL) { struct passwd* pwd = getpwuid(getuid()); if (pwd != NULL) { dir = pwd->pw_dir; } } if (dir == NULL) return boost::filesystem::path(); else return boost::filesystem::path(dir); } boost::filesystem::path getEnv(const std::string& envVariable, const boost::filesystem::path& fallback) { const char* result = getenv(envVariable.c_str()); if (!result) return fallback; boost::filesystem::path dir(result); if (dir.empty()) return fallback; else return dir; } } /** * \namespace Files */ namespace Files { LinuxPath::LinuxPath(const std::string& application_name) : mName(application_name) { } boost::filesystem::path LinuxPath::getUserConfigPath() const { return getEnv("XDG_CONFIG_HOME", getUserHome() / ".config") / mName; } boost::filesystem::path LinuxPath::getUserDataPath() const { return getEnv("XDG_DATA_HOME", getUserHome() / ".local/share") / mName; } boost::filesystem::path LinuxPath::getCachePath() const { return getEnv("XDG_CACHE_HOME", getUserHome() / ".cache") / mName; } boost::filesystem::path LinuxPath::getGlobalConfigPath() const { boost::filesystem::path globalPath(GLOBAL_CONFIG_PATH); return globalPath / mName; } boost::filesystem::path LinuxPath::getLocalPath() const { return boost::filesystem::path("./"); } boost::filesystem::path LinuxPath::getGlobalDataPath() const { boost::filesystem::path globalDataPath(GLOBAL_DATA_PATH); return globalDataPath / mName; } boost::filesystem::path LinuxPath::getInstallPath() const { boost::filesystem::path installPath; boost::filesystem::path homePath = getUserHome(); if (!homePath.empty()) { boost::filesystem::path wineDefaultRegistry(homePath); wineDefaultRegistry /= ".wine/system.reg"; if (boost::filesystem::is_regular_file(wineDefaultRegistry)) { boost::filesystem::ifstream file(wineDefaultRegistry); bool isRegEntry = false; std::string line; std::string mwpath; while (std::getline(file, line)) { if (line[0] == '[') // we found an entry { if (isRegEntry) { break; } isRegEntry = (line.find("Softworks\\\\Morrowind]") != std::string::npos); } else if (isRegEntry) { if (line[0] == '"') // empty line means new registry key { std::string key = line.substr(1, line.find('"', 1) - 1); if (strcasecmp(key.c_str(), "Installed Path") == 0) { std::string::size_type valuePos = line.find('=') + 2; mwpath = line.substr(valuePos, line.rfind('"') - valuePos); std::string::size_type pos = mwpath.find("\\"); while (pos != std::string::npos) { mwpath.replace(pos, 2, "/"); pos = mwpath.find("\\", pos + 1); } break; } } } } if (!mwpath.empty()) { // Change drive letter to lowercase, so we could use // ~/.wine/dosdevices symlinks mwpath[0] = Misc::StringUtils::toLower(mwpath[0]); installPath /= homePath; installPath /= ".wine/dosdevices/"; installPath /= mwpath; if (!boost::filesystem::is_directory(installPath)) { installPath.clear(); } } } } return installPath; } } /* namespace Files */ #endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */ openmw-openmw-0.38.0/components/files/linuxpath.hpp000066400000000000000000000026441264522266000224410ustar00rootroot00000000000000#ifndef COMPONENTS_FILES_LINUXPATH_H #define COMPONENTS_FILES_LINUXPATH_H #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include /** * \namespace Files */ namespace Files { /** * \struct LinuxPath */ struct LinuxPath { LinuxPath(const std::string& application_name); /** * \brief Return path to the user directory. */ boost::filesystem::path getUserConfigPath() const; boost::filesystem::path getUserDataPath() const; /** * \brief Return path to the global (system) directory where config files can be placed. */ boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return path to the runtime configuration directory which is the * place where an application was started. */ boost::filesystem::path getLocalPath() const; /** * \brief Return path to the global (system) directory where game files can be placed. */ boost::filesystem::path getGlobalDataPath() const; /** * \brief */ boost::filesystem::path getCachePath() const; /** * \brief Gets the path of the installed Morrowind version if there is one. */ boost::filesystem::path getInstallPath() const; std::string mName; }; } /* namespace Files */ #endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */ #endif /* COMPONENTS_FILES_LINUXPATH_H */ openmw-openmw-0.38.0/components/files/lowlevelfile.cpp000066400000000000000000000154541264522266000231140ustar00rootroot00000000000000#include "lowlevelfile.hpp" #include #include #include #if FILE_API == FILE_API_POSIX #include #include #include #include #include #endif #if FILE_API == FILE_API_STDIO /* * * Implementation of LowLevelFile methods using c stdio * */ LowLevelFile::LowLevelFile () { mHandle = NULL; } LowLevelFile::~LowLevelFile () { if (mHandle != NULL) fclose (mHandle); } void LowLevelFile::open (char const * filename) { assert (mHandle == NULL); mHandle = fopen (filename, "rb"); if (mHandle == NULL) { std::ostringstream os; os << "Failed to open '" << filename << "' for reading."; throw std::runtime_error (os.str ()); } } void LowLevelFile::close () { assert (mHandle != NULL); fclose (mHandle); mHandle = NULL; } size_t LowLevelFile::size () { assert (mHandle != NULL); long oldPosition = ftell (mHandle); if (oldPosition == -1) throw std::runtime_error ("A query operation on a file failed."); if (fseek (mHandle, 0, SEEK_END) != 0) throw std::runtime_error ("A query operation on a file failed."); long Size = ftell (mHandle); if (Size == -1) throw std::runtime_error ("A query operation on a file failed."); if (fseek (mHandle, oldPosition, SEEK_SET) != 0) throw std::runtime_error ("A query operation on a file failed."); return size_t (Size); } void LowLevelFile::seek (size_t Position) { assert (mHandle != NULL); if (fseek (mHandle, Position, SEEK_SET) != 0) throw std::runtime_error ("A seek operation on a file failed."); } size_t LowLevelFile::tell () { assert (mHandle != NULL); long Position = ftell (mHandle); if (Position == -1) throw std::runtime_error ("A query operation on a file failed."); return size_t (Position); } size_t LowLevelFile::read (void * data, size_t size) { assert (mHandle != NULL); int amount = fread (data, 1, size, mHandle); if (amount == 0 && ferror (mHandle)) throw std::runtime_error ("A read operation on a file failed."); return amount; } #elif FILE_API == FILE_API_POSIX /* * * Implementation of LowLevelFile methods using posix IO calls * */ LowLevelFile::LowLevelFile () { mHandle = -1; } LowLevelFile::~LowLevelFile () { if (mHandle != -1) ::close (mHandle); } void LowLevelFile::open (char const * filename) { assert (mHandle == -1); #ifdef O_BINARY static const int openFlags = O_RDONLY | O_BINARY; #else static const int openFlags = O_RDONLY; #endif mHandle = ::open (filename, openFlags, 0); if (mHandle == -1) { std::ostringstream os; os << "Failed to open '" << filename << "' for reading: " << strerror(errno); throw std::runtime_error (os.str ()); } } void LowLevelFile::close () { assert (mHandle != -1); ::close (mHandle); mHandle = -1; } size_t LowLevelFile::size () { assert (mHandle != -1); size_t oldPosition = ::lseek (mHandle, 0, SEEK_CUR); if (oldPosition == size_t (-1)) { std::ostringstream os; os << "An lseek() call failed:" << strerror(errno); throw std::runtime_error (os.str ()); } size_t Size = ::lseek (mHandle, 0, SEEK_END); if (Size == size_t (-1)) { std::ostringstream os; os << "An lseek() call failed:" << strerror(errno); throw std::runtime_error (os.str ()); } if (lseek (mHandle, oldPosition, SEEK_SET) == -1) { std::ostringstream os; os << "An lseek() call failed:" << strerror(errno); throw std::runtime_error (os.str ()); } return Size; } void LowLevelFile::seek (size_t Position) { assert (mHandle != -1); if (::lseek (mHandle, Position, SEEK_SET) == -1) { std::ostringstream os; os << "An lseek() call failed:" << strerror(errno); throw std::runtime_error (os.str ()); } } size_t LowLevelFile::tell () { assert (mHandle != -1); size_t Position = ::lseek (mHandle, 0, SEEK_CUR); if (Position == size_t (-1)) { std::ostringstream os; os << "An lseek() call failed:" << strerror(errno); throw std::runtime_error (os.str ()); } return Position; } size_t LowLevelFile::read (void * data, size_t size) { assert (mHandle != -1); int amount = ::read (mHandle, data, size); if (amount == -1) { std::ostringstream os; os << "An attempt to read " << size << "bytes failed:" << strerror(errno); throw std::runtime_error (os.str ()); } return amount; } #elif FILE_API == FILE_API_WIN32 #include /* * * Implementation of LowLevelFile methods using Win32 API calls * */ LowLevelFile::LowLevelFile () { mHandle = INVALID_HANDLE_VALUE; } LowLevelFile::~LowLevelFile () { if (mHandle != INVALID_HANDLE_VALUE) CloseHandle (mHandle); } void LowLevelFile::open (char const * filename) { assert (mHandle == INVALID_HANDLE_VALUE); std::wstring wname = boost::locale::conv::utf_to_utf(filename); HANDLE handle = CreateFileW (wname.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); if (handle == INVALID_HANDLE_VALUE) { std::ostringstream os; os << "Failed to open '" << filename << "' for reading."; throw std::runtime_error (os.str ()); } mHandle = handle; } void LowLevelFile::close () { assert (mHandle != INVALID_HANDLE_VALUE); CloseHandle (mHandle); mHandle = INVALID_HANDLE_VALUE; } size_t LowLevelFile::size () { assert (mHandle != INVALID_HANDLE_VALUE); BY_HANDLE_FILE_INFORMATION info; if (!GetFileInformationByHandle (mHandle, &info)) throw std::runtime_error ("A query operation on a file failed."); if (info.nFileSizeHigh != 0) throw std::runtime_error ("Files greater that 4GB are not supported."); return info.nFileSizeLow; } void LowLevelFile::seek (size_t Position) { assert (mHandle != INVALID_HANDLE_VALUE); if (SetFilePointer (mHandle, Position, NULL, SEEK_SET) == INVALID_SET_FILE_POINTER) if (GetLastError () != NO_ERROR) throw std::runtime_error ("A seek operation on a file failed."); } size_t LowLevelFile::tell () { assert (mHandle != INVALID_HANDLE_VALUE); DWORD value = SetFilePointer (mHandle, 0, NULL, SEEK_CUR); if (value == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) throw std::runtime_error ("A query operation on a file failed."); return value; } size_t LowLevelFile::read (void * data, size_t size) { assert (mHandle != INVALID_HANDLE_VALUE); DWORD read; if (!ReadFile (mHandle, data, size, &read, NULL)) throw std::runtime_error ("A read operation on a file failed."); return read; } #endif openmw-openmw-0.38.0/components/files/lowlevelfile.hpp000066400000000000000000000017441264522266000231160ustar00rootroot00000000000000#ifndef COMPONENTS_FILES_LOWLEVELFILE_HPP #define COMPONENTS_FILES_LOWLEVELFILE_HPP #include #define FILE_API_STDIO 0 #define FILE_API_POSIX 1 #define FILE_API_WIN32 2 #if defined(__linux) || defined(__unix) || defined(__posix) #define FILE_API FILE_API_POSIX #elif defined(_WIN32) #define FILE_API FILE_API_WIN32 #else #define FILE_API FILE_API_STDIO #endif #if FILE_API == FILE_API_STDIO #include #elif FILE_API == FILE_API_POSIX #elif FILE_API == FILE_API_WIN32 #include #else #error Unsupported File API #endif class LowLevelFile { public: LowLevelFile (); ~LowLevelFile (); void open (char const * filename); void close (); size_t size (); void seek (size_t Position); size_t tell (); size_t read (void * data, size_t size); private: #if FILE_API == FILE_API_STDIO FILE* mHandle; #elif FILE_API == FILE_API_POSIX int mHandle; #elif FILE_API == FILE_API_WIN32 HANDLE mHandle; #endif }; #endif openmw-openmw-0.38.0/components/files/macospath.cpp000066400000000000000000000103541264522266000223740ustar00rootroot00000000000000#include "macospath.hpp" #if defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) #include #include #include #include #include namespace { boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); if (dir == NULL) { struct passwd* pwd = getpwuid(getuid()); if (pwd != NULL) { dir = pwd->pw_dir; } } if (dir == NULL) return boost::filesystem::path(); else return boost::filesystem::path(dir); } } namespace Files { MacOsPath::MacOsPath(const std::string& application_name) : mName(application_name) { } boost::filesystem::path MacOsPath::getUserConfigPath() const { boost::filesystem::path userPath (getUserHome()); userPath /= "Library/Preferences/"; return userPath / mName; } boost::filesystem::path MacOsPath::getUserDataPath() const { boost::filesystem::path userPath (getUserHome()); userPath /= "Library/Application Support/"; return userPath / mName; } boost::filesystem::path MacOsPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("/Library/Preferences/"); return globalPath / mName; } boost::filesystem::path MacOsPath::getCachePath() const { boost::filesystem::path userPath (getUserHome()); userPath /= "Library/Caches"; return userPath / mName; } boost::filesystem::path MacOsPath::getLocalPath() const { return boost::filesystem::path("./"); } boost::filesystem::path MacOsPath::getGlobalDataPath() const { boost::filesystem::path globalDataPath("/Library/Application Support/"); return globalDataPath / mName; } boost::filesystem::path MacOsPath::getInstallPath() const { boost::filesystem::path installPath; boost::filesystem::path homePath = getUserHome(); if (!homePath.empty()) { boost::filesystem::path wineDefaultRegistry(homePath); wineDefaultRegistry /= ".wine/system.reg"; if (boost::filesystem::is_regular_file(wineDefaultRegistry)) { boost::filesystem::ifstream file(wineDefaultRegistry); bool isRegEntry = false; std::string line; std::string mwpath; while (std::getline(file, line)) { if (line[0] == '[') // we found an entry { if (isRegEntry) { break; } isRegEntry = (line.find("Softworks\\\\Morrowind]") != std::string::npos); } else if (isRegEntry) { if (line[0] == '"') // empty line means new registry key { std::string key = line.substr(1, line.find('"', 1) - 1); if (strcasecmp(key.c_str(), "Installed Path") == 0) { std::string::size_type valuePos = line.find('=') + 2; mwpath = line.substr(valuePos, line.rfind('"') - valuePos); std::string::size_type pos = mwpath.find("\\"); while (pos != std::string::npos) { mwpath.replace(pos, 2, "/"); pos = mwpath.find("\\", pos + 1); } break; } } } } if (!mwpath.empty()) { // Change drive letter to lowercase, so we could use ~/.wine/dosdevice symlinks mwpath[0] = Misc::StringUtils::toLower(mwpath[0]); installPath /= homePath; installPath /= ".wine/dosdevices/"; installPath /= mwpath; if (!boost::filesystem::is_directory(installPath)) { installPath.clear(); } } } } return installPath; } } /* namespace Files */ #endif /* defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) */ openmw-openmw-0.38.0/components/files/macospath.hpp000066400000000000000000000027031264522266000224000ustar00rootroot00000000000000#ifndef COMPONENTS_FILES_MACOSPATH_H #define COMPONENTS_FILES_MACOSPATH_H #if defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) #include /** * \namespace Files */ namespace Files { /** * \struct MacOsPath */ struct MacOsPath { MacOsPath(const std::string& application_name); /** * \brief Return path to the local directory. * * \return boost::filesystem::path */ boost::filesystem::path getUserConfigPath() const; boost::filesystem::path getUserDataPath() const; /** * \brief Return path to the global (system) directory. * * \return boost::filesystem::path */ boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return path to the runtime directory which is the * place where an application was started. * * \return boost::filesystem::path */ boost::filesystem::path getLocalPath() const; /** * \brief * * \return boost::filesystem::path */ boost::filesystem::path getCachePath() const; /** * \brief * * \return boost::filesystem::path */ boost::filesystem::path getGlobalDataPath() const; boost::filesystem::path getInstallPath() const; std::string mName; }; } /* namespace Files */ #endif /* defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) */ #endif /* COMPONENTS_FILES_MACOSPATH_H */ openmw-openmw-0.38.0/components/files/memorystream.hpp000066400000000000000000000014571264522266000231520ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_FILES_MEMORYSTREAM_H #define OPENMW_COMPONENTS_FILES_MEMORYSTREAM_H #include namespace Files { struct MemBuf : std::streambuf { MemBuf(char const* buffer, size_t size) { // a streambuf isn't specific to istreams, so we need a non-const pointer :/ char* nonconstBuffer = (const_cast(buffer)); this->setg(nonconstBuffer, nonconstBuffer, nonconstBuffer + size); } }; /// @brief A variant of std::istream that reads from a constant in-memory buffer. struct IMemStream: virtual MemBuf, std::istream { IMemStream(char const* buffer, size_t size) : MemBuf(buffer, size) , std::istream(static_cast(this)) { } }; } #endif openmw-openmw-0.38.0/components/files/multidircollection.cpp000066400000000000000000000057311264522266000243250ustar00rootroot00000000000000#include "multidircollection.hpp" #include #include #include #include #include #include namespace Files { struct NameEqual { bool mStrict; NameEqual (bool strict) : mStrict (strict) {} bool operator() (const std::string& left, const std::string& right) const { if (mStrict) return left==right; std::size_t len = left.length(); if (len!=right.length()) return false; for (std::size_t i=0; ifirst==filename) { mFiles[filename] = path; } else { // handle case folding mFiles.erase (result->first); mFiles.insert (std::make_pair (filename, path)); } } } } boost::filesystem::path MultiDirCollection::getPath (const std::string& file) const { TIter iter = mFiles.find (file); if (iter==mFiles.end()) throw std::runtime_error ("file " + file + " not found"); return iter->second; } bool MultiDirCollection::doesExist (const std::string& file) const { return mFiles.find (file)!=mFiles.end(); } MultiDirCollection::TIter MultiDirCollection::begin() const { return mFiles.begin(); } MultiDirCollection::TIter MultiDirCollection::end() const { return mFiles.end(); } } openmw-openmw-0.38.0/components/files/multidircollection.hpp000066400000000000000000000050361264522266000243300ustar00rootroot00000000000000#ifndef COMPONENTS_FILES_MULTIDIRSOLLECTION_HPP #define COMPONENTS_FILES_MULTIDIRSOLLECTION_HPP #include #include #include #include #include #include namespace Files { typedef std::vector PathContainer; struct NameLess { bool mStrict; NameLess (bool strict) : mStrict (strict) {} bool operator() (const std::string& left, const std::string& right) const { if (mStrict) return leftr) return false; } return left.length() TContainer; typedef TContainer::const_iterator TIter; private: TContainer mFiles; public: MultiDirCollection (const Files::PathContainer& directories, const std::string& extension, bool foldCase); ///< Directories are listed with increasing priority. /// \param extension The extension that should be listed in this collection. Must /// contain the leading dot. /// \param foldCase Ignore filename case boost::filesystem::path getPath (const std::string& file) const; ///< Return full path (including filename) of \a file. /// /// If the file does not exist, an exception is thrown. \a file must include /// the extension. bool doesExist (const std::string& file) const; ///< \return Does a file with the given name exist? TIter begin() const; ///< Return iterator pointing to the first file. TIter end() const; ///< Return iterator pointing past the last file. }; } #endif openmw-openmw-0.38.0/components/files/windowspath.cpp000066400000000000000000000056171264522266000227720ustar00rootroot00000000000000#include "windowspath.hpp" #if defined(_WIN32) || defined(__WINDOWS__) #include #include #include #include #include namespace bconv = boost::locale::conv; /** * FIXME: Someone with Windows system should check this and correct if necessary * FIXME: MAX_PATH is irrelevant for extended-length paths, i.e. \\?\... */ /** * \namespace Files */ namespace Files { WindowsPath::WindowsPath(const std::string& application_name) : mName(application_name) { /* Since on Windows boost::path.string() returns string of narrow characters in local encoding, it is required to path::imbue() with UTF-8 encoding (generated for empty name from boost::locale) to handle Unicode in platform-agnostic way using std::string. See boost::filesystem and boost::locale reference for details. */ boost::filesystem::path::imbue(boost::locale::generator().generate("")); } boost::filesystem::path WindowsPath::getUserConfigPath() const { boost::filesystem::path userPath("."); WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path))) { userPath = boost::filesystem::path(bconv::utf_to_utf(path)); } return userPath / "My Games" / mName; } boost::filesystem::path WindowsPath::getUserDataPath() const { // Have some chaos, windows people! return getUserConfigPath(); } boost::filesystem::path WindowsPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("."); WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, 0, path))) { globalPath = boost::filesystem::path(bconv::utf_to_utf(path)); } return globalPath / mName; } boost::filesystem::path WindowsPath::getLocalPath() const { return boost::filesystem::path("./"); } boost::filesystem::path WindowsPath::getGlobalDataPath() const { return getGlobalConfigPath(); } boost::filesystem::path WindowsPath::getCachePath() const { return getUserConfigPath() / "cache"; } boost::filesystem::path WindowsPath::getInstallPath() const { boost::filesystem::path installPath(""); HKEY hKey; LPCTSTR regkey = TEXT("SOFTWARE\\Bethesda Softworks\\Morrowind"); if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS) { //Key existed, let's try to read the install dir std::vector buf(512); int len = 512; if (RegQueryValueEx(hKey, TEXT("Installed Path"), NULL, NULL, (LPBYTE)&buf[0], (LPDWORD)&len) == ERROR_SUCCESS) { installPath = &buf[0]; } RegCloseKey(hKey); } return installPath; } } /* namespace Files */ #endif /* defined(_WIN32) || defined(__WINDOWS__) */ openmw-openmw-0.38.0/components/files/windowspath.hpp000066400000000000000000000033051264522266000227670ustar00rootroot00000000000000#ifndef COMPONENTS_FILES_WINDOWSPATH_HPP #define COMPONENTS_FILES_WINDOWSPATH_HPP #if defined(_WIN32) || defined(__WINDOWS__) #include /** * \namespace Files */ namespace Files { /** * \struct WindowsPath */ struct WindowsPath { /** * \brief WindowsPath constructor. * * \param [in] application_name - The name of the application. */ WindowsPath(const std::string& application_name); /** * \brief Returns user path i.e.: * "X:\Documents And Settings\\My Documents\My Games\" * * \return boost::filesystem::path */ boost::filesystem::path getUserConfigPath() const; boost::filesystem::path getUserDataPath() const; /** * \brief Returns "X:\Program Files\" * * \return boost::filesystem::path */ boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return local path which is a location where * an application was started * * \return boost::filesystem::path */ boost::filesystem::path getLocalPath() const; /** * \brief * * \return boost::filesystem::path */ boost::filesystem::path getCachePath() const; /** * \brief Return same path like getGlobalPath * * \return boost::filesystem::path */ boost::filesystem::path getGlobalDataPath() const; /** * \brief Gets the path of the installed Morrowind version if there is one. * * \return boost::filesystem::path */ boost::filesystem::path getInstallPath() const; std::string mName; }; } /* namespace Files */ #endif /* defined(_WIN32) || defined(__WINDOWS__) */ #endif /* COMPONENTS_FILES_WINDOWSPATH_HPP */ openmw-openmw-0.38.0/components/fontloader/000077500000000000000000000000001264522266000207415ustar00rootroot00000000000000openmw-openmw-0.38.0/components/fontloader/fontloader.cpp000066400000000000000000000442021264522266000236040ustar00rootroot00000000000000#include "fontloader.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { unsigned long utf8ToUnicode(const std::string& utf8) { size_t i = 0; unsigned long unicode; size_t numbytes; unsigned char ch = utf8[i++]; if (ch <= 0x7F) { unicode = ch; numbytes = 0; } else if (ch <= 0xBF) { throw std::logic_error("not a UTF-8 string"); } else if (ch <= 0xDF) { unicode = ch&0x1F; numbytes = 1; } else if (ch <= 0xEF) { unicode = ch&0x0F; numbytes = 2; } else if (ch <= 0xF7) { unicode = ch&0x07; numbytes = 3; } else { throw std::logic_error("not a UTF-8 string"); } for (size_t j = 0; j < numbytes; ++j) { unsigned char ch = utf8[i++]; if (ch < 0x80 || ch > 0xBF) throw std::logic_error("not a UTF-8 string"); unicode <<= 6; unicode += ch & 0x3F; } if (unicode >= 0xD800 && unicode <= 0xDFFF) throw std::logic_error("not a UTF-8 string"); if (unicode > 0x10FFFF) throw std::logic_error("not a UTF-8 string"); return unicode; } // getUtf8, aka the worst function ever written. // This includes various hacks for dealing with Morrowind's .fnt files that are *mostly* // in the expected win12XX encoding, but also have randomly swapped characters sometimes. // Looks like the Morrowind developers found standard encodings too boring and threw in some twists for fun. std::string getUtf8 (unsigned char c, ToUTF8::Utf8Encoder& encoder, ToUTF8::FromType encoding) { if (encoding == ToUTF8::WINDOWS_1250) { // Hacks for polish font unsigned char win1250; std::map conv; conv[0x80] = 0xc6; conv[0x81] = 0x9c; conv[0x82] = 0xe6; conv[0x83] = 0xb3; conv[0x84] = 0xf1; conv[0x85] = 0xb9; conv[0x86] = 0xbf; conv[0x87] = 0x9f; conv[0x88] = 0xea; conv[0x89] = 0xea; conv[0x8a] = 0x0; // not contained in win1250 conv[0x8b] = 0x0; // not contained in win1250 conv[0x8c] = 0x8f; conv[0x8d] = 0xaf; conv[0x8e] = 0xa5; conv[0x8f] = 0x8c; conv[0x90] = 0xca; conv[0x93] = 0xa3; conv[0x94] = 0xf6; conv[0x95] = 0xf3; conv[0x96] = 0xaf; conv[0x97] = 0x8f; conv[0x99] = 0xd3; conv[0x9a] = 0xd1; conv[0x9c] = 0x0; // not contained in win1250 conv[0xa0] = 0xb9; conv[0xa1] = 0xaf; conv[0xa2] = 0xf3; conv[0xa3] = 0xbf; conv[0xa4] = 0x0; // not contained in win1250 conv[0xe1] = 0x8c; // Can't remember if this was supposed to read 0xe2, or is it just an extraneous copypaste? //conv[0xe1] = 0x8c; conv[0xe3] = 0x0; // not contained in win1250 conv[0xf5] = 0x0; // not contained in win1250 if (conv.find(c) != conv.end()) win1250 = conv[c]; else win1250 = c; return encoder.getUtf8(std::string(1, win1250)); } else return encoder.getUtf8(std::string(1, c)); } void fail (Files::IStreamPtr file, const std::string& fileName, const std::string& message) { std::stringstream error; error << "Font loading error: " << message; error << "\n File: " << fileName; error << "\n Offset: 0x" << std::hex << file->tellg(); throw std::runtime_error(error.str()); } } namespace Gui { FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs) : mVFS(vfs) { if (encoding == ToUTF8::WINDOWS_1252) mEncoding = ToUTF8::CP437; else mEncoding = encoding; } FontLoader::~FontLoader() { for (std::vector::iterator it = mTextures.begin(); it != mTextures.end(); ++it) delete *it; mTextures.clear(); for (std::vector::iterator it = mFonts.begin(); it != mFonts.end(); ++it) MyGUI::ResourceManager::getInstance().removeByName((*it)->getResourceName()); mFonts.clear(); } void FontLoader::loadAllFonts(bool exportToFile) { const std::map& index = mVFS->getIndex(); std::string pattern = "Fonts/"; mVFS->normalizeFilename(pattern); std::map::const_iterator found = index.lower_bound(pattern); while (found != index.end()) { const std::string& name = found->first; if (name.size() >= pattern.size() && name.substr(0, pattern.size()) == pattern) { size_t pos = name.find_last_of('.'); if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".fnt") == 0) loadFont(name, exportToFile); } else break; ++found; } } typedef struct { float x; float y; } Point; typedef struct { float u1; // appears unused, always 0 Point top_left; Point top_right; Point bottom_left; Point bottom_right; float width; float height; float u2; // appears unused, always 0 float kerning; float ascent; } GlyphInfo; void FontLoader::loadFont(const std::string &fileName, bool exportToFile) { Files::IStreamPtr file = mVFS->get(fileName); float fontSize; file->read((char*)&fontSize, sizeof(fontSize)); if (!file->good()) fail(file, fileName, "File too small to be a valid font"); int one; file->read((char*)&one, sizeof(one)); if (!file->good()) fail(file, fileName, "File too small to be a valid font"); if (one != 1) fail(file, fileName, "Unexpected value"); file->read((char*)&one, sizeof(one)); if (!file->good()) fail(file, fileName, "File too small to be a valid font"); if (one != 1) fail(file, fileName, "Unexpected value"); char name_[284]; file->read(name_, sizeof(name_)); if (!file->good()) fail(file, fileName, "File too small to be a valid font"); std::string name(name_); GlyphInfo data[256]; file->read((char*)data, sizeof(data)); if (!file->good()) fail(file, fileName, "File too small to be a valid font"); file.reset(); // Create the font texture std::string bitmapFilename = "Fonts/" + std::string(name) + ".tex"; Files::IStreamPtr bitmapFile = mVFS->get(bitmapFilename); int width, height; bitmapFile->read((char*)&width, sizeof(int)); bitmapFile->read((char*)&height, sizeof(int)); if (!bitmapFile->good()) fail(bitmapFile, bitmapFilename, "File too small to be a valid bitmap"); if (width <= 0 || height <= 0) fail(bitmapFile, bitmapFilename, "Width and height must be positive"); std::vector textureData; textureData.resize(width*height*4); bitmapFile->read(&textureData[0], width*height*4); if (!bitmapFile->good()) fail(bitmapFile, bitmapFilename, "File too small to be a valid bitmap"); bitmapFile.reset(); std::string resourceName; if (name.size() >= 5 && Misc::StringUtils::ciEqual(name.substr(0, 5), "magic")) resourceName = "Magic Cards"; else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "century")) resourceName = "Century Gothic"; else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "daedric")) resourceName = "Daedric"; if (exportToFile) { osg::ref_ptr image = new osg::Image; image->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE); assert (image->isDataContiguous()); memcpy(image->data(), &textureData[0], textureData.size()); std::cout << "Writing " << resourceName + ".png" << std::endl; osgDB::writeImageFile(*image, resourceName + ".png"); } // Register the font with MyGUI MyGUI::ResourceManualFont* font = static_cast( MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); mFonts.push_back(font); MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture(bitmapFilename); tex->createManual(width, height, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8A8); unsigned char* texData = reinterpret_cast(tex->lock(MyGUI::TextureUsage::Write)); memcpy(texData, &textureData[0], textureData.size()); tex->unlock(); // Using ResourceManualFont::setTexture, enable for MyGUI 3.2.3 /* osg::ref_ptr texture = new osg::Texture2D; texture->setImage(image); osgMyGUI::OSGTexture* myguiTex = new osgMyGUI::OSGTexture(texture); mTextures.push_back(myguiTex); font->setTexture(myguiTex); */ // We need to emulate loading from XML because the data members are private as of mygui 3.2.0 MyGUI::xml::Document xmlDocument; MyGUI::xml::ElementPtr root = xmlDocument.createRoot("ResourceManualFont"); root->addAttribute("name", resourceName); MyGUI::xml::ElementPtr defaultHeight = root->createChild("Property"); defaultHeight->addAttribute("key", "DefaultHeight"); defaultHeight->addAttribute("value", fontSize); MyGUI::xml::ElementPtr source = root->createChild("Property"); source->addAttribute("key", "Source"); source->addAttribute("value", std::string(bitmapFilename)); MyGUI::xml::ElementPtr codes = root->createChild("Codes"); for(int i = 0; i < 256; i++) { float x1 = data[i].top_left.x*width; float y1 = data[i].top_left.y*height; float w = data[i].top_right.x*width - x1; float h = data[i].bottom_left.y*height - y1; ToUTF8::Utf8Encoder encoder(mEncoding); unsigned long unicodeVal = utf8ToUnicode(getUtf8(i, encoder, mEncoding)); MyGUI::xml::ElementPtr code = codes->createChild("Code"); code->addAttribute("index", unicodeVal); code->addAttribute("coord", MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " " + MyGUI::utility::toString(w) + " " + MyGUI::utility::toString(h)); code->addAttribute("advance", data[i].width); code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + MyGUI::utility::toString((fontSize-data[i].ascent))); code->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); // More hacks! The french game uses several win1252 characters that are not included // in the cp437 encoding of the font. Fall back to similar available characters. if (mEncoding == ToUTF8::CP437) { std::multimap additional; // additional.insert(std::make_pair(39, 0x2019)); // apostrophe additional.insert(std::make_pair(45, 0x2013)); // dash additional.insert(std::make_pair(45, 0x2014)); // dash additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark additional.insert(std::make_pair(44, 0x201A)); additional.insert(std::make_pair(44, 0x201E)); additional.insert(std::make_pair(43, 0x2020)); additional.insert(std::make_pair(94, 0x02C6)); additional.insert(std::make_pair(37, 0x2030)); additional.insert(std::make_pair(83, 0x0160)); additional.insert(std::make_pair(60, 0x2039)); additional.insert(std::make_pair(79, 0x0152)); additional.insert(std::make_pair(90, 0x017D)); additional.insert(std::make_pair(39, 0x2019)); additional.insert(std::make_pair(126, 0x02DC)); additional.insert(std::make_pair(84, 0x2122)); additional.insert(std::make_pair(83, 0x0161)); additional.insert(std::make_pair(62, 0x203A)); additional.insert(std::make_pair(111, 0x0153)); additional.insert(std::make_pair(122, 0x017E)); additional.insert(std::make_pair(89, 0x0178)); additional.insert(std::make_pair(156, 0x00A2)); additional.insert(std::make_pair(46, 0x2026)); for (std::multimap::iterator it = additional.begin(); it != additional.end(); ++it) { if (it->first != i) continue; MyGUI::xml::ElementPtr code = codes->createChild("Code"); code->addAttribute("index", it->second); code->addAttribute("coord", MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " " + MyGUI::utility::toString(w) + " " + MyGUI::utility::toString(h)); code->addAttribute("advance", data[i].width); code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + MyGUI::utility::toString((fontSize-data[i].ascent))); code->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); } } // ASCII vertical bar, use this as text input cursor if (i == 124) { MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code"); cursorCode->addAttribute("index", MyGUI::FontCodeType::Cursor); cursorCode->addAttribute("coord", MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " " + MyGUI::utility::toString(w) + " " + MyGUI::utility::toString(h)); cursorCode->addAttribute("advance", data[i].width); cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + MyGUI::utility::toString((fontSize-data[i].ascent))); cursorCode->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); } // Question mark, use for NotDefined marker (used for glyphs not existing in the font) if (i == 63) { MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code"); cursorCode->addAttribute("index", MyGUI::FontCodeType::NotDefined); cursorCode->addAttribute("coord", MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " " + MyGUI::utility::toString(w) + " " + MyGUI::utility::toString(h)); cursorCode->addAttribute("advance", data[i].width); cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + MyGUI::utility::toString((fontSize-data[i].ascent))); cursorCode->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); } } // These are required as well, but the fonts don't provide them for (int i=0; i<2; ++i) { MyGUI::FontCodeType::Enum type; if(i == 0) type = MyGUI::FontCodeType::Selected; else if (i == 1) type = MyGUI::FontCodeType::SelectedBack; MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code"); cursorCode->addAttribute("index", type); cursorCode->addAttribute("coord", "0 0 0 0"); cursorCode->addAttribute("advance", "0"); cursorCode->addAttribute("bearing", "0 0"); cursorCode->addAttribute("size", "0 0"); } if (exportToFile) { std::cout << "Writing " << resourceName + ".xml" << std::endl; xmlDocument.createDeclaration(); xmlDocument.save(resourceName + ".xml"); } font->deserialization(root, MyGUI::Version(3,2,0)); for (std::vector::iterator it = mFonts.begin(); it != mFonts.end();) { if ((*it)->getResourceName() == font->getResourceName()) { MyGUI::ResourceManager::getInstance().removeByName(font->getResourceName()); it = mFonts.erase(it); } else ++it; } MyGUI::ResourceManager::getInstance().addResource(font); } } openmw-openmw-0.38.0/components/fontloader/fontloader.hpp000066400000000000000000000022331264522266000236070ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_FONTLOADER_H #define OPENMW_COMPONENTS_FONTLOADER_H #include namespace VFS { class Manager; } namespace MyGUI { class ITexture; class ResourceManualFont; } namespace Gui { /// @brief loads Morrowind's .fnt/.tex fonts for use with MyGUI and OSG /// @note The FontLoader needs to remain in scope as long as you want to use the loaded fonts. class FontLoader { public: FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs); ~FontLoader(); /// @param exportToFile export the converted fonts (Images and XML with glyph metrics) to files? void loadAllFonts (bool exportToFile); private: ToUTF8::FromType mEncoding; const VFS::Manager* mVFS; std::vector mTextures; std::vector mFonts; /// @param exportToFile export the converted font (Image and XML with glyph metrics) to files? void loadFont (const std::string& fileName, bool exportToFile); FontLoader(const FontLoader&); void operator=(const FontLoader&); }; } #endif openmw-openmw-0.38.0/components/interpreter/000077500000000000000000000000001264522266000211475ustar00rootroot00000000000000openmw-openmw-0.38.0/components/interpreter/context.hpp000066400000000000000000000074231264522266000233520ustar00rootroot00000000000000#ifndef INTERPRETER_CONTEXT_H_INCLUDED #define INTERPRETER_CONTEXT_H_INCLUDED #include #include namespace Interpreter { class Context { public: virtual ~Context() {} virtual int getLocalShort (int index) const = 0; virtual int getLocalLong (int index) const = 0; virtual float getLocalFloat (int index) const = 0; virtual void setLocalShort (int index, int value) = 0; virtual void setLocalLong (int index, int value) = 0; virtual void setLocalFloat (int index, float value) = 0; virtual void messageBox (const std::string& message, const std::vector& buttons) = 0; void messageBox (const std::string& message) { std::vector empty; messageBox (message, empty); } virtual void report (const std::string& message) = 0; virtual bool menuMode() = 0; virtual int getGlobalShort (const std::string& name) const = 0; virtual int getGlobalLong (const std::string& name) const = 0; virtual float getGlobalFloat (const std::string& name) const = 0; virtual void setGlobalShort (const std::string& name, int value) = 0; virtual void setGlobalLong (const std::string& name, int value) = 0; virtual void setGlobalFloat (const std::string& name, float value) = 0; virtual std::vector getGlobals () const = 0; virtual char getGlobalType (const std::string& name) const = 0; virtual std::string getActionBinding(const std::string& action) const = 0; virtual std::string getNPCName() const = 0; virtual std::string getNPCRace() const = 0; virtual std::string getNPCClass() const = 0; virtual std::string getNPCFaction() const = 0; virtual std::string getNPCRank() const = 0; virtual std::string getPCName() const = 0; virtual std::string getPCRace() const = 0; virtual std::string getPCClass() const = 0; virtual std::string getPCRank() const = 0; virtual std::string getPCNextRank() const = 0; virtual int getPCBounty() const = 0; virtual std::string getCurrentCellName() const = 0; virtual bool isScriptRunning (const std::string& name) const = 0; virtual void startScript (const std::string& name, const std::string& targetId = "") = 0; virtual void stopScript (const std::string& name) = 0; virtual float getDistance (const std::string& name, const std::string& id = "") const = 0; virtual float getSecondsPassed() const = 0; virtual bool isDisabled (const std::string& id = "") const = 0; virtual void enable (const std::string& id = "") = 0; virtual void disable (const std::string& id = "") = 0; virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const = 0; virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const = 0; virtual float getMemberFloat (const std::string& id, const std::string& name, bool global) const = 0; virtual void setMemberShort (const std::string& id, const std::string& name, int value, bool global) = 0; virtual void setMemberLong (const std::string& id, const std::string& name, int value, bool global) = 0; virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global) = 0; virtual std::string getTargetId() const = 0; }; } #endif openmw-openmw-0.38.0/components/interpreter/controlopcodes.hpp000066400000000000000000000032001264522266000247100ustar00rootroot00000000000000#ifndef INTERPRETER_CONTROLOPCODES_H_INCLUDED #define INTERPRETER_CONTROLOPCODES_H_INCLUDED #include #include "opcodes.hpp" #include "runtime.hpp" namespace Interpreter { class OpReturn : public Opcode0 { public: virtual void execute (Runtime& runtime) { runtime.setPC (-1); } }; class OpSkipZero : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; runtime.pop(); if (data==0) runtime.setPC (runtime.getPC()+1); } }; class OpSkipNonZero : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; runtime.pop(); if (data!=0) runtime.setPC (runtime.getPC()+1); } }; class OpJumpForward : public Opcode1 { public: virtual void execute (Runtime& runtime, unsigned int arg0) { if (arg0==0) throw std::logic_error ("infinite loop"); runtime.setPC (runtime.getPC()+arg0-1); } }; class OpJumpBackward : public Opcode1 { public: virtual void execute (Runtime& runtime, unsigned int arg0) { if (arg0==0) throw std::logic_error ("infinite loop"); runtime.setPC (runtime.getPC()-arg0-1); } }; } #endif openmw-openmw-0.38.0/components/interpreter/defines.cpp000066400000000000000000000241501264522266000232720ustar00rootroot00000000000000#include "defines.hpp" #include #include #include #include #include namespace Interpreter{ bool check(const std::string& str, const std::string& escword, unsigned int* i, unsigned int* start) { bool retval = str.find(escword) == 0; if(retval){ (*i) += escword.length(); (*start) = (*i) + 1; } return retval; } std::vector globals; bool longerStr(const std::string& a, const std::string& b) { return a.length() > b.length(); } std::string fixDefinesReal(std::string text, char eschar, bool isBook, Context& context) { unsigned int start = 0; std::ostringstream retval; for(unsigned int i = 0; i < text.length(); i++) { if(text[i] == eschar) { retval << text.substr(start, i - start); std::string temp = Misc::StringUtils::lowerCase(text.substr(i+1, 100)); bool found = false; try { if( (found = check(temp, "actionslideright", &i, &start))){ retval << context.getActionBinding("#{sRight}"); } else if((found = check(temp, "actionreadymagic", &i, &start))){ retval << context.getActionBinding("#{sReady_Magic}"); } else if((found = check(temp, "actionprevweapon", &i, &start))){ retval << context.getActionBinding("#{sPrevWeapon}"); } else if((found = check(temp, "actionnextweapon", &i, &start))){ retval << context.getActionBinding("#{sNextWeapon}"); } else if((found = check(temp, "actiontogglerun", &i, &start))){ retval << context.getActionBinding("#{sAuto_Run}"); } else if((found = check(temp, "actionslideleft", &i, &start))){ retval << context.getActionBinding("#{sLeft}"); } else if((found = check(temp, "actionreadyitem", &i, &start))){ retval << context.getActionBinding("#{sReady_Weapon}"); } else if((found = check(temp, "actionprevspell", &i, &start))){ retval << context.getActionBinding("#{sPrevSpell}"); } else if((found = check(temp, "actionnextspell", &i, &start))){ retval << context.getActionBinding("#{sNextSpell}"); } else if((found = check(temp, "actionrestmenu", &i, &start))){ retval << context.getActionBinding("#{sRestKey}"); } else if((found = check(temp, "actionmenumode", &i, &start))){ retval << context.getActionBinding("#{sInventory}"); } else if((found = check(temp, "actionactivate", &i, &start))){ retval << context.getActionBinding("#{sActivate}"); } else if((found = check(temp, "actionjournal", &i, &start))){ retval << context.getActionBinding("#{sJournal}"); } else if((found = check(temp, "actionforward", &i, &start))){ retval << context.getActionBinding("#{sForward}"); } else if((found = check(temp, "pccrimelevel", &i, &start))){ retval << context.getPCBounty(); } else if((found = check(temp, "actioncrouch", &i, &start))){ retval << context.getActionBinding("#{sCrouch_Sneak}"); } else if((found = check(temp, "actionjump", &i, &start))){ retval << context.getActionBinding("#{sJump}"); } else if((found = check(temp, "actionback", &i, &start))){ retval << context.getActionBinding("#{sBack}"); } else if((found = check(temp, "actionuse", &i, &start))){ retval << context.getActionBinding("#{sUse}"); } else if((found = check(temp, "actionrun", &i, &start))){ retval << context.getActionBinding("#{sRun}"); } else if((found = check(temp, "pcclass", &i, &start))){ retval << context.getPCClass(); } else if((found = check(temp, "pcrace", &i, &start))){ retval << context.getPCRace(); } else if((found = check(temp, "pcname", &i, &start))){ retval << context.getPCName(); } else if((found = check(temp, "cell", &i, &start))){ retval << context.getCurrentCellName(); } else if(eschar == '%' && !isBook) { // In Dialogue, not messagebox if( (found = check(temp, "faction", &i, &start))){ retval << context.getNPCFaction(); } else if((found = check(temp, "nextpcrank", &i, &start))){ retval << context.getPCNextRank(); } else if((found = check(temp, "pcnextrank", &i, &start))){ retval << context.getPCNextRank(); } else if((found = check(temp, "pcrank", &i, &start))){ retval << context.getPCRank(); } else if((found = check(temp, "rank", &i, &start))){ retval << context.getNPCRank(); } else if((found = check(temp, "class", &i, &start))){ retval << context.getNPCClass(); } else if((found = check(temp, "race", &i, &start))){ retval << context.getNPCRace(); } else if((found = check(temp, "name", &i, &start))){ retval << context.getNPCName(); } } else { // In messagebox or book, not dialogue /* empty outside dialogue */ if( (found = check(temp, "faction", &i, &start))); else if((found = check(temp, "nextpcrank", &i, &start))); else if((found = check(temp, "pcnextrank", &i, &start))); else if((found = check(temp, "pcrank", &i, &start))); else if((found = check(temp, "rank", &i, &start))); /* uses pc in messageboxes */ else if((found = check(temp, "class", &i, &start))){ retval << context.getPCClass(); } else if((found = check(temp, "race", &i, &start))){ retval << context.getPCRace(); } else if((found = check(temp, "name", &i, &start))){ retval << context.getPCName(); } } /* Not a builtin, try global variables */ if(!found){ /* if list of globals is empty, grab it and sort it by descending string length */ if(globals.empty()){ globals = context.getGlobals(); sort(globals.begin(), globals.end(), longerStr); } for(unsigned int j = 0; j < globals.size(); j++){ if(globals[j].length() > temp.length()){ // Just in case there's a global with a huuuge name std::string temp = text.substr(i+1, globals[j].length()); transform(temp.begin(), temp.end(), temp.begin(), ::tolower); } if((found = check(temp, globals[j], &i, &start))){ char type = context.getGlobalType(globals[j]); switch(type){ case 's': retval << context.getGlobalShort(globals[j]); break; case 'l': retval << context.getGlobalLong(globals[j]); break; case 'f': retval << context.getGlobalFloat(globals[j]); break; } break; } } } } catch (std::exception& e) { std::cerr << "Failed to replace escape character, with the following error: " << e.what() << std::endl; std::cerr << "Full text below: " << std::endl << text << std::endl; } // Not found, or error if(!found){ /* leave unmodified */ i += 1; start = i; retval << eschar; } } } retval << text.substr(start, text.length() - start); return retval.str (); } std::string fixDefinesDialog(std::string text, Context& context){ return fixDefinesReal(text, '%', false, context); } std::string fixDefinesMsgBox(std::string text, Context& context){ return fixDefinesReal(text, '^', false, context); } std::string fixDefinesBook(std::string text, Context& context){ return fixDefinesReal(text, '%', true, context); } } openmw-openmw-0.38.0/components/interpreter/defines.hpp000066400000000000000000000005521264522266000232770ustar00rootroot00000000000000#ifndef INTERPRETER_DEFINES_H_INCLUDED #define INTERPRETER_DEFINES_H_INCLUDED #include #include "context.hpp" namespace Interpreter{ std::string fixDefinesDialog(std::string text, Context& context); std::string fixDefinesMsgBox(std::string text, Context& context); std::string fixDefinesBook(std::string text, Context& context); } #endif openmw-openmw-0.38.0/components/interpreter/docs/000077500000000000000000000000001264522266000220775ustar00rootroot00000000000000openmw-openmw-0.38.0/components/interpreter/docs/vmformat.txt000066400000000000000000000154551264522266000245050ustar00rootroot00000000000000Note: a word is considered to be 32 bit long. Header (4 words): word: number of words in code block word: number of words in integer literal block word: number of words in float literal block word: number of words in string literal block Body (variable length): code block integer literal block (contains a collection of 1 word long integers) float literal block (contains a collection of 1 word long floating point numbers) string literal block (contains a collection of strings of variable length, word-padded) Code bit-patterns: 3322222222221111111111 10987654321098765432109876543210 00ccccccAAAAAAAAAAAAAAAAAAAAAAAA segment 0: 64 opcodes, 1 24-bit argument 01ccccccAAAAAAAAAAAABBBBBBBBBBBB segment 1: 64 opcodes, 2 12-bit arguments 10ccccccccccAAAAAAAAAAAAAAAAAAAA segment 2: 1024 opcodes, 1 20-bit argument 110000ccccccccccccccccccAAAAAAAA segment 3: 262144 opcodes, 1 8-bit argument 110001ccccccccccAAAAAAAABBBBBBBB segment 4: 1024 opcodes, 2 8-bit arguments 110010cccccccccccccccccccccccccc segment 5: 67108864 opcodes, no arguments other bit-patterns reserved legent: c: code A: argument 0 B: argument 1 Segment 0: op 0: push arg0 op 1: move pc ahead by arg0 op 2: move pc back by arg0 opcodes 3-31 unused opcodes 32-63 reserved for extensions Segment 1: opcodes 0-31 unused opcodes 32-63 reserved for extensions Segment 2: opcodes 0-511 unused opcodes 512-1023 reserved for extensions Segment 3: op 0: show message box with message string literal index in stack[0]; buttons (if any) in stack[arg0]..stack[1]; additional arguments (if any) in stack[arg0+n]..stack[arg0+1]; n is determined according to the message string all arguments are removed from stack opcodes 1-131071 unused opcodes 131072-262143 reserved for extensions Segment 4: opcodes 0-511 unused opcodes 512-1023 reserved for extensions Segment 5: op 0: store stack[0] in local short stack[1] and pop twice op 1: store stack[0] in local long stack[1] and pop twice op 2: store stack[0] in local float stack[1] and pop twice op 3: convert stack[0] from integer to float op 4: replace stack[0] with integer literal index stack[0] op 5: replace stack[0] with float literal index stack[0] op 6: convert stack[0] from float to integer op 7: invert sign of int value stack[0] op 8: invert sign of float value stack[0] op 9: add (integer) stack[0] to stack[1], pop twice, push result op 10: add (float) stack[0] to stack[1], pop twice, push result op 11: sub (integer) stack[1] from stack[0], pop twice, push result op 12: sub (float) stack[1] from stack[0], pop twice, push result op 13: mul (integer) stack[0] with stack[1], pop twice, push result op 14: mul (float) stack[0] with stack[1], pop twice, push result op 15: div (integer) stack[1] by stack[0], pop twice, push result op 16: div (float) stack[1] by stack[0], pop twice, push result op 17: convert stack[1] from integer to float op 18: convert stack[1] from float to integer op 19: take square root of stack[0] (float) op 20: return op 21: replace stack[0] with local short stack[0] op 22: replace stack[0] with local long stack[0] op 23: replace stack[0] with local float stack[0] op 24: skip next instruction if stack[0]==0; pop op 25: skip next instruction if stack[0]!=0; pop op 26: compare (intger) stack[1] with stack[0]; pop twice; push 1 if equal, 0 else op 27: compare (intger) stack[1] with stack[0]; pop twice; push 1 if no equal, 0 else op 28: compare (intger) stack[1] with stack[0]; pop twice; push 1 if lesser than, 0 else op 29: compare (intger) stack[1] with stack[0]; pop twice; push 1 if lesser or equal, 0 else op 30: compare (intger) stack[1] with stack[0]; pop twice; push 1 if greater than, 0 else op 31: compare (intger) stack[1] with stack[0]; pop twice; push 1 if greater or equal, 0 else op 32: compare (float) stack[1] with stack[0]; pop twice; push 1 if equal, 0 else op 33: compare (float) stack[1] with stack[0]; pop twice; push 1 if no equal, 0 else op 34: compare (float) stack[1] with stack[0]; pop twice; push 1 if lesser than, 0 else op 35: compare (float) stack[1] with stack[0]; pop twice; push 1 if lesser or equal, 0 else op 36: compare (float) stack[1] with stack[0]; pop twice; push 1 if greater than, 0 else op 37: compare (float) stack[1] with stack[0]; pop twice; push 1 if greater or equal, 0 else op 38: push 1 if game is in menu mode, 0 else op 39: store stack[0] in global short stack[1] and pop twice op 40: store stack[0] in global long stack[1] and pop twice op 41: store stack[0] in global float stack[1] and pop twice op 42: replace stack[0] with global short stack[0] op 43: replace stack[0] with global long stack[0] op 44: replace stack[0] with global float stack[0] op 45: replace stack[0] with a random integer value in the range [0, stack[0]-1] op 46: replace stack[0] with 1, if global script stack[0] is running, 0 else op 47: start script stack[0] and pop op 48: stop script stack[0] and pop op 49: replace stack[0] with distance between implicit reference and a reference of ID stack[0] op 50: push frame duration (float) op 51: enable implicit reference op 52: disable implicit reference op 53: push 1, if implicit reference is disabled, 0 else op 54: explicit reference = stack[0]; pop; enable explicit reference op 55: explicit reference = stack[0]; pop; disable explicit reference op 56: explicit reference = stack[0]; pop; push 1, if explicit reference is disabled, 0 else op 57: explicit reference = stack[0]; pop; replace stack[0] with distance between explicit reference and a reference of ID stack[0] op 58: report string literal index in stack[0]; additional arguments (if any) in stack[n]..stack[1]; n is determined according to the message string all arguments are removed from stack op 59: store stack[0] in member short stack[2] of object with ID stack[1] op 60: store stack[0] in member long stack[2] of object with ID stack[1] op 61: store stack[0] in member float stack[2] of object with ID stack[1] op 62: replace stack[0] with member short stack[1] of object with ID stack[0] op 63: replace stack[0] with member short stack[1] of object with ID stack[0] op 64: replace stack[0] with member short stack[1] of object with ID stack[0] op 65: store stack[0] in member short stack[2] of global script with ID stack[1] op 66: store stack[0] in member long stack[2] of global script with ID stack[1] op 67: store stack[0] in member float stack[2] of global script with ID stack[1] op 68: replace stack[0] with member short stack[1] of global script with ID stack[0] op 69: replace stack[0] with member short stack[1] of global script with ID stack[0] op 70: replace stack[0] with member short stack[1] of global script with ID stack[0] op 71: explicit reference (target) = stack[0]; pop; start script stack[0] and pop opcodes 72-33554431 unused opcodes 33554432-67108863 reserved for extensions openmw-openmw-0.38.0/components/interpreter/genericopcodes.hpp000066400000000000000000000046401264522266000246550ustar00rootroot00000000000000#ifndef INTERPRETER_GENERICOPCODES_H_INCLUDED #define INTERPRETER_GENERICOPCODES_H_INCLUDED #include "opcodes.hpp" #include "runtime.hpp" namespace Interpreter { class OpPushInt : public Opcode1 { public: virtual void execute (Runtime& runtime, unsigned int arg0) { runtime.push (static_cast (arg0)); } }; class OpIntToFloat : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; Type_Float floatValue = static_cast (data); runtime[0].mFloat = floatValue; } }; class OpFloatToInt : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Float data = runtime[0].mFloat; Type_Integer integerValue = static_cast (data); runtime[0].mInteger = integerValue; } }; class OpNegateInt : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; data = -data; runtime[0].mInteger = data; } }; class OpNegateFloat : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Float data = runtime[0].mFloat; data = -data; runtime[0].mFloat = data; } }; class OpIntToFloat1 : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer data = runtime[1].mInteger; Type_Float floatValue = static_cast (data); runtime[1].mFloat = floatValue; } }; class OpFloatToInt1 : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Float data = runtime[1].mFloat; Type_Integer integerValue = static_cast (data); runtime[1].mInteger = integerValue; } }; } #endif openmw-openmw-0.38.0/components/interpreter/installopcodes.cpp000066400000000000000000000136571264522266000247120ustar00rootroot00000000000000#include "installopcodes.hpp" #include #include "interpreter.hpp" #include "genericopcodes.hpp" #include "localopcodes.hpp" #include "mathopcodes.hpp" #include "controlopcodes.hpp" #include "miscopcodes.hpp" #include "scriptopcodes.hpp" #include "spatialopcodes.hpp" namespace Interpreter { void installOpcodes (Interpreter& interpreter) { // generic interpreter.installSegment0 (0, new OpPushInt); interpreter.installSegment5 (3, new OpIntToFloat); interpreter.installSegment5 (6, new OpFloatToInt); interpreter.installSegment5 (7, new OpNegateInt); interpreter.installSegment5 (8, new OpNegateFloat); interpreter.installSegment5 (17, new OpIntToFloat1); interpreter.installSegment5 (18, new OpFloatToInt1); // local variables, global variables & literals interpreter.installSegment5 (0, new OpStoreLocalShort); interpreter.installSegment5 (1, new OpStoreLocalLong); interpreter.installSegment5 (2, new OpStoreLocalFloat); interpreter.installSegment5 (4, new OpFetchIntLiteral); interpreter.installSegment5 (5, new OpFetchFloatLiteral); interpreter.installSegment5 (21, new OpFetchLocalShort); interpreter.installSegment5 (22, new OpFetchLocalLong); interpreter.installSegment5 (23, new OpFetchLocalFloat); interpreter.installSegment5 (39, new OpStoreGlobalShort); interpreter.installSegment5 (40, new OpStoreGlobalLong); interpreter.installSegment5 (41, new OpStoreGlobalFloat); interpreter.installSegment5 (42, new OpFetchGlobalShort); interpreter.installSegment5 (43, new OpFetchGlobalLong); interpreter.installSegment5 (44, new OpFetchGlobalFloat); interpreter.installSegment5 (59, new OpStoreMemberShort (false)); interpreter.installSegment5 (60, new OpStoreMemberLong (false)); interpreter.installSegment5 (61, new OpStoreMemberFloat (false)); interpreter.installSegment5 (62, new OpFetchMemberShort (false)); interpreter.installSegment5 (63, new OpFetchMemberLong (false)); interpreter.installSegment5 (64, new OpFetchMemberFloat (false)); interpreter.installSegment5 (65, new OpStoreMemberShort (true)); interpreter.installSegment5 (66, new OpStoreMemberLong (true)); interpreter.installSegment5 (67, new OpStoreMemberFloat (true)); interpreter.installSegment5 (68, new OpFetchMemberShort (true)); interpreter.installSegment5 (69, new OpFetchMemberLong (true)); interpreter.installSegment5 (70, new OpFetchMemberFloat (true)); // math interpreter.installSegment5 (9, new OpAddInt); interpreter.installSegment5 (10, new OpAddInt); interpreter.installSegment5 (11, new OpSubInt); interpreter.installSegment5 (12, new OpSubInt); interpreter.installSegment5 (13, new OpMulInt); interpreter.installSegment5 (14, new OpMulInt); interpreter.installSegment5 (15, new OpDivInt); interpreter.installSegment5 (16, new OpDivInt); interpreter.installSegment5 (19, new OpSquareRoot); interpreter.installSegment5 (26, new OpCompare >); interpreter.installSegment5 (27, new OpCompare >); interpreter.installSegment5 (28, new OpCompare >); interpreter.installSegment5 (29, new OpCompare >); interpreter.installSegment5 (30, new OpCompare >); interpreter.installSegment5 (31, new OpCompare >); interpreter.installSegment5 (32, new OpCompare >); interpreter.installSegment5 (33, new OpCompare >); interpreter.installSegment5 (34, new OpCompare >); interpreter.installSegment5 (35, new OpCompare >); interpreter.installSegment5 (36, new OpCompare >); interpreter.installSegment5 (37, new OpCompare >); // control structures interpreter.installSegment5 (20, new OpReturn); interpreter.installSegment5 (24, new OpSkipZero); interpreter.installSegment5 (25, new OpSkipNonZero); interpreter.installSegment0 (1, new OpJumpForward); interpreter.installSegment0 (2, new OpJumpBackward); // misc interpreter.installSegment3 (0, new OpMessageBox); interpreter.installSegment5 (38, new OpMenuMode); interpreter.installSegment5 (45, new OpRandom); interpreter.installSegment5 (50, new OpGetSecondsPassed); interpreter.installSegment5 (51, new OpEnable); interpreter.installSegment5 (52, new OpDisable); interpreter.installSegment5 (53, new OpGetDisabled); interpreter.installSegment5 (54, new OpEnableExplicit); interpreter.installSegment5 (55, new OpDisableExplicit); interpreter.installSegment5 (56, new OpGetDisabledExplicit); interpreter.installSegment5 (58, new OpReport); // script control interpreter.installSegment5 (46, new OpScriptRunning); interpreter.installSegment5 (47, new OpStartScript); interpreter.installSegment5 (48, new OpStopScript); interpreter.installSegment5 (71, new OpStartScriptExplicit); // spacial interpreter.installSegment5 (49, new OpGetDistance); interpreter.installSegment5 (57, new OpGetDistanceExplicit); } } openmw-openmw-0.38.0/components/interpreter/installopcodes.hpp000066400000000000000000000003171264522266000247040ustar00rootroot00000000000000#ifndef INTERPRETER_INSTALLOPCODES_H_INCLUDED #define INTERPRETER_INSTALLOPCODES_H_INCLUDED namespace Interpreter { class Interpreter; void installOpcodes (Interpreter& interpreter); } #endif openmw-openmw-0.38.0/components/interpreter/interpreter.cpp000066400000000000000000000150561264522266000242250ustar00rootroot00000000000000#include "interpreter.hpp" #include #include #include #include "opcodes.hpp" namespace Interpreter { void Interpreter::execute (Type_Code code) { unsigned int segSpec = code>>30; switch (segSpec) { case 0: { int opcode = code>>24; unsigned int arg0 = code & 0xffffff; std::map::iterator iter = mSegment0.find (opcode); if (iter==mSegment0.end()) abortUnknownCode (0, opcode); iter->second->execute (mRuntime, arg0); return; } case 1: { int opcode = (code>>24) & 0x3f; unsigned int arg0 = (code>>16) & 0xfff; unsigned int arg1 = code & 0xfff; std::map::iterator iter = mSegment1.find (opcode); if (iter==mSegment1.end()) abortUnknownCode (1, opcode); iter->second->execute (mRuntime, arg0, arg1); return; } case 2: { int opcode = (code>>20) & 0x3ff; unsigned int arg0 = code & 0xfffff; std::map::iterator iter = mSegment2.find (opcode); if (iter==mSegment2.end()) abortUnknownCode (2, opcode); iter->second->execute (mRuntime, arg0); return; } } segSpec = code>>26; switch (segSpec) { case 0x30: { int opcode = (code>>8) & 0x3ffff; unsigned int arg0 = code & 0xff; std::map::iterator iter = mSegment3.find (opcode); if (iter==mSegment3.end()) abortUnknownCode (3, opcode); iter->second->execute (mRuntime, arg0); return; } case 0x31: { int opcode = (code>>16) & 0x3ff; unsigned int arg0 = (code>>8) & 0xff; unsigned int arg1 = code & 0xff; std::map::iterator iter = mSegment4.find (opcode); if (iter==mSegment4.end()) abortUnknownCode (4, opcode); iter->second->execute (mRuntime, arg0, arg1); return; } case 0x32: { int opcode = code & 0x3ffffff; std::map::iterator iter = mSegment5.find (opcode); if (iter==mSegment5.end()) abortUnknownCode (5, opcode); iter->second->execute (mRuntime); return; } } abortUnknownSegment (code); } void Interpreter::abortUnknownCode (int segment, int opcode) { std::ostringstream error; error << "unknown opcode " << opcode << " in segment " << segment; throw std::runtime_error (error.str()); } void Interpreter::abortUnknownSegment (Type_Code code) { std::ostringstream error; error << "opcode outside of the allocated segment range: " << code; throw std::runtime_error (error.str()); } void Interpreter::begin() { if (mRunning) { mCallstack.push (mRuntime); mRuntime.clear(); } else { mRunning = true; } } void Interpreter::end() { if (mCallstack.empty()) { mRuntime.clear(); mRunning = false; } else { mRuntime = mCallstack.top(); mCallstack.pop(); } } Interpreter::Interpreter() : mRunning (false) {} Interpreter::~Interpreter() { for (std::map::iterator iter (mSegment0.begin()); iter!=mSegment0.end(); ++iter) delete iter->second; for (std::map::iterator iter (mSegment1.begin()); iter!=mSegment1.end(); ++iter) delete iter->second; for (std::map::iterator iter (mSegment2.begin()); iter!=mSegment2.end(); ++iter) delete iter->second; for (std::map::iterator iter (mSegment3.begin()); iter!=mSegment3.end(); ++iter) delete iter->second; for (std::map::iterator iter (mSegment4.begin()); iter!=mSegment4.end(); ++iter) delete iter->second; for (std::map::iterator iter (mSegment5.begin()); iter!=mSegment5.end(); ++iter) delete iter->second; } void Interpreter::installSegment0 (int code, Opcode1 *opcode) { assert(mSegment0.find(code) == mSegment0.end()); mSegment0.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment1 (int code, Opcode2 *opcode) { assert(mSegment1.find(code) == mSegment1.end()); mSegment1.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment2 (int code, Opcode1 *opcode) { assert(mSegment2.find(code) == mSegment2.end()); mSegment2.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment3 (int code, Opcode1 *opcode) { assert(mSegment3.find(code) == mSegment3.end()); mSegment3.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment4 (int code, Opcode2 *opcode) { assert(mSegment4.find(code) == mSegment4.end()); mSegment4.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment5 (int code, Opcode0 *opcode) { assert(mSegment5.find(code) == mSegment5.end()); mSegment5.insert (std::make_pair (code, opcode)); } void Interpreter::run (const Type_Code *code, int codeSize, Context& context) { assert (codeSize>=4); begin(); try { mRuntime.configure (code, codeSize, context); int opcodes = static_cast (code[0]); const Type_Code *codeBlock = code + 4; while (mRuntime.getPC()>=0 && mRuntime.getPC() #include #include "runtime.hpp" #include "types.hpp" namespace Interpreter { class Opcode0; class Opcode1; class Opcode2; class Interpreter { std::stack mCallstack; bool mRunning; Runtime mRuntime; std::map mSegment0; std::map mSegment1; std::map mSegment2; std::map mSegment3; std::map mSegment4; std::map mSegment5; // not implemented Interpreter (const Interpreter&); Interpreter& operator= (const Interpreter&); void execute (Type_Code code); void abortUnknownCode (int segment, int opcode); void abortUnknownSegment (Type_Code code); void begin(); void end(); public: Interpreter(); ~Interpreter(); void installSegment0 (int code, Opcode1 *opcode); ///< ownership of \a opcode is transferred to *this. void installSegment1 (int code, Opcode2 *opcode); ///< ownership of \a opcode is transferred to *this. void installSegment2 (int code, Opcode1 *opcode); ///< ownership of \a opcode is transferred to *this. void installSegment3 (int code, Opcode1 *opcode); ///< ownership of \a opcode is transferred to *this. void installSegment4 (int code, Opcode2 *opcode); ///< ownership of \a opcode is transferred to *this. void installSegment5 (int code, Opcode0 *opcode); ///< ownership of \a opcode is transferred to *this. void run (const Type_Code *code, int codeSize, Context& context); }; } #endif openmw-openmw-0.38.0/components/interpreter/localopcodes.hpp000066400000000000000000000226671264522266000243440ustar00rootroot00000000000000#ifndef INTERPRETER_LOCALOPCODES_H_INCLUDED #define INTERPRETER_LOCALOPCODES_H_INCLUDED #include "opcodes.hpp" #include "runtime.hpp" #include "context.hpp" namespace Interpreter { class OpStoreLocalShort : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; int index = runtime[1].mInteger; runtime.getContext().setLocalShort (index, data); runtime.pop(); runtime.pop(); } }; class OpStoreLocalLong : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; int index = runtime[1].mInteger; runtime.getContext().setLocalLong (index, data); runtime.pop(); runtime.pop(); } }; class OpStoreLocalFloat : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Float data = runtime[0].mFloat; int index = runtime[1].mInteger; runtime.getContext().setLocalFloat (index, data); runtime.pop(); runtime.pop(); } }; class OpFetchIntLiteral : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer intValue = runtime.getIntegerLiteral (runtime[0].mInteger); runtime[0].mInteger = intValue; } }; class OpFetchFloatLiteral : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Float floatValue = runtime.getFloatLiteral (runtime[0].mInteger); runtime[0].mFloat = floatValue; } }; class OpFetchLocalShort : public Opcode0 { public: virtual void execute (Runtime& runtime) { int index = runtime[0].mInteger; int value = runtime.getContext().getLocalShort (index); runtime[0].mInteger = value; } }; class OpFetchLocalLong : public Opcode0 { public: virtual void execute (Runtime& runtime) { int index = runtime[0].mInteger; int value = runtime.getContext().getLocalLong (index); runtime[0].mInteger = value; } }; class OpFetchLocalFloat : public Opcode0 { public: virtual void execute (Runtime& runtime) { int index = runtime[0].mInteger; float value = runtime.getContext().getLocalFloat (index); runtime[0].mFloat = value; } }; class OpStoreGlobalShort : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; int index = runtime[1].mInteger; std::string name = runtime.getStringLiteral (index); runtime.getContext().setGlobalShort (name, data); runtime.pop(); runtime.pop(); } }; class OpStoreGlobalLong : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; int index = runtime[1].mInteger; std::string name = runtime.getStringLiteral (index); runtime.getContext().setGlobalLong (name, data); runtime.pop(); runtime.pop(); } }; class OpStoreGlobalFloat : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Float data = runtime[0].mFloat; int index = runtime[1].mInteger; std::string name = runtime.getStringLiteral (index); runtime.getContext().setGlobalFloat (name, data); runtime.pop(); runtime.pop(); } }; class OpFetchGlobalShort : public Opcode0 { public: virtual void execute (Runtime& runtime) { int index = runtime[0].mInteger; std::string name = runtime.getStringLiteral (index); Type_Integer value = runtime.getContext().getGlobalShort (name); runtime[0].mInteger = value; } }; class OpFetchGlobalLong : public Opcode0 { public: virtual void execute (Runtime& runtime) { int index = runtime[0].mInteger; std::string name = runtime.getStringLiteral (index); Type_Integer value = runtime.getContext().getGlobalLong (name); runtime[0].mInteger = value; } }; class OpFetchGlobalFloat : public Opcode0 { public: virtual void execute (Runtime& runtime) { int index = runtime[0].mInteger; std::string name = runtime.getStringLiteral (index); Type_Float value = runtime.getContext().getGlobalFloat (name); runtime[0].mFloat = value; } }; class OpStoreMemberShort : public Opcode0 { bool mGlobal; public: OpStoreMemberShort (bool global) : mGlobal (global) {} virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; Type_Integer index = runtime[1].mInteger; std::string id = runtime.getStringLiteral (index); index = runtime[2].mInteger; std::string variable = runtime.getStringLiteral (index); runtime.getContext().setMemberShort (id, variable, data, mGlobal); runtime.pop(); runtime.pop(); runtime.pop(); } }; class OpStoreMemberLong : public Opcode0 { bool mGlobal; public: OpStoreMemberLong (bool global) : mGlobal (global) {} virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; Type_Integer index = runtime[1].mInteger; std::string id = runtime.getStringLiteral (index); index = runtime[2].mInteger; std::string variable = runtime.getStringLiteral (index); runtime.getContext().setMemberLong (id, variable, data, mGlobal); runtime.pop(); runtime.pop(); runtime.pop(); } }; class OpStoreMemberFloat : public Opcode0 { bool mGlobal; public: OpStoreMemberFloat (bool global) : mGlobal (global) {} virtual void execute (Runtime& runtime) { Type_Float data = runtime[0].mFloat; Type_Integer index = runtime[1].mInteger; std::string id = runtime.getStringLiteral (index); index = runtime[2].mInteger; std::string variable = runtime.getStringLiteral (index); runtime.getContext().setMemberFloat (id, variable, data, mGlobal); runtime.pop(); runtime.pop(); runtime.pop(); } }; class OpFetchMemberShort : public Opcode0 { bool mGlobal; public: OpFetchMemberShort (bool global) : mGlobal (global) {} virtual void execute (Runtime& runtime) { Type_Integer index = runtime[0].mInteger; std::string id = runtime.getStringLiteral (index); index = runtime[1].mInteger; std::string variable = runtime.getStringLiteral (index); runtime.pop(); int value = runtime.getContext().getMemberShort (id, variable, mGlobal); runtime[0].mInteger = value; } }; class OpFetchMemberLong : public Opcode0 { bool mGlobal; public: OpFetchMemberLong (bool global) : mGlobal (global) {} virtual void execute (Runtime& runtime) { Type_Integer index = runtime[0].mInteger; std::string id = runtime.getStringLiteral (index); index = runtime[1].mInteger; std::string variable = runtime.getStringLiteral (index); runtime.pop(); int value = runtime.getContext().getMemberLong (id, variable, mGlobal); runtime[0].mInteger = value; } }; class OpFetchMemberFloat : public Opcode0 { bool mGlobal; public: OpFetchMemberFloat (bool global) : mGlobal (global) {} virtual void execute (Runtime& runtime) { Type_Integer index = runtime[0].mInteger; std::string id = runtime.getStringLiteral (index); index = runtime[1].mInteger; std::string variable = runtime.getStringLiteral (index); runtime.pop(); float value = runtime.getContext().getMemberFloat (id, variable, mGlobal); runtime[0].mFloat = value; } }; } #endif openmw-openmw-0.38.0/components/interpreter/mathopcodes.hpp000066400000000000000000000053431264522266000241730ustar00rootroot00000000000000#ifndef INTERPRETER_MATHOPCODES_H_INCLUDED #define INTERPRETER_MATHOPCODES_H_INCLUDED #include #include #include "opcodes.hpp" #include "runtime.hpp" namespace Interpreter { template class OpAddInt : public Opcode0 { public: virtual void execute (Runtime& runtime) { T result = getData (runtime[1]) + getData (runtime[0]); runtime.pop(); getData (runtime[0]) = result; } }; template class OpSubInt : public Opcode0 { public: virtual void execute (Runtime& runtime) { T result = getData (runtime[1]) - getData (runtime[0]); runtime.pop(); getData (runtime[0]) = result; } }; template class OpMulInt : public Opcode0 { public: virtual void execute (Runtime& runtime) { T result = getData (runtime[1]) * getData (runtime[0]); runtime.pop(); getData (runtime[0]) = result; } }; template class OpDivInt : public Opcode0 { public: virtual void execute (Runtime& runtime) { T left = getData (runtime[0]); if (left==0) throw std::runtime_error ("division by zero"); T result = getData (runtime[1]) / left; runtime.pop(); getData (runtime[0]) = result; } }; class OpSquareRoot : public Opcode0 { public: virtual void execute (Runtime& runtime) { Type_Float value = runtime[0].mFloat; if (value<0) throw std::runtime_error ( "square root of negative number (we aren't that imaginary)"); value = std::sqrt (value); runtime[0].mFloat = value; } }; template class OpCompare : public Opcode0 { public: virtual void execute (Runtime& runtime) { int result = C() (getData (runtime[1]), getData (runtime[0])); runtime.pop(); runtime[0].mInteger = result; } }; } #endif openmw-openmw-0.38.0/components/interpreter/miscopcodes.hpp000066400000000000000000000145151264522266000241760ustar00rootroot00000000000000#ifndef INTERPRETER_MISCOPCODES_H_INCLUDED #define INTERPRETER_MISCOPCODES_H_INCLUDED #include #include #include #include #include #include #include "opcodes.hpp" #include "runtime.hpp" #include "defines.hpp" #include namespace Interpreter { inline std::string formatMessage (const std::string& message, Runtime& runtime) { std::string formattedMessage; for (std::size_t i=0; i buttons; for (std::size_t i=0; i #include #include namespace Interpreter { Runtime::Runtime() : mContext (0), mCode (0), mCodeSize(0), mPC (0) {} int Runtime::getPC() const { return mPC; } int Runtime::getIntegerLiteral (int index) const { assert (index>=0 && index (mCode[1])); const Type_Code *literalBlock = mCode + 4 + mCode[0]; return *reinterpret_cast (&literalBlock[index]); } float Runtime::getFloatLiteral (int index) const { assert (index>=0 && index (mCode[2])); const Type_Code *literalBlock = mCode + 4 + mCode[0] + mCode[1]; return *reinterpret_cast (&literalBlock[index]); } std::string Runtime::getStringLiteral (int index) const { assert (index>=0 && static_cast (mCode[3])>0); const char *literalBlock = reinterpret_cast (mCode + 4 + mCode[0] + mCode[1] + mCode[2]); int offset = 0; for (; index; --index) { offset += std::strlen (literalBlock+offset) + 1; assert (offset/4 (mCode[3])); } return literalBlock+offset; } void Runtime::configure (const Type_Code *code, int codeSize, Context& context) { clear(); mContext = &context; mCode = code; mCodeSize = codeSize; mPC = 0; } void Runtime::clear() { mContext = 0; mCode = 0; mCodeSize = 0; mStack.clear(); } void Runtime::setPC (int PC) { mPC = PC; } void Runtime::push (const Data& data) { mStack.push_back (data); } void Runtime::push (Type_Integer value) { Data data; data.mInteger = value; push (data); } void Runtime::push (Type_Float value) { Data data; data.mFloat = value; push (data); } void Runtime::pop() { if (mStack.empty()) throw std::runtime_error ("stack underflow"); mStack.resize (mStack.size()-1); } Data& Runtime::operator[] (int Index) { if (Index<0 || Index>=static_cast (mStack.size())) throw std::runtime_error ("stack index out of range"); return mStack[mStack.size()-Index-1]; } Context& Runtime::getContext() { assert (mContext); return *mContext; } } openmw-openmw-0.38.0/components/interpreter/runtime.hpp000066400000000000000000000027421264522266000233500ustar00rootroot00000000000000#ifndef INTERPRETER_RUNTIME_H_INCLUDED #define INTERPRETER_RUNTIME_H_INCLUDED #include #include #include "types.hpp" namespace Interpreter { class Context; /// Runtime data and engine interface class Runtime { Context *mContext; const Type_Code *mCode; int mCodeSize; int mPC; std::vector mStack; public: Runtime (); int getPC() const; ///< return program counter. int getIntegerLiteral (int index) const; float getFloatLiteral (int index) const; std::string getStringLiteral (int index) const; void configure (const Type_Code *code, int codeSize, Context& context); ///< \a context and \a code must exist as least until either configure, clear or /// the destructor is called. \a codeSize is given in 32-bit words. void clear(); void setPC (int PC); ///< set program counter. void push (const Data& data); ///< push data on stack void push (Type_Integer value); ///< push integer data on stack. void push (Type_Float value); ///< push float data on stack. void pop(); ///< pop stack Data& operator[] (int Index); ///< Access stack member, counted from the top. Context& getContext(); }; } #endif openmw-openmw-0.38.0/components/interpreter/scriptopcodes.hpp000066400000000000000000000031701264522266000245420ustar00rootroot00000000000000#ifndef INTERPRETER_SCRIPTOPCODES_H_INCLUDED #define INTERPRETER_SCRIPTOPCODES_H_INCLUDED #include "opcodes.hpp" #include "runtime.hpp" #include "context.hpp" namespace Interpreter { class OpScriptRunning : public Opcode0 { public: virtual void execute (Runtime& runtime) { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime[0].mInteger = runtime.getContext().isScriptRunning (name); } }; class OpStartScript : public Opcode0 { public: virtual void execute (Runtime& runtime) { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); runtime.getContext().startScript (name, runtime.getContext().getTargetId()); } }; class OpStartScriptExplicit : public Opcode0 { public: virtual void execute (Runtime& runtime) { std::string targetId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); runtime.getContext().startScript (name, targetId); } }; class OpStopScript : public Opcode0 { public: virtual void execute (Runtime& runtime) { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); runtime.getContext().stopScript (name); } }; } #endif openmw-openmw-0.38.0/components/interpreter/spatialopcodes.hpp000066400000000000000000000022761264522266000247010ustar00rootroot00000000000000#ifndef INTERPRETER_SPATIALOPCODES_H_INCLUDED #define INTERPRETER_SPATIALOPCODES_H_INCLUDED #include "opcodes.hpp" #include "runtime.hpp" namespace Interpreter { class OpGetDistance : public Opcode0 { public: virtual void execute (Runtime& runtime) { std::string name = runtime.getStringLiteral (runtime[0].mInteger); Type_Float distance = runtime.getContext().getDistance (name); runtime[0].mFloat = distance; } }; class OpGetDistanceExplicit : public Opcode0 { public: virtual void execute (Runtime& runtime) { int index = runtime[0].mInteger; runtime.pop(); std::string id = runtime.getStringLiteral (index); std::string name = runtime.getStringLiteral (runtime[0].mInteger); Type_Float distance = runtime.getContext().getDistance (name, id); runtime[0].mFloat = distance; } }; } #endif openmw-openmw-0.38.0/components/interpreter/types.hpp000066400000000000000000000014351264522266000230270ustar00rootroot00000000000000#ifndef INTERPRETER_TYPES_H_INCLUDED #define INTERPRETER_TYPES_H_INCLUDED #include namespace Interpreter { typedef unsigned int Type_Code; // 32 bit typedef unsigned int Type_Data; // 32 bit typedef short Type_Short; // 16 bit typedef int Type_Integer; // 32 bit typedef float Type_Float; // 32 bit union Data { Type_Integer mInteger; Type_Float mFloat; }; template T& getData (Data& data) { throw std::runtime_error ("unsupported data type"); } template<> inline Type_Integer& getData (Data& data) { return data.mInteger; } template<> inline Type_Float& getData (Data& data) { return data.mFloat; } } #endif openmw-openmw-0.38.0/components/loadinglistener/000077500000000000000000000000001264522266000217675ustar00rootroot00000000000000openmw-openmw-0.38.0/components/loadinglistener/loadinglistener.hpp000066400000000000000000000033701264522266000256660ustar00rootroot00000000000000#ifndef COMPONENTS_LOADINGLISTENER_H #define COMPONENTS_LOADINGLISTENER_H #include namespace Loading { class Listener { public: /// Set a text label to show on the loading screen. /// @param label The label /// @param important Is the label considered important to show? /// @note "non-important" labels may not show on screen if the loading process went so fast /// that the implementation decided not to show a loading screen at all. "important" labels /// will show in a separate message-box if the loading screen was not shown. virtual void setLabel (const std::string& label, bool important=false) {} /// Start a loading sequence. Must call loadingOff() when done. /// @note To get the loading screen to actually update, you must call setProgress / increaseProgress periodically. /// @note It is best to use the ScopedLoad object instead of using loadingOn()/loadingOff() directly, /// so that the loading is exception safe. virtual void loadingOn() {} virtual void loadingOff() {} /// Set the total range of progress (e.g. the number of objects to load). virtual void setProgressRange (size_t range) {} /// Set current progress. Valid range is [0, progressRange) virtual void setProgress (size_t value) {} /// Increase current progress, default by 1. virtual void increaseProgress (size_t increase = 1) {} }; /// @brief Used for stopping a loading sequence when the object goes out of scope struct ScopedLoad { ScopedLoad(Listener* l) : mListener(l) { mListener->loadingOn(); } ~ScopedLoad() { mListener->loadingOff(); } Listener* mListener; }; } #endif openmw-openmw-0.38.0/components/misc/000077500000000000000000000000001264522266000175375ustar00rootroot00000000000000openmw-openmw-0.38.0/components/misc/resourcehelpers.cpp000066400000000000000000000105721264522266000234620ustar00rootroot00000000000000#include "resourcehelpers.hpp" #include #include #include namespace { struct MatchPathSeparator { bool operator()( char ch ) const { return ch == '\\' || ch == '/'; } }; std::string getBasename( std::string const& pathname ) { return std::string( std::find_if( pathname.rbegin(), pathname.rend(), MatchPathSeparator() ).base(), pathname.end() ); } } bool Misc::ResourceHelpers::changeExtensionToDds(std::string &path) { std::string::size_type pos = path.rfind('.'); if(pos != std::string::npos && path.compare(pos, path.length() - pos, ".dds") != 0) { path.replace(pos, path.length(), ".dds"); return true; } return false; } std::string Misc::ResourceHelpers::correctResourcePath(const std::string &topLevelDirectory, const std::string &resPath, const VFS::Manager* vfs) { /* Bethesda at some point converted all their BSA * textures from tga to dds for increased load speed, but all * texture file name references were kept as .tga. */ std::string prefix1 = topLevelDirectory + '\\'; std::string prefix2 = topLevelDirectory + '/'; std::string correctedPath = resPath; Misc::StringUtils::lowerCaseInPlace(correctedPath); // Apparently, leading separators are allowed while (correctedPath.size() && (correctedPath[0] == '/' || correctedPath[0] == '\\')) correctedPath.erase(0, 1); if(correctedPath.compare(0, prefix1.size(), prefix1.data()) != 0 && correctedPath.compare(0, prefix2.size(), prefix2.data()) != 0) correctedPath = prefix1 + correctedPath; std::string origExt = correctedPath; // since we know all (GOTY edition or less) textures end // in .dds, we change the extension bool changedToDds = changeExtensionToDds(correctedPath); if (vfs->exists(correctedPath)) return correctedPath; // if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods) // verify, and revert if false (this call succeeds quickly, but fails slowly) if (changedToDds && vfs->exists(origExt)) return origExt; // fall back to a resource in the top level directory if it exists std::string fallback = topLevelDirectory + "\\" + getBasename(correctedPath); if (vfs->exists(fallback)) return fallback; if (changedToDds) { fallback = topLevelDirectory + "\\" + getBasename(origExt); if (vfs->exists(fallback)) return fallback; } return correctedPath; } std::string Misc::ResourceHelpers::correctTexturePath(const std::string &resPath, const VFS::Manager* vfs) { static const std::string dir = "textures"; return correctResourcePath(dir, resPath, vfs); } std::string Misc::ResourceHelpers::correctIconPath(const std::string &resPath, const VFS::Manager* vfs) { static const std::string dir = "icons"; return correctResourcePath(dir, resPath, vfs); } std::string Misc::ResourceHelpers::correctBookartPath(const std::string &resPath, const VFS::Manager* vfs) { static const std::string dir = "bookart"; std::string image = correctResourcePath(dir, resPath, vfs); return image; } std::string Misc::ResourceHelpers::correctBookartPath(const std::string &resPath, int width, int height, const VFS::Manager* vfs) { std::string image = correctBookartPath(resPath, vfs); // Apparently a bug with some morrowind versions, they reference the image without the size suffix. // So if the image isn't found, try appending the size. if (!vfs->exists(image)) { std::stringstream str; str << image.substr(0, image.rfind('.')) << "_" << width << "_" << height << image.substr(image.rfind('.')); image = Misc::ResourceHelpers::correctBookartPath(str.str(), vfs); } return image; } std::string Misc::ResourceHelpers::correctActorModelPath(const std::string &resPath, const VFS::Manager* vfs) { std::string mdlname = resPath; std::string::size_type p = mdlname.rfind('\\'); if(p == std::string::npos) p = mdlname.rfind('/'); if(p != std::string::npos) mdlname.insert(mdlname.begin()+p+1, 'x'); else mdlname.insert(mdlname.begin(), 'x'); if(!vfs->exists(mdlname)) { return resPath; } return mdlname; } openmw-openmw-0.38.0/components/misc/resourcehelpers.hpp000066400000000000000000000022361264522266000234650ustar00rootroot00000000000000#ifndef MISC_RESOURCEHELPERS_H #define MISC_RESOURCEHELPERS_H #include namespace VFS { class Manager; } namespace Misc { // Workarounds for messy resource handling in vanilla morrowind // The below functions are provided on a opt-in basis, instead of built into the VFS, // so we have the opportunity to use proper resource handling for content created in OpenMW-CS. namespace ResourceHelpers { bool changeExtensionToDds(std::string &path); std::string correctResourcePath(const std::string &topLevelDirectory, const std::string &resPath, const VFS::Manager* vfs); std::string correctTexturePath(const std::string &resPath, const VFS::Manager* vfs); std::string correctIconPath(const std::string &resPath, const VFS::Manager* vfs); std::string correctBookartPath(const std::string &resPath, const VFS::Manager* vfs); std::string correctBookartPath(const std::string &resPath, int width, int height, const VFS::Manager* vfs); /// Use "xfoo.nif" instead of "foo.nif" if available std::string correctActorModelPath(const std::string &resPath, const VFS::Manager* vfs); } } #endif openmw-openmw-0.38.0/components/misc/rng.cpp000066400000000000000000000011001264522266000210210ustar00rootroot00000000000000#include "rng.hpp" #include #include namespace Misc { void Rng::init() { std::srand(static_cast(std::time(NULL))); } float Rng::rollProbability() { return static_cast(std::rand() / (static_cast(RAND_MAX)+1.0)); } float Rng::rollClosedProbability() { return static_cast(std::rand() / static_cast(RAND_MAX)); } int Rng::rollDice(int max) { return static_cast((std::rand() / (static_cast(RAND_MAX)+1.0)) * (max)); } } openmw-openmw-0.38.0/components/misc/rng.hpp000066400000000000000000000012331264522266000210350ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_MISC_RNG_H #define OPENMW_COMPONENTS_MISC_RNG_H #include namespace Misc { /* Provides central implementation of the RNG logic */ class Rng { public: /// seed the RNG static void init(); /// return value in range [0.0f, 1.0f) <- note open upper range. static float rollProbability(); /// return value in range [0.0f, 1.0f] <- note closed upper range. static float rollClosedProbability(); /// return value in range [0, max) <- note open upper range. static int rollDice(int max); /// return value in range [0, 99] static int roll0to99() { return rollDice(100); } }; } #endif openmw-openmw-0.38.0/components/misc/stringops.hpp000066400000000000000000000057441264522266000223120ustar00rootroot00000000000000#ifndef MISC_STRINGOPS_H #define MISC_STRINGOPS_H #include #include #include namespace Misc { class StringUtils { struct ci { bool operator()(char x, char y) const { return toLower(x) < toLower(y); } }; public: /// Plain and simple locale-unaware toLower. Anything from A to Z is lower-cased, multibyte characters are unchanged. /// Don't use std::tolower(char, locale&) because that is abysmally slow. /// Don't use tolower(int) because that depends on global locale. static char toLower(char c) { switch(c) { case 'A':return 'a'; case 'B':return 'b'; case 'C':return 'c'; case 'D':return 'd'; case 'E':return 'e'; case 'F':return 'f'; case 'G':return 'g'; case 'H':return 'h'; case 'I':return 'i'; case 'J':return 'j'; case 'K':return 'k'; case 'L':return 'l'; case 'M':return 'm'; case 'N':return 'n'; case 'O':return 'o'; case 'P':return 'p'; case 'Q':return 'q'; case 'R':return 'r'; case 'S':return 's'; case 'T':return 't'; case 'U':return 'u'; case 'V':return 'v'; case 'W':return 'w'; case 'X':return 'x'; case 'Y':return 'y'; case 'Z':return 'z'; default:return c; }; } static bool ciLess(const std::string &x, const std::string &y) { return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci()); } static bool ciEqual(const std::string &x, const std::string &y) { if (x.size() != y.size()) { return false; } std::string::const_iterator xit = x.begin(); std::string::const_iterator yit = y.begin(); for (; xit != x.end(); ++xit, ++yit) { if (toLower(*xit) != toLower(*yit)) { return false; } } return true; } static int ciCompareLen(const std::string &x, const std::string &y, size_t len) { std::string::const_iterator xit = x.begin(); std::string::const_iterator yit = y.begin(); for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len) { int res = *xit - *yit; if(res != 0 && toLower(*xit) != toLower(*yit)) return (res > 0) ? 1 : -1; } if(len > 0) { if(xit != x.end()) return 1; if(yit != y.end()) return -1; } return 0; } /// Transforms input string to lower case w/o copy static void lowerCaseInPlace(std::string &inout) { for (unsigned int i=0; i class Utf8Stream { public: typedef uint32_t UnicodeChar; typedef unsigned char const * Point; //static const unicode_char sBadChar = 0xFFFFFFFF; gcc can't handle this static UnicodeChar sBadChar () { return UnicodeChar (0xFFFFFFFF); } Utf8Stream (Point begin, Point end) : cur (begin), nxt (begin), end (end), val(Utf8Stream::sBadChar()) { } Utf8Stream (std::pair range) : cur (range.first), nxt (range.first), end (range.second), val(Utf8Stream::sBadChar()) { } bool eof () const { return cur == end; } Point current () const { return cur; } UnicodeChar peek () { if (cur == nxt) next (); return val; } UnicodeChar consume () { if (cur == nxt) next (); cur = nxt; return val; } static std::pair decode (Point cur, Point end) { if ((*cur & 0x80) == 0) { UnicodeChar chr = *cur++; return std::make_pair (chr, cur); } int octets; UnicodeChar chr; boost::tie (octets, chr) = octet_count (*cur++); if (octets > 5) return std::make_pair (sBadChar(), cur); Point eoc = cur + octets; if (eoc > end) return std::make_pair (sBadChar(), cur); while (cur != eoc) { if ((*cur & 0xC0) != 0x80) // check continuation mark return std::make_pair (sBadChar(), cur); chr = (chr << 6) | UnicodeChar ((*cur++) & 0x3F); } return std::make_pair (chr, cur); } private: static std::pair octet_count (unsigned char octet) { int octets; unsigned char mark = 0xC0; unsigned char mask = 0xE0; for (octets = 1; octets <= 5; ++octets) { if ((octet & mask) == mark) break; mark = (mark >> 1) | 0x80; mask = (mask >> 1) | 0x80; } return std::make_pair (octets, octet & ~mask); } void next () { boost::tie (val, nxt) = decode (nxt, end); } Point cur; Point nxt; Point end; UnicodeChar val; }; #endif openmw-openmw-0.38.0/components/myguiplatform/000077500000000000000000000000001264522266000215035ustar00rootroot00000000000000openmw-openmw-0.38.0/components/myguiplatform/additivelayer.cpp000066400000000000000000000014711264522266000250400ustar00rootroot00000000000000#include "additivelayer.hpp" #include #include #include "myguirendermanager.hpp" namespace osgMyGUI { AdditiveLayer::AdditiveLayer() { mStateSet = new osg::StateSet; mStateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE)); } AdditiveLayer::~AdditiveLayer() { // defined in .cpp file since we can't delete incomplete types } void AdditiveLayer::renderToTarget(MyGUI::IRenderTarget *_target, bool _update) { RenderManager& renderManager = static_cast(MyGUI::RenderManager::getInstance()); renderManager.setInjectState(mStateSet.get()); MyGUI::OverlappedLayer::renderToTarget(_target, _update); renderManager.setInjectState(NULL); } } openmw-openmw-0.38.0/components/myguiplatform/additivelayer.hpp000066400000000000000000000011551264522266000250440ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_ADDITIVELAYER #define OPENMW_COMPONENTS_MYGUIPLATFORM_ADDITIVELAYER #include #include namespace osg { class StateSet; } namespace osgMyGUI { /// @brief A Layer rendering with additive blend mode. class AdditiveLayer : public MyGUI::OverlappedLayer { public: MYGUI_RTTI_DERIVED( AdditiveLayer ) AdditiveLayer(); ~AdditiveLayer(); virtual void renderToTarget(MyGUI::IRenderTarget* _target, bool _update); private: osg::ref_ptr mStateSet; }; } #endif openmw-openmw-0.38.0/components/myguiplatform/myguidatamanager.cpp000066400000000000000000000027231264522266000255320ustar00rootroot00000000000000#include "myguidatamanager.hpp" #include #include #include #include namespace osgMyGUI { void DataManager::setResourcePath(const std::string &path) { mResourcePath = path; } MyGUI::IDataStream *DataManager::getData(const std::string &name) { std::string fullpath = getDataPath(name); std::auto_ptr stream; stream.reset(new boost::filesystem::ifstream); stream->open(fullpath, std::ios::binary); if (stream->fail()) { std::cerr << "DataManager::getData: Failed to open '" << name << "'" << std::endl; return NULL; } return new MyGUI::DataFileStream(stream.release()); } void DataManager::freeData(MyGUI::IDataStream *data) { delete data; } bool DataManager::isDataExist(const std::string &name) { std::string fullpath = mResourcePath + "/" + name; return boost::filesystem::exists(fullpath); } const MyGUI::VectorString &DataManager::getDataListNames(const std::string &pattern) { // TODO: pattern matching (unused?) static MyGUI::VectorString strings; strings.clear(); strings.push_back(getDataPath(pattern)); return strings; } const std::string &DataManager::getDataPath(const std::string &name) { static std::string result; result.clear(); if (!isDataExist(name)) { return result; } result = mResourcePath + "/" + name; return result; } } openmw-openmw-0.38.0/components/myguiplatform/myguidatamanager.hpp000066400000000000000000000023451264522266000255370ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIDATAMANAGER_H #define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIDATAMANAGER_H #include namespace osgMyGUI { class DataManager : public MyGUI::DataManager { public: void initialise() {} void shutdown() {} void setResourcePath(const std::string& path); /** Get data stream from specified resource name. @param _name Resource name (usually file name). */ virtual MyGUI::IDataStream* getData(const std::string& _name); /** Free data stream. @param _data Data stream. */ virtual void freeData(MyGUI::IDataStream* _data); /** Is data with specified name exist. @param _name Resource name. */ virtual bool isDataExist(const std::string& _name); /** Get all data names with names that matches pattern. @param _pattern Pattern to match (for example "*.layout"). */ virtual const MyGUI::VectorString& getDataListNames(const std::string& _pattern); /** Get full path to data. @param _name Resource name. @return Return full path to specified data. */ virtual const std::string& getDataPath(const std::string& _name); private: std::string mResourcePath; }; } #endif openmw-openmw-0.38.0/components/myguiplatform/myguiloglistener.cpp000066400000000000000000000022471264522266000256160ustar00rootroot00000000000000#include "myguiloglistener.hpp" #include #include #include namespace osgMyGUI { void CustomLogListener::open() { mStream.open(boost::filesystem::path(mFileName), std::ios_base::out); } void CustomLogListener::close() { if (mStream.is_open()) mStream.close(); } void CustomLogListener::flush() { if (mStream.is_open()) mStream.flush(); } void CustomLogListener::log(const std::string& _section, MyGUI::LogLevel _level, const struct tm* _time, const std::string& _message, const char* _file, int _line) { if (mStream.is_open()) { const char* separator = " | "; mStream << std::setw(2) << std::setfill('0') << _time->tm_hour << ":" << std::setw(2) << std::setfill('0') << _time->tm_min << ":" << std::setw(2) << std::setfill('0') << _time->tm_sec << separator << _section << separator << _level.print() << separator << _message << separator << _file << separator << _line << std::endl; } } } openmw-openmw-0.38.0/components/myguiplatform/myguiloglistener.hpp000066400000000000000000000036301264522266000256200ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_LOGLISTENER_H #define OPENMW_COMPONENTS_MYGUIPLATFORM_LOGLISTENER_H #include #include #include #include #include #include namespace osgMyGUI { /// \brief Custom MyGUI::ILogListener interface implementation /// being able to portably handle UTF-8 encoded path. /// \todo try patching MyGUI to make this easier class CustomLogListener : public MyGUI::ILogListener { public: CustomLogListener(const std::string &name) : mFileName(name) {} ~CustomLogListener() {} virtual void open(); virtual void close(); virtual void flush(); virtual void log(const std::string& _section, MyGUI::LogLevel _level, const struct tm* _time, const std::string& _message, const char* _file, int _line); const std::string& getFileName() const { return mFileName; } private: boost::filesystem::ofstream mStream; std::string mFileName; }; /// \brief Helper class holding data that required during /// MyGUI log creation class LogFacility { MyGUI::ConsoleLogListener mConsole; CustomLogListener mFile; MyGUI::LevelLogFilter mFilter; MyGUI::LogSource mSource; public: LogFacility(const std::string &output, bool console) : mFile(output) { mConsole.setEnabled(console); mFilter.setLoggingLevel(MyGUI::LogLevel::Info); mSource.addLogListener(&mFile); mSource.addLogListener(&mConsole); mSource.setLogFilter(&mFilter); mSource.open(); } MyGUI::LogSource *getSource() { return &mSource; } }; } #endif openmw-openmw-0.38.0/components/myguiplatform/myguiplatform.cpp000066400000000000000000000026461264522266000251160ustar00rootroot00000000000000#include "myguiplatform.hpp" #include "myguirendermanager.hpp" #include "myguidatamanager.hpp" #include "myguiloglistener.hpp" namespace osgMyGUI { Platform::Platform(osgViewer::Viewer *viewer, osg::Group *guiRoot, Resource::TextureManager *textureManager, float uiScalingFactor) : mRenderManager(nullptr) , mDataManager(nullptr) , mLogManager(nullptr) , mLogFacility(nullptr) { mLogManager = new MyGUI::LogManager(); mRenderManager = new RenderManager(viewer, guiRoot, textureManager, uiScalingFactor); mDataManager = new DataManager(); } Platform::~Platform() { delete mRenderManager; mRenderManager = nullptr; delete mDataManager; mDataManager = nullptr; delete mLogManager; mLogManager = nullptr; delete mLogFacility; mLogFacility = nullptr; } void Platform::initialise(const std::string &resourcePath, const std::string &_logName) { if (!_logName.empty() && !mLogFacility) { mLogFacility = new LogFacility(_logName, false); mLogManager->addLogSource(mLogFacility->getSource()); } mDataManager->setResourcePath(resourcePath); mRenderManager->initialise(); mDataManager->initialise(); } void Platform::shutdown() { mRenderManager->shutdown(); mDataManager->shutdown(); } RenderManager *Platform::getRenderManagerPtr() { return mRenderManager; } DataManager *Platform::getDataManagerPtr() { return mDataManager; } } openmw-openmw-0.38.0/components/myguiplatform/myguiplatform.hpp000066400000000000000000000020451264522266000251140ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIPLATFORM_H #define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIPLATFORM_H #include namespace osgViewer { class Viewer; } namespace osg { class Group; } namespace Resource { class TextureManager; } namespace MyGUI { class LogManager; } namespace osgMyGUI { class RenderManager; class DataManager; class LogFacility; class Platform { public: Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::TextureManager* textureManager, float uiScalingFactor); ~Platform(); void initialise(const std::string& resourcePath, const std::string& _logName = "MyGUI.log"); void shutdown(); RenderManager* getRenderManagerPtr(); DataManager* getDataManagerPtr(); private: RenderManager* mRenderManager; DataManager* mDataManager; MyGUI::LogManager* mLogManager; LogFacility* mLogFacility; void operator=(const Platform&); Platform(const Platform&); }; } #endif openmw-openmw-0.38.0/components/myguiplatform/myguirendermanager.cpp000066400000000000000000000361351264522266000261040ustar00rootroot00000000000000#include "myguirendermanager.hpp" #include #include #include #include #include #include #include #include #include #include #include "myguitexture.hpp" #define MYGUI_PLATFORM_LOG_SECTION "Platform" #define MYGUI_PLATFORM_LOG(level, text) MYGUI_LOGGING(MYGUI_PLATFORM_LOG_SECTION, level, text) #define MYGUI_PLATFORM_EXCEPT(dest) do { \ MYGUI_PLATFORM_LOG(Critical, dest); \ MYGUI_DBG_BREAK;\ std::ostringstream stream; \ stream << dest << "\n"; \ MYGUI_BASE_EXCEPT(stream.str().c_str(), "MyGUI"); \ } while(0) #define MYGUI_PLATFORM_ASSERT(exp, dest) do { \ if ( ! (exp) ) \ { \ MYGUI_PLATFORM_LOG(Critical, dest); \ MYGUI_DBG_BREAK;\ std::ostringstream stream; \ stream << dest << "\n"; \ MYGUI_BASE_EXCEPT(stream.str().c_str(), "MyGUI"); \ } \ } while(0) namespace osgMyGUI { class Drawable : public osg::Drawable { osgMyGUI::RenderManager *mParent; osg::ref_ptr mStateSet; public: // Stage 0: update widget animations and controllers. Run during the Update traversal. class FrameUpdate : public osg::Drawable::UpdateCallback { public: FrameUpdate() : mRenderManager(NULL) { } void setRenderManager(osgMyGUI::RenderManager* renderManager) { mRenderManager = renderManager; } virtual void update(osg::NodeVisitor*, osg::Drawable*) { if (mRenderManager) mRenderManager->update(); } private: osgMyGUI::RenderManager* mRenderManager; }; // Stage 1: collect draw calls. Run during the Cull traversal. class CollectDrawCalls : public osg::Drawable::CullCallback { public: CollectDrawCalls() : mRenderManager(NULL) { } void setRenderManager(osgMyGUI::RenderManager* renderManager) { mRenderManager = renderManager; } virtual bool cull(osg::NodeVisitor*, osg::Drawable*, osg::State*) const { if (!mRenderManager) return false; mRenderManager->collectDrawCalls(); return false; } private: osgMyGUI::RenderManager* mRenderManager; }; // Stage 2: execute the draw calls. Run during the Draw traversal. May run in parallel with the update traversal of the next frame. virtual void drawImplementation(osg::RenderInfo &renderInfo) const { osg::State *state = renderInfo.getState(); state->pushStateSet(mStateSet); state->apply(); state->disableAllVertexArrays(); state->setClientActiveTextureUnit(0); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); mReadFrom = (mReadFrom+1)%sNumBuffers; const std::vector& vec = mBatchVector[mReadFrom]; for (std::vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { const Batch& batch = *it; osg::VertexBufferObject *vbo = batch.mVertexBuffer; if (batch.mStateSet) { state->pushStateSet(batch.mStateSet); state->apply(); } osg::Texture2D* texture = batch.mTexture; if(texture) state->applyTextureAttribute(0, texture); // VBOs disabled due to crash in OSG: http://forum.openscenegraph.org/viewtopic.php?t=14909 osg::GLBufferObject* bufferobject = 0;//state->isVertexBufferObjectSupported() ? vbo->getOrCreateGLBufferObject(state->getContextID()) : 0; if (0)//bufferobject) { state->bindVertexBufferObject(bufferobject); glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)NULL); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)NULL + 12); glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)NULL + 16); } else { glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)vbo->getArray(0)->getDataPointer()); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)vbo->getArray(0)->getDataPointer() + 12); glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)vbo->getArray(0)->getDataPointer() + 16); } glDrawArrays(GL_TRIANGLES, 0, batch.mVertexCount); if (batch.mStateSet) { state->popStateSet(); state->apply(); } } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_COLOR_ARRAY); state->popStateSet(); state->unbindVertexBufferObject(); state->dirtyAllVertexArrays(); state->disableAllVertexArrays(); } public: Drawable(osgMyGUI::RenderManager *parent = nullptr) : mParent(parent) , mWriteTo(0) , mReadFrom(0) { setSupportsDisplayList(false); osg::ref_ptr collectDrawCalls = new CollectDrawCalls; collectDrawCalls->setRenderManager(mParent); setCullCallback(collectDrawCalls); osg::ref_ptr frameUpdate = new FrameUpdate; frameUpdate->setRenderManager(mParent); setUpdateCallback(frameUpdate); mStateSet = new osg::StateSet; mStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); mStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON); mStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); mStateSet->setMode(GL_BLEND, osg::StateAttribute::ON); } Drawable(const Drawable ©, const osg::CopyOp ©op=osg::CopyOp::SHALLOW_COPY) : osg::Drawable(copy, copyop) , mParent(copy.mParent) , mStateSet(copy.mStateSet) , mWriteTo(0) , mReadFrom(0) { } // Defines the necessary information for a draw call struct Batch { // May be empty osg::ref_ptr mTexture; osg::ref_ptr mVertexBuffer; // need to hold on to this too as the mVertexBuffer does not hold a ref to its own array osg::ref_ptr mArray; // optional osg::ref_ptr mStateSet; size_t mVertexCount; }; void addBatch(const Batch& batch) { mBatchVector[mWriteTo].push_back(batch); } void clear() { mWriteTo = (mWriteTo+1)%sNumBuffers; mBatchVector[mWriteTo].clear(); } META_Object(osgMyGUI, Drawable) private: // 2 would be enough in most cases, use 4 to get stereo working static const int sNumBuffers = 4; // double buffering approach, to avoid the need for synchronization with the draw thread std::vector mBatchVector[sNumBuffers]; int mWriteTo; mutable int mReadFrom; }; class OSGVertexBuffer : public MyGUI::IVertexBuffer { osg::ref_ptr mBuffer; osg::ref_ptr mVertexArray; size_t mNeedVertexCount; bool mQueuedForDrawing; void destroy(); void create(); public: OSGVertexBuffer(); virtual ~OSGVertexBuffer(); void markAsQueuedForDrawing(); virtual void setVertexCount(size_t count); virtual size_t getVertexCount(); virtual MyGUI::Vertex *lock(); virtual void unlock(); /*internal:*/ osg::VertexBufferObject *getBuffer() const { return mBuffer.get(); } osg::UByteArray *getArray() const { return mVertexArray.get(); } }; OSGVertexBuffer::OSGVertexBuffer() : mNeedVertexCount(0) , mQueuedForDrawing(false) { } OSGVertexBuffer::~OSGVertexBuffer() { destroy(); } void OSGVertexBuffer::markAsQueuedForDrawing() { mQueuedForDrawing = true; } void OSGVertexBuffer::setVertexCount(size_t count) { if(count == mNeedVertexCount) return; mNeedVertexCount = count; } size_t OSGVertexBuffer::getVertexCount() { return mNeedVertexCount; } MyGUI::Vertex *OSGVertexBuffer::lock() { if (mQueuedForDrawing || !mVertexArray) { // Force recreating the buffer, to make sure we are not modifying a buffer currently // queued for rendering in the last frame's draw thread. // a more efficient solution might be double buffering destroy(); create(); mQueuedForDrawing = false; } else { mVertexArray->resize(mNeedVertexCount * sizeof(MyGUI::Vertex)); } MYGUI_PLATFORM_ASSERT(mBuffer.valid(), "Vertex buffer is not created"); return (MyGUI::Vertex*)&(*mVertexArray)[0]; } void OSGVertexBuffer::unlock() { mVertexArray->dirty(); mBuffer->dirty(); } void OSGVertexBuffer::destroy() { mBuffer = nullptr; mVertexArray = nullptr; } void OSGVertexBuffer::create() { MYGUI_PLATFORM_ASSERT(!mBuffer.valid(), "Vertex buffer already exist"); mVertexArray = new osg::UByteArray(mNeedVertexCount*sizeof(MyGUI::Vertex)); mBuffer = new osg::VertexBufferObject; mBuffer->setDataVariance(osg::Object::DYNAMIC); mBuffer->setUsage(GL_DYNAMIC_DRAW); // NB mBuffer does not own the array mBuffer->setArray(0, mVertexArray.get()); } // --------------------------------------------------------------------------- RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::TextureManager* textureManager, float scalingFactor) : mViewer(viewer) , mSceneRoot(sceneroot) , mTextureManager(textureManager) , mUpdate(false) , mIsInitialise(false) , mInvScalingFactor(1.f) , mInjectState(NULL) { if (scalingFactor != 0.f) mInvScalingFactor = 1.f / scalingFactor; } RenderManager::~RenderManager() { MYGUI_PLATFORM_LOG(Info, "* Shutdown: "<removeChild(mGuiRoot.get()); mGuiRoot = nullptr; mSceneRoot = nullptr; mViewer = nullptr; destroyAllResources(); MYGUI_PLATFORM_LOG(Info, getClassTypeName()<<" successfully shutdown"); mIsInitialise = false; } void RenderManager::initialise() { MYGUI_PLATFORM_ASSERT(!mIsInitialise, getClassTypeName()<<" initialised twice"); MYGUI_PLATFORM_LOG(Info, "* Initialise: "< geode = new osg::Geode; geode->addDrawable(mDrawable.get()); osg::ref_ptr camera = new osg::Camera(); camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); camera->setProjectionResizePolicy(osg::Camera::FIXED); camera->setProjectionMatrix(osg::Matrix::identity()); camera->setViewMatrix(osg::Matrix::identity()); camera->setRenderOrder(osg::Camera::POST_RENDER); camera->setClearMask(GL_NONE); geode->setCullingActive(false); camera->addChild(geode.get()); mGuiRoot = camera; mSceneRoot->addChild(mGuiRoot.get()); osg::ref_ptr vp = mViewer->getCamera()->getViewport(); setViewSize(vp->width(), vp->height()); MYGUI_PLATFORM_LOG(Info, getClassTypeName()<<" successfully initialized"); mIsInitialise = true; } void RenderManager::shutdown() { } MyGUI::IVertexBuffer* RenderManager::createVertexBuffer() { return new OSGVertexBuffer(); } void RenderManager::destroyVertexBuffer(MyGUI::IVertexBuffer *buffer) { delete buffer; } void RenderManager::begin() { mDrawable->clear(); // variance will be recomputed based on textures being rendered in this frame mDrawable->setDataVariance(osg::Object::STATIC); } void RenderManager::doRender(MyGUI::IVertexBuffer *buffer, MyGUI::ITexture *texture, size_t count) { Drawable::Batch batch; batch.mVertexCount = count; batch.mVertexBuffer = static_cast(buffer)->getBuffer(); static_cast(buffer)->markAsQueuedForDrawing(); batch.mArray = static_cast(buffer)->getArray(); if (texture) { batch.mTexture = static_cast(texture)->getTexture(); if (batch.mTexture->getDataVariance() == osg::Object::DYNAMIC) mDrawable->setDataVariance(osg::Object::DYNAMIC); // only for this frame, reset in begin() } if (mInjectState) batch.mStateSet = mInjectState; mDrawable->addBatch(batch); } void RenderManager::setInjectState(osg::StateSet* stateSet) { mInjectState = stateSet; } void RenderManager::end() { } void RenderManager::update() { static MyGUI::Timer timer; static unsigned long last_time = timer.getMilliseconds(); unsigned long now_time = timer.getMilliseconds(); unsigned long time = now_time - last_time; onFrameEvent((float)((double)(time) / (double)1000)); last_time = now_time; } void RenderManager::collectDrawCalls() { begin(); onRenderToTarget(this, mUpdate); end(); mUpdate = false; } void RenderManager::setViewSize(int width, int height) { if(width < 1) width = 1; if(height < 1) height = 1; mGuiRoot->setViewport(0, 0, width, height); mViewSize.set(width * mInvScalingFactor, height * mInvScalingFactor); mInfo.maximumDepth = 1; mInfo.hOffset = 0; mInfo.vOffset = 0; mInfo.aspectCoef = float(mViewSize.height) / float(mViewSize.width); mInfo.pixScaleX = 1.0f / float(mViewSize.width); mInfo.pixScaleY = 1.0f / float(mViewSize.height); onResizeView(mViewSize); mUpdate = true; } bool RenderManager::isFormatSupported(MyGUI::PixelFormat /*format*/, MyGUI::TextureUsage /*usage*/) { return true; } MyGUI::ITexture* RenderManager::createTexture(const std::string &name) { MapTexture::iterator item = mTextures.find(name); if (item != mTextures.end()) { delete item->second; mTextures.erase(item); } OSGTexture* texture = new OSGTexture(name, mTextureManager); mTextures.insert(std::make_pair(name, texture)); return texture; } void RenderManager::destroyTexture(MyGUI::ITexture *texture) { if(texture == nullptr) return; MapTexture::iterator item = mTextures.find(texture->getName()); MYGUI_PLATFORM_ASSERT(item != mTextures.end(), "Texture '"<getName()<<"' not found"); mTextures.erase(item); delete texture; } MyGUI::ITexture* RenderManager::getTexture(const std::string &name) { if (name.empty()) return NULL; MapTexture::const_iterator item = mTextures.find(name); if(item == mTextures.end()) { MyGUI::ITexture* tex = createTexture(name); tex->loadFromFile(name); return tex; } return item->second; } void RenderManager::destroyAllResources() { for (MapTexture::iterator it = mTextures.begin(); it != mTextures.end(); ++it) delete it->second; mTextures.clear(); } bool RenderManager::checkTexture(MyGUI::ITexture* _texture) { // We support external textures that aren't registered via this manager, so can't implement this method sensibly. return true; } } openmw-openmw-0.38.0/components/myguiplatform/myguirendermanager.hpp000066400000000000000000000062641264522266000261110ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIRENDERMANAGER_H #define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUIRENDERMANAGER_H #include #include namespace Resource { class TextureManager; } namespace osgViewer { class Viewer; } namespace osg { class Group; class Camera; class RenderInfo; class StateSet; } namespace osgMyGUI { class Drawable; class RenderManager : public MyGUI::RenderManager, public MyGUI::IRenderTarget { osg::ref_ptr mViewer; osg::ref_ptr mSceneRoot; osg::ref_ptr mDrawable; Resource::TextureManager* mTextureManager; MyGUI::IntSize mViewSize; bool mUpdate; MyGUI::VertexColourType mVertexFormat; MyGUI::RenderTargetInfo mInfo; typedef std::map MapTexture; MapTexture mTextures; bool mIsInitialise; osg::ref_ptr mGuiRoot; float mInvScalingFactor; osg::StateSet* mInjectState; void destroyAllResources(); public: RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::TextureManager* textureManager, float scalingFactor); virtual ~RenderManager(); void initialise(); void shutdown(); void setScalingFactor(float factor); static RenderManager& getInstance() { return *getInstancePtr(); } static RenderManager* getInstancePtr() { return static_cast(MyGUI::RenderManager::getInstancePtr()); } /** @see RenderManager::getViewSize */ virtual const MyGUI::IntSize& getViewSize() const { return mViewSize; } /** @see RenderManager::getVertexFormat */ virtual MyGUI::VertexColourType getVertexFormat() { return mVertexFormat; } /** @see RenderManager::isFormatSupported */ virtual bool isFormatSupported(MyGUI::PixelFormat format, MyGUI::TextureUsage usage); /** @see RenderManager::createVertexBuffer */ virtual MyGUI::IVertexBuffer* createVertexBuffer(); /** @see RenderManager::destroyVertexBuffer */ virtual void destroyVertexBuffer(MyGUI::IVertexBuffer *buffer); /** @see RenderManager::createTexture */ virtual MyGUI::ITexture* createTexture(const std::string &name); /** @see RenderManager::destroyTexture */ virtual void destroyTexture(MyGUI::ITexture* _texture); /** @see RenderManager::getTexture */ virtual MyGUI::ITexture* getTexture(const std::string &name); // Called by the update traversal void update(); // Called by the cull traversal /** @see IRenderTarget::begin */ virtual void begin(); /** @see IRenderTarget::end */ virtual void end(); /** @see IRenderTarget::doRender */ virtual void doRender(MyGUI::IVertexBuffer *buffer, MyGUI::ITexture *texture, size_t count); /** specify a StateSet to inject for rendering. The StateSet will be used by future doRender calls until you reset it to NULL again. */ void setInjectState(osg::StateSet* stateSet); /** @see IRenderTarget::getInfo */ virtual const MyGUI::RenderTargetInfo& getInfo() { return mInfo; } bool checkTexture(MyGUI::ITexture* _texture); /*internal:*/ void collectDrawCalls(); void setViewSize(int width, int height); }; } #endif openmw-openmw-0.38.0/components/myguiplatform/myguitexture.cpp000066400000000000000000000131231264522266000247620ustar00rootroot00000000000000#include "myguitexture.hpp" #include #include #include #include namespace osgMyGUI { OSGTexture::OSGTexture(const std::string &name, Resource::TextureManager* textureManager) : mName(name) , mTextureManager(textureManager) , mFormat(MyGUI::PixelFormat::Unknow) , mUsage(MyGUI::TextureUsage::Default) , mNumElemBytes(0) { } OSGTexture::OSGTexture(osg::Texture2D *texture) : mTextureManager(NULL) , mTexture(texture) , mFormat(MyGUI::PixelFormat::Unknow) , mUsage(MyGUI::TextureUsage::Default) , mNumElemBytes(0) { } OSGTexture::~OSGTexture() { } void OSGTexture::createManual(int width, int height, MyGUI::TextureUsage usage, MyGUI::PixelFormat format) { GLenum glfmt = GL_NONE; size_t numelems = 0; switch(format.getValue()) { case MyGUI::PixelFormat::L8: glfmt = GL_LUMINANCE; numelems = 1; break; case MyGUI::PixelFormat::L8A8: glfmt = GL_LUMINANCE_ALPHA; numelems = 2; break; case MyGUI::PixelFormat::R8G8B8: glfmt = GL_RGB; numelems = 3; break; case MyGUI::PixelFormat::R8G8B8A8: glfmt = GL_RGBA; numelems = 4; break; } if(glfmt == GL_NONE) throw std::runtime_error("Texture format not supported"); mTexture = new osg::Texture2D(); mTexture->setTextureSize(width, height); mTexture->setSourceFormat(glfmt); mTexture->setSourceType(GL_UNSIGNED_BYTE); mTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); mFormat = format; mUsage = usage; mNumElemBytes = numelems; } void OSGTexture::destroy() { mTexture = nullptr; mFormat = MyGUI::PixelFormat::Unknow; mUsage = MyGUI::TextureUsage::Default; mNumElemBytes = 0; } void OSGTexture::loadFromFile(const std::string &fname) { if (!mTextureManager) throw std::runtime_error("No texturemanager set"); mTexture = mTextureManager->getTexture2D(fname, osg::Texture2D::CLAMP_TO_EDGE, osg::Texture2D::CLAMP_TO_EDGE); // disable mip-maps mTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); // FIXME mFormat = MyGUI::PixelFormat::R8G8B8; mUsage = MyGUI::TextureUsage::Static | MyGUI::TextureUsage::Write; mNumElemBytes = 3; // FIXME } void OSGTexture::saveToFile(const std::string &fname) { std::cerr << "Would save image to file " << fname << std::endl; } int OSGTexture::getWidth() { if(!mTexture.valid()) return 0; osg::Image *image = mTexture->getImage(); if(image) return image->s(); return mTexture->getTextureWidth(); } int OSGTexture::getHeight() { if(!mTexture.valid()) return 0; osg::Image *image = mTexture->getImage(); if(image) return image->t(); return mTexture->getTextureHeight(); } void *OSGTexture::lock(MyGUI::TextureUsage /*access*/) { if (!mTexture.valid()) throw std::runtime_error("Texture is not created"); if (mLockedImage.valid()) throw std::runtime_error("Texture already locked"); mLockedImage = mTexture->getImage(); if(!mLockedImage.valid()) { mLockedImage = new osg::Image(); mLockedImage->allocateImage( mTexture->getTextureWidth(), mTexture->getTextureHeight(), mTexture->getTextureDepth(), mTexture->getSourceFormat(), mTexture->getSourceType() ); } return mLockedImage->data(); } void OSGTexture::unlock() { if (!mLockedImage.valid()) throw std::runtime_error("Texture not locked"); // mTexture might be in use by the draw thread, so create a new texture instead and use that. osg::ref_ptr newTexture = new osg::Texture2D; newTexture->setTextureSize(getWidth(), getHeight()); newTexture->setSourceFormat(mTexture->getSourceFormat()); newTexture->setSourceType(mTexture->getSourceType()); newTexture->setFilter(osg::Texture::MIN_FILTER, mTexture->getFilter(osg::Texture::MIN_FILTER)); newTexture->setFilter(osg::Texture::MAG_FILTER, mTexture->getFilter(osg::Texture::MAG_FILTER)); newTexture->setWrap(osg::Texture::WRAP_S, mTexture->getWrap(osg::Texture::WRAP_S)); newTexture->setWrap(osg::Texture::WRAP_T, mTexture->getWrap(osg::Texture::WRAP_T)); newTexture->setImage(mLockedImage.get()); // Tell the texture it can get rid of the image for static textures (since // they aren't expected to update much at all). newTexture->setUnRefImageDataAfterApply(mUsage.isValue(MyGUI::TextureUsage::Static) ? true : false); mTexture = newTexture; mLockedImage = nullptr; } bool OSGTexture::isLocked() { return mLockedImage.valid(); } // Render-to-texture not currently implemented. MyGUI::IRenderTarget* OSGTexture::getRenderTarget() { return nullptr; } } openmw-openmw-0.38.0/components/myguiplatform/myguitexture.hpp000066400000000000000000000032211264522266000247650ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUITEXTURE_H #define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUITEXTURE_H #include #include namespace osg { class Image; class Texture2D; } namespace Resource { class TextureManager; } namespace osgMyGUI { class OSGTexture : public MyGUI::ITexture { std::string mName; Resource::TextureManager* mTextureManager; osg::ref_ptr mLockedImage; osg::ref_ptr mTexture; MyGUI::PixelFormat mFormat; MyGUI::TextureUsage mUsage; size_t mNumElemBytes; public: OSGTexture(const std::string &name, Resource::TextureManager* textureManager); OSGTexture(osg::Texture2D* texture); virtual ~OSGTexture(); virtual const std::string& getName() const { return mName; } virtual void createManual(int width, int height, MyGUI::TextureUsage usage, MyGUI::PixelFormat format); virtual void loadFromFile(const std::string &fname); virtual void saveToFile(const std::string &fname); virtual void destroy(); virtual void* lock(MyGUI::TextureUsage access); virtual void unlock(); virtual bool isLocked(); virtual int getWidth(); virtual int getHeight(); virtual MyGUI::PixelFormat getFormat() { return mFormat; } virtual MyGUI::TextureUsage getUsage() { return mUsage; } virtual size_t getNumElemBytes() { return mNumElemBytes; } virtual MyGUI::IRenderTarget *getRenderTarget(); /*internal:*/ osg::Texture2D *getTexture() const { return mTexture.get(); } }; } #endif openmw-openmw-0.38.0/components/myguiplatform/scalinglayer.cpp000066400000000000000000000105631264522266000246710ustar00rootroot00000000000000#include "scalinglayer.hpp" #include #include namespace osgMyGUI { /// @brief the ProxyRenderTarget allows to adjust the pixel scale and offset for a "source" render target. class ProxyRenderTarget : public MyGUI::IRenderTarget { public: /// @param target The target to render to. /// @param viewSize The size of the underlying layer node to render. /// @param hoffset The horizontal rendering offset, specified as an offset from the left screen edge in range 0-1. /// @param voffset The vertical rendering offset, specified as an offset from the top screen edge in range 0-1. ProxyRenderTarget(MyGUI::IRenderTarget* target, MyGUI::IntSize viewSize, float hoffset, float voffset) : mTarget(target) , mViewSize(viewSize) , mHOffset(hoffset) , mVOffset(voffset) { } virtual void begin() { mTarget->begin(); } virtual void end() { mTarget->end(); } virtual void doRender(MyGUI::IVertexBuffer* _buffer, MyGUI::ITexture* _texture, size_t _count) { mTarget->doRender(_buffer, _texture, _count); } virtual const MyGUI::RenderTargetInfo& getInfo() { mInfo = mTarget->getInfo(); mInfo.hOffset = mHOffset; mInfo.vOffset = mVOffset; mInfo.pixScaleX = 1.f / mViewSize.width; mInfo.pixScaleY = 1.f / mViewSize.height; return mInfo; } private: MyGUI::IRenderTarget* mTarget; MyGUI::IntSize mViewSize; float mHOffset, mVOffset; MyGUI::RenderTargetInfo mInfo; }; MyGUI::ILayerItem *ScalingLayer::getLayerItemByPoint(int _left, int _top) const { screenToLayerCoords(_left, _top); return OverlappedLayer::getLayerItemByPoint(_left, _top); } void ScalingLayer::screenToLayerCoords(int& _left, int& _top) const { float scale = getScaleFactor(); if (scale <= 0.f) return; MyGUI::IntSize globalViewSize = MyGUI::RenderManager::getInstance().getViewSize(); _left -= globalViewSize.width/2; _top -= globalViewSize.height/2; _left /= scale; _top /= scale; _left += mViewSize.width/2; _top += mViewSize.height/2; } float ScalingLayer::getScaleFactor() const { MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); float w = viewSize.width; float h = viewSize.height; float heightScale = (h / mViewSize.height); float widthScale = (w / mViewSize.width); return std::min(widthScale, heightScale); } MyGUI::IntPoint ScalingLayer::getPosition(int _left, int _top) const { screenToLayerCoords(_left, _top); return MyGUI::IntPoint(_left, _top); } void ScalingLayer::renderToTarget(MyGUI::IRenderTarget *_target, bool _update) { MyGUI::IntSize globalViewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntSize viewSize = globalViewSize; float scale = getScaleFactor(); viewSize.width /= scale; viewSize.height /= scale; float hoffset = (globalViewSize.width - mViewSize.width*getScaleFactor())/2.f / static_cast(globalViewSize.width); float voffset = (globalViewSize.height - mViewSize.height*getScaleFactor())/2.f / static_cast(globalViewSize.height); ProxyRenderTarget proxy(_target, viewSize, hoffset, voffset); MyGUI::OverlappedLayer::renderToTarget(&proxy, _update); } void ScalingLayer::resizeView(const MyGUI::IntSize &_viewSize) { // do nothing } void ScalingLayer::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) { MyGUI::OverlappedLayer::deserialization(_node, _version); MyGUI::xml::ElementEnumerator info = _node->getElementEnumerator(); while (info.next()) { if (info->getName() == "Property") { const std::string& key = info->findAttribute("key"); const std::string& value = info->findAttribute("value"); if (key == "Size") { mViewSize = MyGUI::IntSize::parse(value); } } } } } openmw-openmw-0.38.0/components/myguiplatform/scalinglayer.hpp000066400000000000000000000021271264522266000246730ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_SCALINGLAYER #define OPENMW_COMPONENTS_MYGUIPLATFORM_SCALINGLAYER #include namespace osgMyGUI { ///@brief A Layer that lays out and renders widgets in screen-relative coordinates. The "Size" property determines the size of the virtual screen, /// which is then upscaled to the real screen size during rendering. The aspect ratio is kept intact, adding blanks to the sides when necessary. class ScalingLayer : public MyGUI::OverlappedLayer { public: MYGUI_RTTI_DERIVED(ScalingLayer) virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version); virtual MyGUI::ILayerItem* getLayerItemByPoint(int _left, int _top) const; virtual MyGUI::IntPoint getPosition(int _left, int _top) const; virtual void renderToTarget(MyGUI::IRenderTarget* _target, bool _update); virtual void resizeView(const MyGUI::IntSize& _viewSize); private: void screenToLayerCoords(int& _left, int& _top) const; float getScaleFactor() const; }; } #endif openmw-openmw-0.38.0/components/nif/000077500000000000000000000000001264522266000173605ustar00rootroot00000000000000openmw-openmw-0.38.0/components/nif/base.hpp000066400000000000000000000026451264522266000210120ustar00rootroot00000000000000///This file holds the main classes of NIF Records used by everything else. #ifndef OPENMW_COMPONENTS_NIF_BASE_HPP #define OPENMW_COMPONENTS_NIF_BASE_HPP #include "record.hpp" #include "niffile.hpp" #include "recordptr.hpp" #include "nifstream.hpp" #include "nifkey.hpp" namespace Nif { /** A record that can have extra data. The extra data objects themselves descend from the Extra class, and all the extra data connected to an object form a linked list */ class Extra : public Record { public: ExtraPtr extra; void read(NIFStream *nif) { extra.read(nif); } void post(NIFFile *nif) { extra.post(nif); } }; class Controller : public Record { public: ControllerPtr next; int flags; float frequency, phase; float timeStart, timeStop; ControlledPtr target; void read(NIFStream *nif); void post(NIFFile *nif); }; /// Anything that has a controller class Controlled : public Extra { public: ControllerPtr controller; void read(NIFStream *nif) { Extra::read(nif); controller.read(nif); } void post(NIFFile *nif) { Extra::post(nif); controller.post(nif); } }; /// Has name, extra-data and controller class Named : public Controlled { public: std::string name; void read(NIFStream *nif) { name = nif->getString(); Controlled::read(nif); } }; typedef Named NiSequenceStreamHelper; } // Namespace #endif openmw-openmw-0.38.0/components/nif/controlled.cpp000066400000000000000000000034151264522266000222340ustar00rootroot00000000000000#include "controlled.hpp" #include "data.hpp" namespace Nif { void NiSourceTexture::read(NIFStream *nif) { Named::read(nif); external = !!nif->getChar(); if(external) filename = nif->getString(); else { nif->getChar(); // always 1 data.read(nif); } pixel = nif->getInt(); mipmap = nif->getInt(); alpha = nif->getInt(); nif->getChar(); // always 1 } void NiSourceTexture::post(NIFFile *nif) { Named::post(nif); data.post(nif); } void NiParticleGrowFade::read(NIFStream *nif) { Controlled::read(nif); growTime = nif->getFloat(); fadeTime = nif->getFloat(); } void NiParticleColorModifier::read(NIFStream *nif) { Controlled::read(nif); data.read(nif); } void NiParticleColorModifier::post(NIFFile *nif) { Controlled::post(nif); data.post(nif); } void NiGravity::read(NIFStream *nif) { Controlled::read(nif); mDecay = nif->getFloat(); mForce = nif->getFloat(); mType = nif->getUInt(); mPosition = nif->getVector3(); mDirection = nif->getVector3(); } void NiPlanarCollider::read(NIFStream *nif) { Controlled::read(nif); mBounceFactor = nif->getFloat(); /*unknown*/nif->getFloat(); for (int i=0;i<10;++i) /*unknown*/nif->getFloat(); mPlaneNormal = nif->getVector3(); mPlaneDistance = nif->getFloat(); } void NiParticleRotation::read(NIFStream *nif) { Controlled::read(nif); /* byte (0 or 1) float (1) float*3 */ nif->skip(17); } } openmw-openmw-0.38.0/components/nif/controlled.hpp000066400000000000000000000051031264522266000222350ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (controlled.h) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #ifndef OPENMW_COMPONENTS_NIF_CONTROLLED_HPP #define OPENMW_COMPONENTS_NIF_CONTROLLED_HPP #include "base.hpp" namespace Nif { class NiSourceTexture : public Named { public: // Is this an external (references a separate texture file) or // internal (data is inside the nif itself) texture? bool external; std::string filename; // In case of external textures NiPixelDataPtr data; // In case of internal textures /* Pixel layout 0 - Palettised 1 - High color 16 2 - True color 32 3 - Compressed 4 - Bumpmap 5 - Default */ int pixel; /* Mipmap format 0 - no 1 - yes 2 - default */ int mipmap; /* Alpha 0 - none 1 - binary 2 - smooth 3 - default (use material alpha, or multiply material with texture if present) */ int alpha; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiParticleGrowFade : public Controlled { public: float growTime; float fadeTime; void read(NIFStream *nif); }; class NiParticleColorModifier : public Controlled { public: NiColorDataPtr data; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiGravity : public Controlled { public: float mForce; /* 0 - Wind (fixed direction) * 1 - Point (fixed origin) */ int mType; float mDecay; osg::Vec3f mPosition; osg::Vec3f mDirection; void read(NIFStream *nif); }; // NiPinaColada class NiPlanarCollider : public Controlled { public: void read(NIFStream *nif); float mBounceFactor; osg::Vec3f mPlaneNormal; float mPlaneDistance; }; class NiParticleRotation : public Controlled { public: void read(NIFStream *nif); }; } // Namespace #endif openmw-openmw-0.38.0/components/nif/controller.cpp000066400000000000000000000107101264522266000222460ustar00rootroot00000000000000#include "controller.hpp" #include "node.hpp" #include "data.hpp" namespace Nif { void Controller::read(NIFStream *nif) { next.read(nif); flags = nif->getUShort(); frequency = nif->getFloat(); phase = nif->getFloat(); timeStart = nif->getFloat(); timeStop = nif->getFloat(); target.read(nif); } void Controller::post(NIFFile *nif) { Record::post(nif); next.post(nif); target.post(nif); } void NiParticleSystemController::read(NIFStream *nif) { Controller::read(nif); velocity = nif->getFloat(); velocityRandom = nif->getFloat(); verticalDir = nif->getFloat(); verticalAngle = nif->getFloat(); horizontalDir = nif->getFloat(); horizontalAngle = nif->getFloat(); /*normal?*/ nif->getVector3(); /*color?*/ nif->getVector4(); size = nif->getFloat(); startTime = nif->getFloat(); stopTime = nif->getFloat(); nif->getChar(); emitRate = nif->getFloat(); lifetime = nif->getFloat(); lifetimeRandom = nif->getFloat(); emitFlags = nif->getUShort(); offsetRandom = nif->getVector3(); emitter.read(nif); /* Unknown Short, 0? * Unknown Float, 1.0? * Unknown Int, 1? * Unknown Int, 0? * Unknown Short, 0? */ nif->skip(16); numParticles = nif->getUShort(); activeCount = nif->getUShort(); particles.resize(numParticles); for(size_t i = 0;i < particles.size();i++) { particles[i].velocity = nif->getVector3(); nif->getVector3(); /* unknown */ particles[i].lifetime = nif->getFloat(); particles[i].lifespan = nif->getFloat(); particles[i].timestamp = nif->getFloat(); nif->getUShort(); /* unknown */ particles[i].vertex = nif->getUShort(); } nif->getUInt(); /* -1? */ affectors.read(nif); colliders.read(nif); nif->getChar(); } void NiParticleSystemController::post(NIFFile *nif) { Controller::post(nif); emitter.post(nif); affectors.post(nif); colliders.post(nif); } void NiMaterialColorController::read(NIFStream *nif) { Controller::read(nif); data.read(nif); } void NiMaterialColorController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); } void NiPathController::read(NIFStream *nif) { Controller::read(nif); /* int = 1 2xfloat short = 0 or 1 */ nif->skip(14); posData.read(nif); floatData.read(nif); } void NiPathController::post(NIFFile *nif) { Controller::post(nif); posData.post(nif); floatData.post(nif); } void NiUVController::read(NIFStream *nif) { Controller::read(nif); nif->getUShort(); // always 0 data.read(nif); } void NiUVController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); } void NiKeyframeController::read(NIFStream *nif) { Controller::read(nif); data.read(nif); } void NiKeyframeController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); } void NiAlphaController::read(NIFStream *nif) { Controller::read(nif); data.read(nif); } void NiAlphaController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); } void NiGeomMorpherController::read(NIFStream *nif) { Controller::read(nif); data.read(nif); nif->getChar(); // always 0 } void NiGeomMorpherController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); } void NiVisController::read(NIFStream *nif) { Controller::read(nif); data.read(nif); } void NiVisController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); } void NiFlipController::read(NIFStream *nif) { Controller::read(nif); mTexSlot = nif->getUInt(); /*unknown=*/nif->getUInt();/*0?*/ mDelta = nif->getFloat(); mSources.read(nif); } void NiFlipController::post(NIFFile *nif) { Controller::post(nif); mSources.post(nif); } } openmw-openmw-0.38.0/components/nif/controller.hpp000066400000000000000000000066211264522266000222610ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (controller.h) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #ifndef OPENMW_COMPONENTS_NIF_CONTROLLER_HPP #define OPENMW_COMPONENTS_NIF_CONTROLLER_HPP #include "base.hpp" namespace Nif { class NiParticleSystemController : public Controller { public: struct Particle { osg::Vec3f velocity; float lifetime; float lifespan; float timestamp; int vertex; }; float velocity; float velocityRandom; float verticalDir; // 0=up, pi/2=horizontal, pi=down float verticalAngle; float horizontalDir; float horizontalAngle; float size; float startTime; float stopTime; float emitRate; float lifetime; float lifetimeRandom; enum EmitFlags { NoAutoAdjust = 0x1 // If this flag is set, we use the emitRate value. Otherwise, // we calculate an emit rate so that the maximum number of particles // in the system (numParticles) is never exceeded. }; int emitFlags; osg::Vec3f offsetRandom; NodePtr emitter; int numParticles; int activeCount; std::vector particles; ExtraPtr affectors; ExtraPtr colliders; void read(NIFStream *nif); void post(NIFFile *nif); }; typedef NiParticleSystemController NiBSPArrayController; class NiMaterialColorController : public Controller { public: NiPosDataPtr data; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiPathController : public Controller { public: NiPosDataPtr posData; NiFloatDataPtr floatData; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiUVController : public Controller { public: NiUVDataPtr data; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiKeyframeController : public Controller { public: NiKeyframeDataPtr data; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiAlphaController : public Controller { public: NiFloatDataPtr data; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiGeomMorpherController : public Controller { public: NiMorphDataPtr data; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiVisController : public Controller { public: NiVisDataPtr data; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiFlipController : public Controller { public: int mTexSlot; // NiTexturingProperty::TextureType float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources NiSourceTextureList mSources; void read(NIFStream *nif); void post(NIFFile *nif); }; } // Namespace #endif openmw-openmw-0.38.0/components/nif/data.cpp000066400000000000000000000143711264522266000210030ustar00rootroot00000000000000#include "data.hpp" #include "node.hpp" #include #include namespace Nif { void NiSkinInstance::read(NIFStream *nif) { data.read(nif); root.read(nif); bones.read(nif); } void NiSkinInstance::post(NIFFile *nif) { data.post(nif); root.post(nif); bones.post(nif); if(data.empty() || root.empty()) nif->fail("NiSkinInstance missing root or data"); size_t bnum = bones.length(); if(bnum != data->bones.size()) nif->fail("Mismatch in NiSkinData bone count"); root->makeRootBone(&data->trafo); for(size_t i=0; ifail("Oops: Missing bone! Don't know how to handle this."); bones[i]->makeBone(i, data->bones[i]); } } void ShapeData::read(NIFStream *nif) { int verts = nif->getUShort(); vertices = new osg::Vec3Array; if(nif->getInt()) nif->getVector3s(vertices, verts); normals = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX); if(nif->getInt()) nif->getVector3s(normals, verts); center = nif->getVector3(); radius = nif->getFloat(); colors = new osg::Vec4Array(osg::Array::BIND_PER_VERTEX); if(nif->getInt()) nif->getVector4s(colors, verts); // Only the first 6 bits are used as a count. I think the rest are // flags of some sort. int uvs = nif->getUShort(); uvs &= 0x3f; if(nif->getInt()) { uvlist.resize(uvs); for(int i = 0;i < uvs;i++) { uvlist[i] = new osg::Vec2Array(osg::Array::BIND_PER_VERTEX); nif->getVector2s(uvlist[i], verts); } } } void NiTriShapeData::read(NIFStream *nif) { ShapeData::read(nif); /*int tris =*/ nif->getUShort(); // We have three times as many vertices as triangles, so this // is always equal to tris*3. int cnt = nif->getInt(); triangles = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES); nif->getUShorts(triangles, cnt); // Read the match list, which lists the vertices that are equal to // vertices. We don't actually need need this for anything, so // just skip it. int verts = nif->getUShort(); for(int i=0;i < verts;i++) { // Number of vertices matching vertex 'i' int num = nif->getUShort(); nif->skip(num * sizeof(short)); } } void NiAutoNormalParticlesData::read(NIFStream *nif) { ShapeData::read(nif); // Should always match the number of vertices numParticles = nif->getUShort(); particleRadius = nif->getFloat(); activeCount = nif->getUShort(); if(nif->getInt()) { int numVerts = vertices->size(); // Particle sizes nif->getFloats(sizes, numVerts); } } void NiRotatingParticlesData::read(NIFStream *nif) { NiAutoNormalParticlesData::read(nif); if(nif->getInt()) { int numVerts = vertices->size(); // Rotation quaternions. nif->getQuaternions(rotations, numVerts); } } void NiPosData::read(NIFStream *nif) { mKeyList.reset(new Vector3KeyMap); mKeyList->read(nif); } void NiUVData::read(NIFStream *nif) { for(int i = 0;i < 4;i++) { mKeyList[i].reset(new FloatKeyMap); mKeyList[i]->read(nif); } } void NiFloatData::read(NIFStream *nif) { mKeyList.reset(new FloatKeyMap); mKeyList->read(nif); } void NiPixelData::read(NIFStream *nif) { nif->getInt(); // always 0 or 1 rmask = nif->getInt(); // usually 0xff gmask = nif->getInt(); // usually 0xff00 bmask = nif->getInt(); // usually 0xff0000 amask = nif->getInt(); // usually 0xff000000 or zero bpp = nif->getInt(); // Unknown nif->skip(12); mips = nif->getInt(); // Bytes per pixel, should be bpp * 8 /*int bytes =*/ nif->getInt(); for(int i=0; igetInt(); /*int y =*/ nif->getInt(); /*int offset =*/ nif->getInt(); } // Skip the data unsigned int dataSize = nif->getInt(); nif->skip(dataSize); } void NiColorData::read(NIFStream *nif) { mKeyMap.reset(new Vector4KeyMap); mKeyMap->read(nif); } void NiVisData::read(NIFStream *nif) { int count = nif->getInt(); mVis.resize(count); for(size_t i = 0;i < mVis.size();i++) { mVis[i].time = nif->getFloat(); mVis[i].isSet = (nif->getChar() != 0); } } void NiSkinData::read(NIFStream *nif) { trafo.rotation = nif->getMatrix3(); trafo.pos = nif->getVector3(); trafo.scale = nif->getFloat(); int boneNum = nif->getInt(); nif->getInt(); // -1 bones.resize(boneNum); for(int i=0;igetMatrix3(); bi.trafo.pos = nif->getVector3(); bi.trafo.scale = nif->getFloat(); bi.boundSphereCenter = nif->getVector3(); bi.boundSphereRadius = nif->getFloat(); // Number of vertex weights bi.weights.resize(nif->getUShort()); for(size_t j = 0;j < bi.weights.size();j++) { bi.weights[j].vertex = nif->getUShort(); bi.weights[j].weight = nif->getFloat(); } } } void NiMorphData::read(NIFStream *nif) { int morphCount = nif->getInt(); int vertCount = nif->getInt(); /*relative targets?*/nif->getChar(); mMorphs.resize(morphCount); for(int i = 0;i < morphCount;i++) { mMorphs[i].mKeyFrames.reset(new FloatKeyMap); mMorphs[i].mKeyFrames->read(nif, true); mMorphs[i].mVertices = new osg::Vec3Array; nif->getVector3s(mMorphs[i].mVertices, vertCount); } } void NiKeyframeData::read(NIFStream *nif) { mRotations.reset(new QuaternionKeyMap); mRotations->read(nif); if(mRotations->mInterpolationType == Vector3KeyMap::sXYZInterpolation) { //Chomp unused float nif->getFloat(); mXRotations.reset(new FloatKeyMap); mYRotations.reset(new FloatKeyMap); mZRotations.reset(new FloatKeyMap); mXRotations->read(nif, true); mYRotations->read(nif, true); mZRotations->read(nif, true); } mTranslations.reset(new Vector3KeyMap); mTranslations->read(nif); mScales.reset(new FloatKeyMap); mScales->read(nif); } } // Namespace openmw-openmw-0.38.0/components/nif/data.hpp000066400000000000000000000072331264522266000210070ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (data.h) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #ifndef OPENMW_COMPONENTS_NIF_DATA_HPP #define OPENMW_COMPONENTS_NIF_DATA_HPP #include "base.hpp" #include "niftypes.hpp" // Transformation #include namespace Nif { // Common ancestor for several data classes class ShapeData : public Record { public: osg::ref_ptr vertices, normals; osg::ref_ptr colors; std::vector< osg::ref_ptr > uvlist; osg::Vec3f center; float radius; void read(NIFStream *nif); }; class NiTriShapeData : public ShapeData { public: // Triangles, three vertex indices per triangle osg::ref_ptr triangles; void read(NIFStream *nif); }; class NiAutoNormalParticlesData : public ShapeData { public: int numParticles; float particleRadius; int activeCount; std::vector sizes; void read(NIFStream *nif); }; class NiRotatingParticlesData : public NiAutoNormalParticlesData { public: std::vector rotations; void read(NIFStream *nif); }; class NiPosData : public Record { public: Vector3KeyMapPtr mKeyList; void read(NIFStream *nif); }; class NiUVData : public Record { public: FloatKeyMapPtr mKeyList[4]; void read(NIFStream *nif); }; class NiFloatData : public Record { public: FloatKeyMapPtr mKeyList; void read(NIFStream *nif); }; class NiPixelData : public Record { public: unsigned int rmask, gmask, bmask, amask; int bpp, mips; void read(NIFStream *nif); }; class NiColorData : public Record { public: Vector4KeyMapPtr mKeyMap; void read(NIFStream *nif); }; class NiVisData : public Record { public: struct VisData { float time; bool isSet; }; std::vector mVis; void read(NIFStream *nif); }; class NiSkinInstance : public Record { public: NiSkinDataPtr data; NodePtr root; NodeList bones; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiSkinData : public Record { public: struct VertWeight { unsigned short vertex; float weight; }; struct BoneInfo { Transformation trafo; osg::Vec3f boundSphereCenter; float boundSphereRadius; std::vector weights; }; Transformation trafo; std::vector bones; void read(NIFStream *nif); }; struct NiMorphData : public Record { struct MorphData { FloatKeyMapPtr mKeyFrames; osg::ref_ptr mVertices; }; std::vector mMorphs; void read(NIFStream *nif); }; struct NiKeyframeData : public Record { QuaternionKeyMapPtr mRotations; // may be NULL FloatKeyMapPtr mXRotations; FloatKeyMapPtr mYRotations; FloatKeyMapPtr mZRotations; Vector3KeyMapPtr mTranslations; FloatKeyMapPtr mScales; void read(NIFStream *nif); }; } // Namespace #endif openmw-openmw-0.38.0/components/nif/effect.cpp000066400000000000000000000021331264522266000213170ustar00rootroot00000000000000#include "effect.hpp" #include "node.hpp" namespace Nif { void NiLight::read(NIFStream *nif) { NiDynamicEffect::read(nif); dimmer = nif->getFloat(); ambient = nif->getVector3(); diffuse = nif->getVector3(); specular = nif->getVector3(); } void NiTextureEffect::read(NIFStream *nif) { NiDynamicEffect::read(nif); /* 3 x Vector4 = [1,0,0,0] int = 2 int = 0 or 3 int = 2 int = 2 */ nif->skip(16*4); texture.read(nif); /* byte = 0 vector4 = [1,0,0,0] short = 0 short = -75 short = 0 */ nif->skip(23); } void NiTextureEffect::post(NIFFile *nif) { NiDynamicEffect::post(nif); texture.post(nif); } void NiPointLight::read(NIFStream *nif) { NiLight::read(nif); constantAttenuation = nif->getFloat(); linearAttenuation = nif->getFloat(); quadraticAttenuation = nif->getFloat(); } void NiSpotLight::read(NIFStream *nif) { NiPointLight::read(nif); cutoff = nif->getFloat(); exponent = nif->getFloat(); } } openmw-openmw-0.38.0/components/nif/effect.hpp000066400000000000000000000036011264522266000213250ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (effect.h) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #ifndef OPENMW_COMPONENTS_NIF_EFFECT_HPP #define OPENMW_COMPONENTS_NIF_EFFECT_HPP #include "node.hpp" namespace Nif { struct NiDynamicEffect : public Node { void read(NIFStream *nif) { Node::read(nif); unsigned int numAffectedNodes = nif->getUInt(); for (unsigned int i=0; igetUInt(); // ref to another Node } }; // Used as base for NiAmbientLight, NiDirectionalLight, NiPointLight and NiSpotLight. struct NiLight : NiDynamicEffect { float dimmer; osg::Vec3f ambient; osg::Vec3f diffuse; osg::Vec3f specular; void read(NIFStream *nif); }; struct NiPointLight : public NiLight { float constantAttenuation; float linearAttenuation; float quadraticAttenuation; void read(NIFStream *nif); }; struct NiSpotLight : public NiPointLight { float cutoff; float exponent; void read(NIFStream *nif); }; struct NiTextureEffect : NiDynamicEffect { NiSourceTexturePtr texture; void read(NIFStream *nif); void post(NIFFile *nif); }; } // Namespace #endif openmw-openmw-0.38.0/components/nif/extra.cpp000066400000000000000000000014671264522266000212170ustar00rootroot00000000000000#include "extra.hpp" namespace Nif { void NiStringExtraData::read(NIFStream *nif) { Extra::read(nif); nif->getInt(); // size of string + 4. Really useful... string = nif->getString(); } void NiTextKeyExtraData::read(NIFStream *nif) { Extra::read(nif); nif->getInt(); // 0 int keynum = nif->getInt(); list.resize(keynum); for(int i=0; igetFloat(); list[i].text = nif->getString(); } } void NiVertWeightsExtraData::read(NIFStream *nif) { Extra::read(nif); // We should have s*4+2 == i, for some reason. Might simply be the // size of the rest of the record, unhelpful as that may be. /*int i =*/ nif->getInt(); int s = nif->getUShort(); nif->skip(s * sizeof(float)); // vertex weights I guess } } openmw-openmw-0.38.0/components/nif/extra.hpp000066400000000000000000000027351264522266000212230ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (extra.h) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #ifndef OPENMW_COMPONENTS_NIF_EXTRA_HPP #define OPENMW_COMPONENTS_NIF_EXTRA_HPP #include "base.hpp" namespace Nif { class NiVertWeightsExtraData : public Extra { public: void read(NIFStream *nif); }; class NiTextKeyExtraData : public Extra { public: struct TextKey { float time; std::string text; }; std::vector list; void read(NIFStream *nif); }; class NiStringExtraData : public Extra { public: /* Two known meanings: "MRK" - marker, only visible in the editor, not rendered in-game "NCO" - no collision */ std::string string; void read(NIFStream *nif); }; } // Namespace #endif openmw-openmw-0.38.0/components/nif/niffile.cpp000066400000000000000000000307311264522266000215040ustar00rootroot00000000000000#include "niffile.hpp" #include "effect.hpp" #include #include namespace Nif { /// Open a NIF stream. The name is used for error messages. NIFFile::NIFFile(Files::IStreamPtr stream, const std::string &name) : ver(0) , filename(name) , mUseSkinning(false) { parse(stream); } NIFFile::~NIFFile() { for (std::vector::iterator it = records.begin() ; it != records.end(); ++it) { delete *it; } } template static Record* construct() { return new NodeType; } struct RecordFactoryEntry { typedef Record* (*create_t) (); create_t mCreate; RecordType mType; }; ///Helper function for adding records to the factory map static std::pair makeEntry(std::string recName, Record* (*create_t) (), RecordType type) { RecordFactoryEntry anEntry = {create_t,type}; return std::make_pair(recName, anEntry); } ///These are all the record types we know how to read. static std::map makeFactory() { std::map newFactory; newFactory.insert(makeEntry("NiNode", &construct , RC_NiNode )); newFactory.insert(makeEntry("NiSwitchNode", &construct , RC_NiSwitchNode )); newFactory.insert(makeEntry("NiLODNode", &construct , RC_NiLODNode )); newFactory.insert(makeEntry("AvoidNode", &construct , RC_AvoidNode )); newFactory.insert(makeEntry("NiBSParticleNode", &construct , RC_NiBSParticleNode )); newFactory.insert(makeEntry("NiBSAnimationNode", &construct , RC_NiBSAnimationNode )); newFactory.insert(makeEntry("NiBillboardNode", &construct , RC_NiBillboardNode )); newFactory.insert(makeEntry("NiTriShape", &construct , RC_NiTriShape )); newFactory.insert(makeEntry("NiRotatingParticles", &construct , RC_NiRotatingParticles )); newFactory.insert(makeEntry("NiAutoNormalParticles", &construct , RC_NiAutoNormalParticles )); newFactory.insert(makeEntry("NiCamera", &construct , RC_NiCamera )); newFactory.insert(makeEntry("RootCollisionNode", &construct , RC_RootCollisionNode )); newFactory.insert(makeEntry("NiTexturingProperty", &construct , RC_NiTexturingProperty )); newFactory.insert(makeEntry("NiFogProperty", &construct , RC_NiFogProperty )); newFactory.insert(makeEntry("NiMaterialProperty", &construct , RC_NiMaterialProperty )); newFactory.insert(makeEntry("NiZBufferProperty", &construct , RC_NiZBufferProperty )); newFactory.insert(makeEntry("NiAlphaProperty", &construct , RC_NiAlphaProperty )); newFactory.insert(makeEntry("NiVertexColorProperty", &construct , RC_NiVertexColorProperty )); newFactory.insert(makeEntry("NiShadeProperty", &construct , RC_NiShadeProperty )); newFactory.insert(makeEntry("NiDitherProperty", &construct , RC_NiDitherProperty )); newFactory.insert(makeEntry("NiWireframeProperty", &construct , RC_NiWireframeProperty )); newFactory.insert(makeEntry("NiSpecularProperty", &construct , RC_NiSpecularProperty )); newFactory.insert(makeEntry("NiStencilProperty", &construct , RC_NiStencilProperty )); newFactory.insert(makeEntry("NiVisController", &construct , RC_NiVisController )); newFactory.insert(makeEntry("NiGeomMorpherController", &construct , RC_NiGeomMorpherController )); newFactory.insert(makeEntry("NiKeyframeController", &construct , RC_NiKeyframeController )); newFactory.insert(makeEntry("NiAlphaController", &construct , RC_NiAlphaController )); newFactory.insert(makeEntry("NiUVController", &construct , RC_NiUVController )); newFactory.insert(makeEntry("NiPathController", &construct , RC_NiPathController )); newFactory.insert(makeEntry("NiMaterialColorController", &construct , RC_NiMaterialColorController )); newFactory.insert(makeEntry("NiBSPArrayController", &construct , RC_NiBSPArrayController )); newFactory.insert(makeEntry("NiParticleSystemController", &construct , RC_NiParticleSystemController )); newFactory.insert(makeEntry("NiFlipController", &construct , RC_NiFlipController )); newFactory.insert(makeEntry("NiAmbientLight", &construct , RC_NiLight )); newFactory.insert(makeEntry("NiDirectionalLight", &construct , RC_NiLight )); newFactory.insert(makeEntry("NiPointLight", &construct , RC_NiLight )); newFactory.insert(makeEntry("NiSpotLight", &construct , RC_NiLight )); newFactory.insert(makeEntry("NiTextureEffect", &construct , RC_NiTextureEffect )); newFactory.insert(makeEntry("NiVertWeightsExtraData", &construct , RC_NiVertWeightsExtraData )); newFactory.insert(makeEntry("NiTextKeyExtraData", &construct , RC_NiTextKeyExtraData )); newFactory.insert(makeEntry("NiStringExtraData", &construct , RC_NiStringExtraData )); newFactory.insert(makeEntry("NiGravity", &construct , RC_NiGravity )); newFactory.insert(makeEntry("NiPlanarCollider", &construct , RC_NiPlanarCollider )); newFactory.insert(makeEntry("NiParticleGrowFade", &construct , RC_NiParticleGrowFade )); newFactory.insert(makeEntry("NiParticleColorModifier", &construct , RC_NiParticleColorModifier )); newFactory.insert(makeEntry("NiParticleRotation", &construct , RC_NiParticleRotation )); newFactory.insert(makeEntry("NiFloatData", &construct , RC_NiFloatData )); newFactory.insert(makeEntry("NiTriShapeData", &construct , RC_NiTriShapeData )); newFactory.insert(makeEntry("NiVisData", &construct , RC_NiVisData )); newFactory.insert(makeEntry("NiColorData", &construct , RC_NiColorData )); newFactory.insert(makeEntry("NiPixelData", &construct , RC_NiPixelData )); newFactory.insert(makeEntry("NiMorphData", &construct , RC_NiMorphData )); newFactory.insert(makeEntry("NiKeyframeData", &construct , RC_NiKeyframeData )); newFactory.insert(makeEntry("NiSkinData", &construct , RC_NiSkinData )); newFactory.insert(makeEntry("NiUVData", &construct , RC_NiUVData )); newFactory.insert(makeEntry("NiPosData", &construct , RC_NiPosData )); newFactory.insert(makeEntry("NiRotatingParticlesData", &construct , RC_NiRotatingParticlesData )); newFactory.insert(makeEntry("NiAutoNormalParticlesData", &construct , RC_NiAutoNormalParticlesData )); newFactory.insert(makeEntry("NiSequenceStreamHelper", &construct , RC_NiSequenceStreamHelper )); newFactory.insert(makeEntry("NiSourceTexture", &construct , RC_NiSourceTexture )); newFactory.insert(makeEntry("NiSkinInstance", &construct , RC_NiSkinInstance )); return newFactory; } ///Make the factory map used for parsing the file static const std::map factories = makeFactory(); std::string NIFFile::printVersion(unsigned int version) { union ver_quad { uint32_t full; uint8_t quad[4]; } version_out; version_out.full = version; std::stringstream stream; stream << version_out.quad[3] << "." << version_out.quad[2] << "." << version_out.quad[1] << "." << version_out.quad[0]; return stream.str(); } void NIFFile::parse(Files::IStreamPtr stream) { NIFStream nif (this, stream); // Check the header string std::string head = nif.getVersionString(); if(head.compare(0, 22, "NetImmerse File Format") != 0) fail("Invalid NIF header: " + head); // Get BCD version ver = nif.getUInt(); if(ver != VER_MW) fail("Unsupported NIF version: " + printVersion(ver)); // Number of records size_t recNum = nif.getInt(); records.resize(recNum); /* The format for 10.0.1.0 seems to be a bit different. After the header, it contains the number of records, r (int), just like 4.0.0.2, but following that it contains a short x, followed by x strings. Then again by r shorts, one for each record, giving which of the above strings to use to identify the record. After this follows two ints (zero?) and then the record data. However we do not support or plan to support other versions yet. */ for(size_t i = 0;i < recNum;i++) { Record *r = NULL; std::string rec = nif.getString(); if(rec.empty()) { std::stringstream error; error << "Record number " << i << " out of " << recNum << " is blank."; fail(error.str()); } std::map::const_iterator entry = factories.find(rec); if (entry != factories.end()) { r = entry->second.mCreate (); r->recType = entry->second.mType; } else fail("Unknown record type " + rec); assert(r != NULL); assert(r->recType != RC_MISSING); r->recName = rec; r->recIndex = i; records[i] = r; r->read(&nif); } size_t rootNum = nif.getUInt(); roots.resize(rootNum); //Determine which records are roots for(size_t i = 0;i < rootNum;i++) { int idx = nif.getInt(); if (idx >= 0 && idx < int(records.size())) { roots[i] = records[idx]; } else { roots[i] = NULL; warn("Null Root found"); } } // Once parsing is done, do post-processing. for(size_t i=0; ipost(this); } void NIFFile::setUseSkinning(bool skinning) { mUseSkinning = skinning; } bool NIFFile::getUseSkinning() const { return mUseSkinning; } } openmw-openmw-0.38.0/components/nif/niffile.hpp000066400000000000000000000047311264522266000215120ustar00rootroot00000000000000///Main header for reading .nif files #ifndef OPENMW_COMPONENTS_NIF_NIFFILE_HPP #define OPENMW_COMPONENTS_NIF_NIFFILE_HPP #include #include #include #include #include "record.hpp" namespace Nif { class NIFFile { enum NIFVersion { VER_MW = 0x04000002 // Morrowind NIFs }; /// Nif file version unsigned int ver; /// File name, used for error messages and opening the file std::string filename; /// Record list std::vector records; /// Root list. This is a select portion of the pointers from records std::vector roots; bool mUseSkinning; /// Parse the file void parse(Files::IStreamPtr stream); /// Get the file's version in a human readable form ///\returns A string containing a human readable NIF version number std::string printVersion(unsigned int version); ///Private Copy Constructor NIFFile (NIFFile const &); ///\overload void operator = (NIFFile const &); public: /// Used if file parsing fails void fail(const std::string &msg) { std::string err = " NIFFile Error: " + msg; err += "\nFile: " + filename; throw std::runtime_error(err); } /// Used when something goes wrong, but not catastrophically so void warn(const std::string &msg) { std::cerr << " NIFFile Warning: " << msg < NIFFilePtr; } // Namespace #endif openmw-openmw-0.38.0/components/nif/nifkey.hpp000066400000000000000000000116151264522266000213620ustar00rootroot00000000000000///File to handle keys used by nif file records #ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP #define OPENMW_COMPONENTS_NIF_NIFKEY_HPP #include "nifstream.hpp" #include #include #include #include "niffile.hpp" namespace Nif { template struct KeyT { T mValue; // FIXME: Implement Quadratic and TBC interpolation /* T mForwardValue; // Only for Quadratic interpolation, and never for QuaternionKeyList T mBackwardValue; // Only for Quadratic interpolation, and never for QuaternionKeyList float mTension; // Only for TBC interpolation float mBias; // Only for TBC interpolation float mContinuity; // Only for TBC interpolation */ }; typedef KeyT FloatKey; typedef KeyT Vector3Key; typedef KeyT Vector4Key; typedef KeyT QuaternionKey; template struct KeyMapT { typedef std::map< float, KeyT > MapType; typedef T ValueType; typedef KeyT KeyType; static const unsigned int sLinearInterpolation = 1; static const unsigned int sQuadraticInterpolation = 2; static const unsigned int sTBCInterpolation = 3; static const unsigned int sXYZInterpolation = 4; unsigned int mInterpolationType; MapType mKeys; KeyMapT() : mInterpolationType(sLinearInterpolation) {} //Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html) void read(NIFStream *nif, bool force=false) { assert(nif); mInterpolationType = 0; size_t count = nif->getUInt(); if(count == 0 && !force) return; mKeys.clear(); mInterpolationType = nif->getUInt(); KeyT key; NIFStream &nifReference = *nif; if(mInterpolationType == sLinearInterpolation) { for(size_t i = 0;i < count;i++) { float time = nif->getFloat(); readValue(nifReference, key); mKeys[time] = key; } } else if(mInterpolationType == sQuadraticInterpolation) { for(size_t i = 0;i < count;i++) { float time = nif->getFloat(); readQuadratic(nifReference, key); mKeys[time] = key; } } else if(mInterpolationType == sTBCInterpolation) { for(size_t i = 0;i < count;i++) { float time = nif->getFloat(); readTBC(nifReference, key); mKeys[time] = key; } } //XYZ keys aren't actually read here. //data.hpp sees that the last type read was sXYZInterpolation and: // Eats a floating point number, then // Re-runs the read function 3 more times. // When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation. else if(mInterpolationType == sXYZInterpolation) { //Don't try to read XYZ keys into the wrong part if ( count != 1 ) { std::stringstream error; error << "XYZ_ROTATION_KEY count should always be '1' . Retrieved Value: " << count; nif->file->fail(error.str()); } } else if (0 == mInterpolationType) { if (count != 0) nif->file->fail("Interpolation type 0 doesn't work with keys"); } else { std::stringstream error; error << "Unhandled interpolation type: " << mInterpolationType; nif->file->fail(error.str()); } } private: static void readValue(NIFStream &nif, KeyT &key) { key.mValue = (nif.*getValue)(); } template static void readQuadratic(NIFStream &nif, KeyT &key) { readValue(nif, key); /*key.mForwardValue = */(nif.*getValue)(); /*key.mBackwardValue = */(nif.*getValue)(); } static void readQuadratic(NIFStream &nif, KeyT &key) { readValue(nif, key); } static void readTBC(NIFStream &nif, KeyT &key) { readValue(nif, key); /*key.mTension = */nif.getFloat(); /*key.mBias = */nif.getFloat(); /*key.mContinuity = */nif.getFloat(); } }; typedef KeyMapT FloatKeyMap; typedef KeyMapT Vector3KeyMap; typedef KeyMapT Vector4KeyMap; typedef KeyMapT QuaternionKeyMap; typedef boost::shared_ptr FloatKeyMapPtr; typedef boost::shared_ptr Vector3KeyMapPtr; typedef boost::shared_ptr Vector4KeyMapPtr; typedef boost::shared_ptr QuaternionKeyMapPtr; } // Namespace #endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP openmw-openmw-0.38.0/components/nif/nifstream.cpp000066400000000000000000000056361264522266000220660ustar00rootroot00000000000000#include "nifstream.hpp" //For error reporting #include "niffile.hpp" namespace Nif { //Private functions uint8_t NIFStream::read_byte() { uint8_t byte; inp->read((char*)&byte, 1); return byte; } uint16_t NIFStream::read_le16() { uint8_t buffer[2]; inp->read((char*)buffer, 2); return buffer[0] | (buffer[1]<<8); } uint32_t NIFStream::read_le32() { uint8_t buffer[4]; inp->read((char*)buffer, 4); return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24); } float NIFStream::read_le32f() { union { uint32_t i; float f; } u = { read_le32() }; return u.f; } //Public functions osg::Vec2f NIFStream::getVector2() { osg::Vec2f vec; for(size_t i = 0;i < 2;i++) vec._v[i] = getFloat(); return vec; } osg::Vec3f NIFStream::getVector3() { osg::Vec3f vec; for(size_t i = 0;i < 3;i++) vec._v[i] = getFloat(); return vec; } osg::Vec4f NIFStream::getVector4() { osg::Vec4f vec; for(size_t i = 0;i < 4;i++) vec._v[i] = getFloat(); return vec; } Matrix3 NIFStream::getMatrix3() { Matrix3 mat; for(size_t i = 0;i < 3;i++) { for(size_t j = 0;j < 3;j++) mat.mValues[i][j] = getFloat(); } return mat; } osg::Quat NIFStream::getQuaternion() { osg::Quat quat; quat.w() = getFloat(); quat.x() = getFloat(); quat.y() = getFloat(); quat.z() = getFloat(); return quat; } Transformation NIFStream::getTrafo() { Transformation t; t.pos = getVector3(); t.rotation = getMatrix3(); t.scale = getFloat(); return t; } std::string NIFStream::getString(size_t length) { std::vector str (length+1, 0); inp->read(&str[0], length); return &str[0]; } std::string NIFStream::getString() { size_t size = read_le32(); return getString(size); } std::string NIFStream::getVersionString() { std::string result; std::getline(*inp, result); return result; } void NIFStream::getUShorts(osg::VectorGLushort* vec, size_t size) { vec->reserve(size); for(size_t i = 0;i < size;i++) vec->push_back(getUShort()); } void NIFStream::getFloats(std::vector &vec, size_t size) { vec.resize(size); for(size_t i = 0;i < vec.size();i++) vec[i] = getFloat(); } void NIFStream::getVector2s(osg::Vec2Array* vec, size_t size) { vec->reserve(size); for(size_t i = 0;i < size;i++) vec->push_back(getVector2()); } void NIFStream::getVector3s(osg::Vec3Array* vec, size_t size) { vec->reserve(size); for(size_t i = 0;i < size;i++) vec->push_back(getVector3()); } void NIFStream::getVector4s(osg::Vec4Array* vec, size_t size) { vec->reserve(size); for(size_t i = 0;i < size;i++) vec->push_back(getVector4()); } void NIFStream::getQuaternions(std::vector &quat, size_t size) { quat.resize(size); for(size_t i = 0;i < quat.size();i++) quat[i] = getQuaternion(); } } openmw-openmw-0.38.0/components/nif/nifstream.hpp000066400000000000000000000035631264522266000220700ustar00rootroot00000000000000///Functions used to read raw binary data from .nif files #ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP #define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP #include #include #include #include #include #include #include #include #include #include "niftypes.hpp" namespace Nif { class NIFFile; class NIFStream { /// Input stream Files::IStreamPtr inp; uint8_t read_byte(); uint16_t read_le16(); uint32_t read_le32(); float read_le32f(); public: NIFFile * const file; NIFStream (NIFFile * file, Files::IStreamPtr inp): inp (inp), file (file) {} void skip(size_t size) { inp->ignore(size); } char getChar() { return read_byte(); } short getShort() { return read_le16(); } unsigned short getUShort() { return read_le16(); } int getInt() { return read_le32(); } unsigned int getUInt() { return read_le32(); } float getFloat() { return read_le32f(); } osg::Vec2f getVector2(); osg::Vec3f getVector3(); osg::Vec4f getVector4(); Matrix3 getMatrix3(); osg::Quat getQuaternion(); Transformation getTrafo(); ///Read in a string of the given length std::string getString(size_t length); ///Read in a string of the length specified in the file std::string getString(); ///This is special since the version string doesn't start with a number, and ends with "\n" std::string getVersionString(); void getUShorts(osg::VectorGLushort* vec, size_t size); void getFloats(std::vector &vec, size_t size); void getVector2s(osg::Vec2Array* vec, size_t size); void getVector3s(osg::Vec3Array* vec, size_t size); void getVector4s(osg::Vec4Array* vec, size_t size); void getQuaternions(std::vector &quat, size_t size); }; } #endif openmw-openmw-0.38.0/components/nif/niftypes.hpp000066400000000000000000000043031264522266000217320ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (nif_types.h) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #ifndef OPENMW_COMPONENTS_NIF_NIFTYPES_HPP #define OPENMW_COMPONENTS_NIF_NIFTYPES_HPP #include #include // Common types used in NIF files namespace Nif { struct Matrix3 { float mValues[3][3]; Matrix3() { for (int i=0;i<3;++i) for (int j=0;j<3;++j) mValues[i][j] = (i==j) ? 1.f : 0.f; } bool isIdentity() const { for (int i=0;i<3;++i) for (int j=0;j<3;++j) if ((i==j) != (mValues[i][j] == 1)) return false; return true; } }; struct Transformation { osg::Vec3f pos; Matrix3 rotation; // this can contain scale components too, including negative and nonuniform scales float scale; osg::Matrixf toMatrix() const { osg::Matrixf transform; transform.setTrans(pos); for (int i=0;i<3;++i) for (int j=0;j<3;++j) transform(j,i) = rotation.mValues[i][j] * scale; // NB column/row major difference return transform; } bool isIdentity() const { return pos == osg::Vec3f(0,0,0) && rotation.isIdentity() && scale == 1.f; } static const Transformation& getIdentity() { static const Transformation identity = { osg::Vec3f(), Matrix3(), 1.0f }; return identity; } }; } // Namespace #endif openmw-openmw-0.38.0/components/nif/node.cpp000066400000000000000000000000001264522266000207770ustar00rootroot00000000000000openmw-openmw-0.38.0/components/nif/node.hpp000066400000000000000000000143721264522266000210250ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_NIF_NODE_HPP #define OPENMW_COMPONENTS_NIF_NODE_HPP #include "controlled.hpp" #include "extra.hpp" #include "data.hpp" #include "property.hpp" #include "niftypes.hpp" #include "controller.hpp" #include "base.hpp" #include namespace Nif { struct NiNode; /** A Node is an object that's part of the main NIF tree. It has parent node (unless it's the root), and transformation (location and rotation) relative to it's parent. */ class Node : public Named { public: // Node flags. Interpretation depends somewhat on the type of node. int flags; Transformation trafo; osg::Vec3f velocity; // Unused? Might be a run-time game state PropertyList props; // Bounding box info bool hasBounds; osg::Vec3f boundPos; Matrix3 boundRot; osg::Vec3f boundXYZ; // Box size void read(NIFStream *nif) { Named::read(nif); flags = nif->getUShort(); trafo = nif->getTrafo(); velocity = nif->getVector3(); props.read(nif); hasBounds = !!nif->getInt(); if(hasBounds) { nif->getInt(); // always 1 boundPos = nif->getVector3(); boundRot = nif->getMatrix3(); boundXYZ = nif->getVector3(); } parent = NULL; boneTrafo = NULL; boneIndex = -1; } void post(NIFFile *nif) { Named::post(nif); props.post(nif); } // Parent node, or NULL for the root node. As far as I'm aware, only // NiNodes (or types derived from NiNodes) can be parents. NiNode *parent; // Bone transformation. If set, node is a part of a skeleton. const Transformation *boneTrafo; // Bone weight info, from NiSkinData const NiSkinData::BoneInfo *boneInfo; // Bone index. If -1, this node is either not a bone, or if // boneTrafo is set it is the root bone in the skeleton. short boneIndex; void makeRootBone(const Transformation *tr) { boneTrafo = tr; boneIndex = -1; } void makeBone(short ind, const NiSkinData::BoneInfo &bi) { boneInfo = &bi; boneTrafo = &bi.trafo; boneIndex = ind; } }; struct NiNode : Node { NodeList children; NodeList effects; enum Flags { Flag_Hidden = 0x0001, Flag_MeshCollision = 0x0002, Flag_BBoxCollision = 0x0004 }; enum BSAnimFlags { AnimFlag_AutoPlay = 0x0020 }; enum BSParticleFlags { ParticleFlag_AutoPlay = 0x0020, ParticleFlag_LocalSpace = 0x0080 }; enum ControllerFlags { ControllerFlag_Active = 0x8 }; void read(NIFStream *nif) { Node::read(nif); children.read(nif); effects.read(nif); // Discard transformations for the root node, otherwise some meshes // occasionally get wrong orientation. Only for NiNode-s for now, but // can be expanded if needed. if (0 == recIndex && !Misc::StringUtils::ciEqual(name, "bip01")) { static_cast(this)->trafo = Nif::Transformation::getIdentity(); } } void post(NIFFile *nif) { Node::post(nif); children.post(nif); effects.post(nif); for(size_t i = 0;i < children.length();i++) { // Why would a unique list of children contain empty refs? if(!children[i].empty()) children[i]->parent = this; } } }; struct NiTriShape : Node { /* Possible flags: 0x40 - mesh has no vertex normals ? Only flags included in 0x47 (ie. 0x01, 0x02, 0x04 and 0x40) have been observed so far. */ NiTriShapeDataPtr data; NiSkinInstancePtr skin; void read(NIFStream *nif) { Node::read(nif); data.read(nif); skin.read(nif); } void post(NIFFile *nif) { Node::post(nif); data.post(nif); skin.post(nif); if (!skin.empty()) nif->setUseSkinning(true); } }; struct NiCamera : Node { struct Camera { // Camera frustrum float left, right, top, bottom, nearDist, farDist; // Viewport float vleft, vright, vtop, vbottom; // Level of detail modifier float LOD; void read(NIFStream *nif) { left = nif->getFloat(); right = nif->getFloat(); top = nif->getFloat(); bottom = nif->getFloat(); nearDist = nif->getFloat(); farDist = nif->getFloat(); vleft = nif->getFloat(); vright = nif->getFloat(); vtop = nif->getFloat(); vbottom = nif->getFloat(); LOD = nif->getFloat(); } }; Camera cam; void read(NIFStream *nif) { Node::read(nif); cam.read(nif); nif->getInt(); // -1 nif->getInt(); // 0 } }; struct NiAutoNormalParticles : Node { NiAutoNormalParticlesDataPtr data; void read(NIFStream *nif) { Node::read(nif); data.read(nif); nif->getInt(); // -1 } void post(NIFFile *nif) { Node::post(nif); data.post(nif); } }; struct NiRotatingParticles : Node { NiRotatingParticlesDataPtr data; void read(NIFStream *nif) { Node::read(nif); data.read(nif); nif->getInt(); // -1 } void post(NIFFile *nif) { Node::post(nif); data.post(nif); } }; // A node used as the base to switch between child nodes, such as for LOD levels. struct NiSwitchNode : public NiNode { void read(NIFStream *nif) { NiNode::read(nif); nif->getInt(); // unknown } }; struct NiLODNode : public NiSwitchNode { osg::Vec3f lodCenter; struct LODRange { float minRange; float maxRange; }; std::vector lodLevels; void read(NIFStream *nif) { NiSwitchNode::read(nif); lodCenter = nif->getVector3(); unsigned int numLodLevels = nif->getUInt(); for (unsigned int i=0; igetFloat(); r.maxRange = nif->getFloat(); lodLevels.push_back(r); } } }; } // Namespace #endif openmw-openmw-0.38.0/components/nif/property.cpp000066400000000000000000000045041264522266000217530ustar00rootroot00000000000000#include "property.hpp" #include "data.hpp" #include "controlled.hpp" namespace Nif { void Property::read(NIFStream *nif) { Named::read(nif); flags = nif->getUShort(); } void NiTexturingProperty::Texture::read(NIFStream *nif) { inUse = !!nif->getInt(); if(!inUse) return; texture.read(nif); clamp = nif->getInt(); filter = nif->getInt(); uvSet = nif->getInt(); // I have no idea, but I think these are actually two // PS2-specific shorts (ps2L and ps2K), followed by an unknown // short. nif->skip(6); } void NiTexturingProperty::Texture::post(NIFFile *nif) { texture.post(nif); } void NiTexturingProperty::read(NIFStream *nif) { Property::read(nif); apply = nif->getInt(); // Unknown, always 7. Probably the number of textures to read // below nif->getInt(); textures[0].read(nif); // Base textures[1].read(nif); // Dark textures[2].read(nif); // Detail textures[3].read(nif); // Gloss (never present) textures[4].read(nif); // Glow textures[5].read(nif); // Bump map if(textures[5].inUse) { // Ignore these at the moment /*float lumaScale =*/ nif->getFloat(); /*float lumaOffset =*/ nif->getFloat(); /*const Vector4 *lumaMatrix =*/ nif->getVector4(); } textures[6].read(nif); // Decal } void NiTexturingProperty::post(NIFFile *nif) { Property::post(nif); for(int i = 0;i < 7;i++) textures[i].post(nif); } void NiFogProperty::read(NIFStream *nif) { Property::read(nif); mFogDepth = nif->getFloat(); mColour = nif->getVector3(); } void S_MaterialProperty::read(NIFStream *nif) { ambient = nif->getVector3(); diffuse = nif->getVector3(); specular = nif->getVector3(); emissive = nif->getVector3(); glossiness = nif->getFloat(); alpha = nif->getFloat(); } void S_VertexColorProperty::read(NIFStream *nif) { vertmode = nif->getInt(); lightmode = nif->getInt(); } void S_AlphaProperty::read(NIFStream *nif) { threshold = nif->getChar(); } void S_StencilProperty::read(NIFStream *nif) { enabled = nif->getChar(); compareFunc = nif->getInt(); stencilRef = nif->getUInt(); stencilMask = nif->getUInt(); failAction = nif->getInt(); zFailAction = nif->getInt(); zPassAction = nif->getInt(); drawMode = nif->getInt(); } } openmw-openmw-0.38.0/components/nif/property.hpp000066400000000000000000000137051264522266000217630ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (property.h) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #ifndef OPENMW_COMPONENTS_NIF_PROPERTY_HPP #define OPENMW_COMPONENTS_NIF_PROPERTY_HPP #include "base.hpp" namespace Nif { class Property : public Named { public: // The meaning of these depends on the actual property type. unsigned int flags; void read(NIFStream *nif); }; class NiTexturingProperty : public Property { public: // A sub-texture struct Texture { /* Clamp mode 0 - clampS clampT 1 - clampS wrapT 2 - wrapS clampT 3 - wrapS wrapT */ /* Filter: 0 - nearest 1 - bilinear 2 - trilinear 3, 4, 5 - who knows */ bool inUse; NiSourceTexturePtr texture; int clamp, uvSet, filter; short unknown2; void read(NIFStream *nif); void post(NIFFile *nif); }; /* Apply mode: 0 - replace 1 - decal 2 - modulate 3 - hilight // These two are for PS2 only? 4 - hilight2 */ int apply; /* * The textures in this list are as follows: * * 0 - Base texture * 1 - Dark texture * 2 - Detail texture * 3 - Gloss texture (never used?) * 4 - Glow texture * 5 - Bump map texture * 6 - Decal texture */ enum TextureType { BaseTexture = 0, DarkTexture = 1, DetailTexture = 2, GlossTexture = 3, GlowTexture = 4, BumpTexture = 5, DecalTexture = 6, NumTextures = 7 // Sentry value }; Texture textures[7]; void read(NIFStream *nif); void post(NIFFile *nif); }; class NiFogProperty : public Property { public: float mFogDepth; osg::Vec3f mColour; void read(NIFStream *nif); }; // These contain no other data than the 'flags' field in Property class NiShadeProperty : public Property { }; class NiDitherProperty : public Property { }; class NiZBufferProperty : public Property { }; class NiSpecularProperty : public Property { }; class NiWireframeProperty : public Property { }; // The rest are all struct-based template struct StructPropT : Property { T data; void read(NIFStream *nif) { Property::read(nif); data.read(nif); } }; struct S_MaterialProperty { // The vector components are R,G,B osg::Vec3f ambient, diffuse, specular, emissive; float glossiness, alpha; void read(NIFStream *nif); }; struct S_VertexColorProperty { /* Vertex mode: 0 - source ignore 1 - source emmisive 2 - source amb diff Lighting mode 0 - lighting emmisive 1 - lighting emmisive ambient/diffuse */ int vertmode, lightmode; void read(NIFStream *nif); }; struct S_AlphaProperty { /* In NiAlphaProperty, the flags have the following meaning: Bit 0 : alpha blending enable Bits 1-4 : source blend mode Bits 5-8 : destination blend mode Bit 9 : alpha test enable Bit 10-12 : alpha test mode Bit 13 : no sorter flag ( disables triangle sorting ) blend modes (glBlendFunc): 0000 GL_ONE 0001 GL_ZERO 0010 GL_SRC_COLOR 0011 GL_ONE_MINUS_SRC_COLOR 0100 GL_DST_COLOR 0101 GL_ONE_MINUS_DST_COLOR 0110 GL_SRC_ALPHA 0111 GL_ONE_MINUS_SRC_ALPHA 1000 GL_DST_ALPHA 1001 GL_ONE_MINUS_DST_ALPHA 1010 GL_SRC_ALPHA_SATURATE test modes (glAlphaFunc): 000 GL_ALWAYS 001 GL_LESS 010 GL_EQUAL 011 GL_LEQUAL 100 GL_GREATER 101 GL_NOTEQUAL 110 GL_GEQUAL 111 GL_NEVER Taken from: http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html */ // Tested against when certain flags are set (see above.) unsigned char threshold; void read(NIFStream *nif); }; /* Docs taken from: http://niftools.sourceforge.net/doc/nif/NiStencilProperty.html */ struct S_StencilProperty { // Is stencil test enabled? unsigned char enabled; /* 0 TEST_NEVER 1 TEST_LESS 2 TEST_EQUAL 3 TEST_LESS_EQUAL 4 TEST_GREATER 5 TEST_NOT_EQUAL 6 TEST_GREATER_EQUAL 7 TEST_NEVER (though nifskope comment says TEST_ALWAYS, but ingame it is TEST_NEVER) */ int compareFunc; unsigned stencilRef; unsigned stencilMask; /* Stencil test fail action, depth test fail action and depth test pass action: 0 ACTION_KEEP 1 ACTION_ZERO 2 ACTION_REPLACE 3 ACTION_INCREMENT 4 ACTION_DECREMENT 5 ACTION_INVERT */ int failAction; int zFailAction; int zPassAction; /* Face draw mode: 0 DRAW_CCW_OR_BOTH 1 DRAW_CCW [default] 2 DRAW_CW 3 DRAW_BOTH */ int drawMode; void read(NIFStream *nif); }; class NiAlphaProperty : public StructPropT { }; class NiMaterialProperty : public StructPropT { }; class NiVertexColorProperty : public StructPropT { }; class NiStencilProperty : public StructPropT { }; } // Namespace #endif openmw-openmw-0.38.0/components/nif/record.hpp000066400000000000000000000053321264522266000213520ustar00rootroot00000000000000/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > WWW: http://openmw.sourceforge.net/ This file (record.h) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, 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 version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ #ifndef OPENMW_COMPONENTS_NIF_RECORD_HPP #define OPENMW_COMPONENTS_NIF_RECORD_HPP #include namespace Nif { class NIFFile; class NIFStream; enum RecordType { RC_MISSING = 0, RC_NiNode, RC_NiSwitchNode, RC_NiLODNode, RC_NiBillboardNode, RC_AvoidNode, RC_NiTriShape, RC_NiRotatingParticles, RC_NiAutoNormalParticles, RC_NiBSParticleNode, RC_NiCamera, RC_NiTexturingProperty, RC_NiFogProperty, RC_NiMaterialProperty, RC_NiZBufferProperty, RC_NiAlphaProperty, RC_NiVertexColorProperty, RC_NiShadeProperty, RC_NiDitherProperty, RC_NiWireframeProperty, RC_NiSpecularProperty, RC_NiStencilProperty, RC_NiVisController, RC_NiGeomMorpherController, RC_NiKeyframeController, RC_NiAlphaController, RC_NiUVController, RC_NiPathController, RC_NiMaterialColorController, RC_NiBSPArrayController, RC_NiParticleSystemController, RC_NiFlipController, RC_NiBSAnimationNode, RC_NiLight, RC_NiTextureEffect, RC_NiVertWeightsExtraData, RC_NiTextKeyExtraData, RC_NiStringExtraData, RC_NiGravity, RC_NiPlanarCollider, RC_NiParticleGrowFade, RC_NiParticleColorModifier, RC_NiParticleRotation, RC_NiFloatData, RC_NiTriShapeData, RC_NiVisData, RC_NiColorData, RC_NiPixelData, RC_NiMorphData, RC_NiKeyframeData, RC_NiSkinData, RC_NiUVData, RC_NiPosData, RC_NiRotatingParticlesData, RC_NiAutoNormalParticlesData, RC_NiSequenceStreamHelper, RC_NiSourceTexture, RC_NiSkinInstance, RC_RootCollisionNode }; /// Base class for all records struct Record { // Record type and type name int recType; std::string recName; size_t recIndex; Record() : recType(RC_MISSING), recIndex(~(size_t)0) {} /// Parses the record from file virtual void read(NIFStream *nif) = 0; /// Does post-processing, after the entire tree is loaded virtual void post(NIFFile *nif) {} virtual ~Record() {} }; } // Namespace #endif openmw-openmw-0.38.0/components/nif/recordptr.hpp000066400000000000000000000072211264522266000220770ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_NIF_RECORDPTR_HPP #define OPENMW_COMPONENTS_NIF_RECORDPTR_HPP #include "niffile.hpp" #include "nifstream.hpp" #include namespace Nif { /** A reference to another record. It is read as an index from the NIF, and later looked up in the index table to get an actual pointer. */ template class RecordPtrT { union { intptr_t index; X* ptr; }; public: RecordPtrT() : index(-2) {} /// Read the index from the nif void read(NIFStream *nif) { // Can only read the index once assert(index == -2); // Store the index for later index = nif->getInt(); assert(index >= -1); } /// Resolve index to pointer void post(NIFFile *nif) { if(index < 0) ptr = NULL; else { Record *r = nif->getRecord(index); // And cast it ptr = dynamic_cast(r); assert(ptr != NULL); } } /// Look up the actual object from the index const X* getPtr() const { assert(ptr != NULL); return ptr; } X* getPtr() { assert(ptr != NULL); return ptr; } const X& get() const { return *getPtr(); } X& get() { return *getPtr(); } /// Syntactic sugar const X* operator->() const { return getPtr(); } X* operator->() { return getPtr(); } /// Pointers are allowed to be empty bool empty() const { return ptr == NULL; } }; /** A list of references to other records. These are read as a list, and later converted to pointers as needed. Not an optimized implementation. */ template class RecordListT { typedef RecordPtrT Ptr; std::vector list; public: void read(NIFStream *nif) { int len = nif->getInt(); list.resize(len); for(size_t i=0;i < list.size();i++) list[i].read(nif); } void post(NIFFile *nif) { for(size_t i=0;i < list.size();i++) list[i].post(nif); } const Ptr& operator[](size_t index) const { return list.at(index); } Ptr& operator[](size_t index) { return list.at(index); } size_t length() const { return list.size(); } }; class Node; class Extra; class Property; class NiUVData; class NiPosData; class NiVisData; class Controller; class Controlled; class NiSkinData; class NiFloatData; struct NiMorphData; class NiPixelData; class NiColorData; struct NiKeyframeData; class NiTriShapeData; class NiSkinInstance; class NiSourceTexture; class NiRotatingParticlesData; class NiAutoNormalParticlesData; typedef RecordPtrT NodePtr; typedef RecordPtrT ExtraPtr; typedef RecordPtrT NiUVDataPtr; typedef RecordPtrT NiPosDataPtr; typedef RecordPtrT NiVisDataPtr; typedef RecordPtrT ControllerPtr; typedef RecordPtrT ControlledPtr; typedef RecordPtrT NiSkinDataPtr; typedef RecordPtrT NiMorphDataPtr; typedef RecordPtrT NiPixelDataPtr; typedef RecordPtrT NiFloatDataPtr; typedef RecordPtrT NiColorDataPtr; typedef RecordPtrT NiKeyframeDataPtr; typedef RecordPtrT NiTriShapeDataPtr; typedef RecordPtrT NiSkinInstancePtr; typedef RecordPtrT NiSourceTexturePtr; typedef RecordPtrT NiRotatingParticlesDataPtr; typedef RecordPtrT NiAutoNormalParticlesDataPtr; typedef RecordListT NodeList; typedef RecordListT PropertyList; typedef RecordListT NiSourceTextureList; } // Namespace #endif openmw-openmw-0.38.0/components/nifbullet/000077500000000000000000000000001264522266000205705ustar00rootroot00000000000000openmw-openmw-0.38.0/components/nifbullet/bulletnifloader.cpp000066400000000000000000000243661264522266000244620ustar00rootroot00000000000000#include "bulletnifloader.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { osg::Matrixf getWorldTransform(const Nif::Node *node) { if(node->parent != NULL) return node->trafo.toMatrix() * getWorldTransform(node->parent); return node->trafo.toMatrix(); } btVector3 getbtVector(const osg::Vec3f &v) { return btVector3(v.x(), v.y(), v.z()); } } namespace NifBullet { BulletNifLoader::BulletNifLoader() : mCompoundShape(NULL) , mStaticMesh(NULL) { } BulletNifLoader::~BulletNifLoader() { } osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr nif) { mShape = new Resource::BulletShape; mCompoundShape = NULL; mStaticMesh = NULL; if (nif->numRoots() < 1) { warn("Found no root nodes in NIF."); return mShape; } Nif::Record *r = nif->getRoot(0); assert(r != NULL); Nif::Node *node = dynamic_cast(r); if (node == NULL) { warn("First root in file was not a node, but a " + r->recName + ". Skipping file."); return mShape; } if (findBoundingBox(node)) { std::auto_ptr compound (new btCompoundShape); btBoxShape* boxShape = new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents)); btTransform transform = btTransform::getIdentity(); transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate)); compound->addChildShape(transform, boxShape); mShape->mCollisionShape = compound.release(); return mShape; } else { bool autogenerated = hasAutoGeneratedCollision(node); bool isAnimated = false; // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated std::string filename = nif->getFilename(); size_t slashpos = filename.find_last_of("/\\"); if (slashpos == std::string::npos) slashpos = 0; if (slashpos+1 < filename.size() && (filename[slashpos+1] == 'x' || filename[slashpos+1] == 'X')) { isAnimated = true; } handleNode(node, 0, autogenerated, isAnimated, autogenerated); if (mCompoundShape) { mShape->mCollisionShape = mCompoundShape; if (mStaticMesh) { btTransform trans; trans.setIdentity(); mCompoundShape->addChildShape(trans, new Resource::TriangleMeshShape(mStaticMesh,true)); } } else if (mStaticMesh) mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh,true); return mShape; } } // Find a boundingBox in the node hierarchy. // Return: use bounding box for collision? bool BulletNifLoader::findBoundingBox(const Nif::Node* node, int flags) { flags |= node->flags; if (node->hasBounds) { mShape->mCollisionBoxHalfExtents = node->boundXYZ; mShape->mCollisionBoxTranslate = node->boundPos; if (flags & Nif::NiNode::Flag_BBoxCollision) { return true; } } const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { const Nif::NodeList &list = ninode->children; for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) { bool found = findBoundingBox (list[i].getPtr()); if (found) return true; } } } return false; } bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode) { const Nif::NiNode *ninode = dynamic_cast(rootNode); if(ninode) { const Nif::NodeList &list = ninode->children; for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) { if(list[i].getPtr()->recType == Nif::RC_RootCollisionNode) return false; } } } return true; } void BulletNifLoader::handleNode(const Nif::Node *node, int flags, bool isCollisionNode, bool isAnimated, bool autogenerated) { // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. flags |= node->flags; if (!node->controller.empty() && node->controller->recType == Nif::RC_NiKeyframeController && (node->controller->flags & Nif::NiNode::ControllerFlag_Active)) isAnimated = true; isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); // Don't collide with AvoidNode shapes if(node->recType == Nif::RC_AvoidNode) flags |= 0x800; // Check for extra data Nif::Extra const *e = node; while (!e->extra.empty()) { // Get the next extra data in the list e = e->extra.getPtr(); assert(e != NULL); if (e->recType == Nif::RC_NiStringExtraData) { // String markers may contain important information // affecting the entire subtree of this node Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e; // not sure what the difference between NCO and NCC is, or if there even is one if (sd->string == "NCO" || sd->string == "NCC") { // No collision. Use an internal flag setting to mark this. flags |= 0x800; } else if (sd->string == "MRK" && autogenerated) { // Marker can still have collision if the model explicitely specifies it via a RootCollisionNode. return; } } } if (isCollisionNode) { // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! // It must be ignored completely. // (occurs in tr_ex_imp_wall_arch_04.nif) if(!node->hasBounds && node->recType == Nif::RC_NiTriShape) { handleNiTriShape(static_cast(node), flags, getWorldTransform(node), isAnimated); } } // For NiNodes, loop through children const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { const Nif::NodeList &list = ninode->children; for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) handleNode(list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated); } } } void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf &transform, bool isAnimated) { assert(shape != NULL); // Interpret flags bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0; bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0; bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0; // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. if ((flags & 0x800)) { return; } if (!collide && !bbcollide && hidden) // This mesh apparently isn't being used for anything, so don't // bother setting it up. return; if (!shape->skin.empty()) isAnimated = false; if (isAnimated) { if (!mCompoundShape) mCompoundShape = new btCompoundShape(); btTriangleMesh* childMesh = new btTriangleMesh(); const Nif::NiTriShapeData *data = shape->data.getPtr(); childMesh->preallocateVertices(data->vertices->size()); childMesh->preallocateIndices(data->triangles->size()); const osg::Vec3Array& vertices = *data->vertices; const osg::DrawElementsUShort& triangles = *data->triangles; size_t numtris = data->triangles->size(); for(size_t i = 0;i < numtris;i+=3) { osg::Vec3f b1 = vertices[triangles[i+0]]; osg::Vec3f b2 = vertices[triangles[i+1]]; osg::Vec3f b3 = vertices[triangles[i+2]]; childMesh->addTriangle(getbtVector(b1), getbtVector(b2), getbtVector(b3)); } Resource::TriangleMeshShape* childShape = new Resource::TriangleMeshShape(childMesh,true); float scale = shape->trafo.scale; const Nif::Node* parent = shape; while (parent->parent) { parent = parent->parent; scale *= parent->trafo.scale; } osg::Quat q = transform.getRotate(); osg::Vec3f v = transform.getTrans(); childShape->setLocalScaling(btVector3(scale, scale, scale)); btTransform trans(btQuaternion(q.x(), q.y(), q.z(), q.w()), btVector3(v.x(), v.y(), v.z())); mShape->mAnimatedShapes.insert(std::make_pair(shape->recIndex, mCompoundShape->getNumChildShapes())); mCompoundShape->addChildShape(trans, childShape); } else { if (!mStaticMesh) mStaticMesh = new btTriangleMesh(false); // Static shape, just transform all vertices into position const Nif::NiTriShapeData *data = shape->data.getPtr(); const osg::Vec3Array& vertices = *data->vertices; const osg::DrawElementsUShort& triangles = *data->triangles; mStaticMesh->preallocateVertices(data->vertices->size()); mStaticMesh->preallocateIndices(data->triangles->size()); size_t numtris = data->triangles->size(); for(size_t i = 0;i < numtris;i+=3) { osg::Vec3f b1 = vertices[triangles[i+0]]*transform; osg::Vec3f b2 = vertices[triangles[i+1]]*transform; osg::Vec3f b3 = vertices[triangles[i+2]]*transform; mStaticMesh->addTriangle(getbtVector(b1), getbtVector(b2), getbtVector(b3)); } } } } // namespace NifBullet openmw-openmw-0.38.0/components/nifbullet/bulletnifloader.hpp000066400000000000000000000027621264522266000244630ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_NIFBULLET_BULLETNIFLOADER_HPP #define OPENMW_COMPONENTS_NIFBULLET_BULLETNIFLOADER_HPP #include #include #include #include #include #include #include #include #include #include #include class btTriangleMesh; class btCompoundShape; class btCollisionShape; namespace Nif { class Node; struct Transformation; struct NiTriShape; } namespace NifBullet { /** *Load bulletShape from NIF files. */ class BulletNifLoader { public: BulletNifLoader(); virtual ~BulletNifLoader(); void warn(const std::string &msg) { std::cerr << "NIFLoader: Warn:" << msg << "\n"; } void fail(const std::string &msg) { std::cerr << "NIFLoader: Fail: "<< msg << std::endl; abort(); } osg::ref_ptr load(const Nif::NIFFilePtr file); private: bool findBoundingBox(const Nif::Node* node, int flags = 0); void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); bool hasAutoGeneratedCollision(const Nif::Node *rootNode); void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated); btCompoundShape* mCompoundShape; btTriangleMesh* mStaticMesh; osg::ref_ptr mShape; }; } #endif openmw-openmw-0.38.0/components/nifosg/000077500000000000000000000000001264522266000200715ustar00rootroot00000000000000openmw-openmw-0.38.0/components/nifosg/controller.cpp000066400000000000000000000313531264522266000227650ustar00rootroot00000000000000#include "controller.hpp" #include #include #include #include #include #include #include #include #include "userdata.hpp" namespace NifOsg { ControllerFunction::ControllerFunction(const Nif::Controller *ctrl) : mFrequency(ctrl->frequency) , mPhase(ctrl->phase) , mStartTime(ctrl->timeStart) , mStopTime(ctrl->timeStop) , mExtrapolationMode(static_cast((ctrl->flags&0x6) >> 1)) { } float ControllerFunction::calculate(float value) const { float time = mFrequency * value + mPhase; if (time >= mStartTime && time <= mStopTime) return time; switch (mExtrapolationMode) { case Cycle: { float delta = mStopTime - mStartTime; if ( delta <= 0 ) return mStartTime; float cycles = ( time - mStartTime ) / delta; float remainder = ( cycles - std::floor( cycles ) ) * delta; return mStartTime + remainder; } case Reverse: { float delta = mStopTime - mStartTime; if ( delta <= 0 ) return mStartTime; float cycles = ( time - mStartTime ) / delta; float remainder = ( cycles - std::floor( cycles ) ) * delta; // Even number of cycles? if ( ( static_cast(std::fabs( std::floor( cycles ) )) % 2 ) == 0 ) return mStartTime + remainder; return mStopTime - remainder; } case Constant: default: return std::min(mStopTime, std::max(mStartTime, time)); } } float ControllerFunction::getMaximum() const { return mStopTime; } KeyframeController::KeyframeController() { } KeyframeController::KeyframeController(const KeyframeController ©, const osg::CopyOp ©op) : osg::NodeCallback(copy, copyop) , Controller(copy) , mRotations(copy.mRotations) , mXRotations(copy.mXRotations) , mYRotations(copy.mYRotations) , mZRotations(copy.mZRotations) , mTranslations(copy.mTranslations) , mScales(copy.mScales) { } KeyframeController::KeyframeController(const Nif::NiKeyframeData *data) : mRotations(data->mRotations) , mXRotations(data->mXRotations, 0.f) , mYRotations(data->mYRotations, 0.f) , mZRotations(data->mZRotations, 0.f) , mTranslations(data->mTranslations, osg::Vec3f()) , mScales(data->mScales, 1.f) { } osg::Quat KeyframeController::getXYZRotation(float time) const { float xrot = 0, yrot = 0, zrot = 0; if (!mXRotations.empty()) xrot = mXRotations.interpKey(time); if (!mYRotations.empty()) yrot = mYRotations.interpKey(time); if (!mZRotations.empty()) zrot = mZRotations.interpKey(time); osg::Quat xr(xrot, osg::Vec3f(1,0,0)); osg::Quat yr(yrot, osg::Vec3f(0,1,0)); osg::Quat zr(zrot, osg::Vec3f(0,0,1)); return (xr*yr*zr); } osg::Vec3f KeyframeController::getTranslation(float time) const { if(!mTranslations.empty()) return mTranslations.interpKey(time); return osg::Vec3f(); } void KeyframeController::operator() (osg::Node* node, osg::NodeVisitor* nv) { if (hasInput()) { osg::MatrixTransform* trans = static_cast(node); osg::Matrix mat = trans->getMatrix(); float time = getInputValue(nv); NodeUserData* userdata = static_cast(trans->getUserDataContainer()->getUserObject(0)); Nif::Matrix3& rot = userdata->mRotationScale; bool setRot = false; if(!mRotations.empty()) { mat.setRotate(mRotations.interpKey(time)); setRot = true; } else if (!mXRotations.empty() || !mYRotations.empty() || !mZRotations.empty()) { mat.setRotate(getXYZRotation(time)); setRot = true; } else { // no rotation specified, use the previous value from the UserData for (int i=0;i<3;++i) for (int j=0;j<3;++j) mat(j,i) = rot.mValues[i][j]; // NB column/row major difference } if (setRot) // copy the new values back to the UserData for (int i=0;i<3;++i) for (int j=0;j<3;++j) rot.mValues[i][j] = mat(j,i); // NB column/row major difference float& scale = userdata->mScale; if(!mScales.empty()) scale = mScales.interpKey(time); for (int i=0;i<3;++i) for (int j=0;j<3;++j) mat(i,j) *= scale; if(!mTranslations.empty()) mat.setTrans(mTranslations.interpKey(time)); trans->setMatrix(mat); } traverse(node, nv); } GeomMorpherController::GeomMorpherController() { } GeomMorpherController::GeomMorpherController(const GeomMorpherController ©, const osg::CopyOp ©op) : osg::Drawable::UpdateCallback(copy, copyop) , Controller(copy) , mKeyFrames(copy.mKeyFrames) { } GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data) { for (unsigned int i=0; imMorphs.size(); ++i) mKeyFrames.push_back(FloatInterpolator(data->mMorphs[i].mKeyFrames)); } void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable) { osgAnimation::MorphGeometry* morphGeom = static_cast(drawable); if (hasInput()) { if (mKeyFrames.size() <= 1) return; float input = getInputValue(nv); int i = 0; for (std::vector::iterator it = mKeyFrames.begin()+1; it != mKeyFrames.end(); ++it,++i) { float val = 0; if (!(*it).empty()) val = it->interpKey(input); val = std::max(0.f, std::min(1.f, val)); osgAnimation::MorphGeometry::MorphTarget& target = morphGeom->getMorphTarget(i); if (target.getWeight() != val) { target.setWeight(val); morphGeom->dirty(); } } } // morphGeometry::transformSoftwareMethod() done in cull callback i.e. only for visible morph geometries } UVController::UVController() { } UVController::UVController(const Nif::NiUVData *data, std::set textureUnits) : mUTrans(data->mKeyList[0], 0.f) , mVTrans(data->mKeyList[1], 0.f) , mUScale(data->mKeyList[2], 1.f) , mVScale(data->mKeyList[3], 1.f) , mTextureUnits(textureUnits) { } UVController::UVController(const UVController& copy, const osg::CopyOp& copyop) : osg::Object(copy, copyop), StateSetUpdater(copy, copyop), Controller(copy) , mUTrans(copy.mUTrans) , mVTrans(copy.mVTrans) , mUScale(copy.mUScale) , mVScale(copy.mVScale) , mTextureUnits(copy.mTextureUnits) { } void UVController::setDefaults(osg::StateSet *stateset) { osg::TexMat* texMat = new osg::TexMat; for (std::set::const_iterator it = mTextureUnits.begin(); it != mTextureUnits.end(); ++it) stateset->setTextureAttributeAndModes(*it, texMat, osg::StateAttribute::ON); } void UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv) { if (hasInput()) { float value = getInputValue(nv); float uTrans = mUTrans.interpKey(value); float vTrans = mVTrans.interpKey(value); float uScale = mUScale.interpKey(value); float vScale = mVScale.interpKey(value); osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1); mat.setTrans(uTrans, vTrans, 0); // setting once is enough because all other texture units share the same TexMat (see setDefaults). if (!mTextureUnits.empty()) { osg::TexMat* texMat = static_cast(stateset->getTextureAttribute(*mTextureUnits.begin(), osg::StateAttribute::TEXMAT)); texMat->setMatrix(mat); } } } VisController::VisController(const Nif::NiVisData *data) : mData(data->mVis) { } VisController::VisController() { } VisController::VisController(const VisController ©, const osg::CopyOp ©op) : osg::NodeCallback(copy, copyop) , Controller(copy) , mData(copy.mData) { } bool VisController::calculate(float time) const { if(mData.size() == 0) return true; for(size_t i = 1;i < mData.size();i++) { if(mData[i].time > time) return mData[i-1].isSet; } return mData.back().isSet; } void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv) { if (hasInput()) { bool vis = calculate(getInputValue(nv)); // Leave 0x1 enabled for UpdateVisitor, so we can make ourselves visible again in the future from this update callback node->setNodeMask(vis ? ~0 : 0x1); } traverse(node, nv); } AlphaController::AlphaController(const Nif::NiFloatData *data) : mData(data->mKeyList, 1.f) { } AlphaController::AlphaController() { } AlphaController::AlphaController(const AlphaController ©, const osg::CopyOp ©op) : StateSetUpdater(copy, copyop), Controller(copy) , mData(copy.mData) { } void AlphaController::setDefaults(osg::StateSet *stateset) { // need to create a deep copy of StateAttributes we will modify osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) { if (hasInput()) { float value = mData.interpKey(getInputValue(nv)); osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); diffuse.a() = value; mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse); } } MaterialColorController::MaterialColorController(const Nif::NiPosData *data) : mData(data->mKeyList, osg::Vec3f(1,1,1)) { } MaterialColorController::MaterialColorController() { } MaterialColorController::MaterialColorController(const MaterialColorController ©, const osg::CopyOp ©op) : StateSetUpdater(copy, copyop), Controller(copy) , mData(copy.mData) { } void MaterialColorController::setDefaults(osg::StateSet *stateset) { // need to create a deep copy of StateAttributes we will modify osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); stateset->setAttribute(osg::clone(mat, osg::CopyOp::DEEP_COPY_ALL), osg::StateAttribute::ON); } void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) { if (hasInput()) { osg::Vec3f value = mData.interpKey(getInputValue(nv)); osg::Material* mat = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); diffuse.set(value.x(), value.y(), value.z(), diffuse.a()); mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse); } } FlipController::FlipController(const Nif::NiFlipController *ctrl, std::vector > textures) : mTexSlot(ctrl->mTexSlot) , mDelta(ctrl->mDelta) , mTextures(textures) { } FlipController::FlipController(int texSlot, float delta, std::vector > textures) : mTexSlot(texSlot) , mDelta(delta) , mTextures(textures) { } FlipController::FlipController() : mTexSlot(0) , mDelta(0.f) { } FlipController::FlipController(const FlipController ©, const osg::CopyOp ©op) : StateSetUpdater(copy, copyop) , Controller(copy) , mTexSlot(copy.mTexSlot) , mDelta(copy.mDelta) , mTextures(copy.mTextures) { } void FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv) { if (hasInput() && mDelta != 0) { int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]); } } ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController *ctrl) : mEmitStart(ctrl->startTime), mEmitStop(ctrl->stopTime) { } ParticleSystemController::ParticleSystemController() : mEmitStart(0.f), mEmitStop(0.f) { } ParticleSystemController::ParticleSystemController(const ParticleSystemController ©, const osg::CopyOp ©op) : osg::NodeCallback(copy, copyop) , Controller(copy) , mEmitStart(copy.mEmitStart) , mEmitStop(copy.mEmitStop) { } void ParticleSystemController::operator() (osg::Node* node, osg::NodeVisitor* nv) { if (hasInput()) { osgParticle::ParticleProcessor* emitter = static_cast(node); float time = getInputValue(nv); emitter->setEnabled(time >= mEmitStart && time < mEmitStop); } traverse(node, nv); } } openmw-openmw-0.38.0/components/nifosg/controller.hpp000066400000000000000000000226751264522266000230010ustar00rootroot00000000000000#ifndef COMPONENTS_NIFOSG_CONTROLLER_H #define COMPONENTS_NIFOSG_CONTROLLER_H #include #include #include #include #include #include #include #include //UVController // FlipController #include #include #include #include #include #include namespace osg { class Node; class StateSet; } namespace osgParticle { class Emitter; } namespace osgAnimation { class MorphGeometry; } namespace NifOsg { // interpolation of keyframes template class ValueInterpolator { public: typedef typename MapT::ValueType ValueT; ValueInterpolator() : mDefaultVal(ValueT()) { } ValueInterpolator(boost::shared_ptr keys, ValueT defaultVal = ValueT()) : mKeys(keys) , mDefaultVal(defaultVal) { if (keys) { mLastLowKey = mKeys->mKeys.end(); mLastHighKey = mKeys->mKeys.end(); } } ValueT interpKey(float time) const { if (empty()) return mDefaultVal; const typename MapT::MapType & keys = mKeys->mKeys; if(time <= keys.begin()->first) return keys.begin()->second.mValue; // retrieve the current position in the map, optimized for the most common case // where time moves linearly along the keyframe track typename MapT::MapType::const_iterator it = mLastHighKey; if (mLastHighKey != keys.end()) { if (time > mLastHighKey->first) { // try if we're there by incrementing one ++mLastLowKey; ++mLastHighKey; it = mLastHighKey; } if (mLastHighKey == keys.end() || (time < mLastLowKey->first || time > mLastHighKey->first)) it = keys.lower_bound(time); // still not there, reorient by performing lower_bound check on the whole map } else it = keys.lower_bound(time); // now do the actual interpolation if (it != keys.end()) { float aTime = it->first; const typename MapT::KeyType* aKey = &it->second; // cache for next time mLastHighKey = it; assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function typename MapT::MapType::const_iterator last = --it; mLastLowKey = last; float aLastTime = last->first; const typename MapT::KeyType* aLastKey = &last->second; float a = (time - aLastTime) / (aTime - aLastTime); return InterpolationFunc()(aLastKey->mValue, aKey->mValue, a); } else return keys.rbegin()->second.mValue; } bool empty() const { return !mKeys || mKeys->mKeys.empty(); } private: mutable typename MapT::MapType::const_iterator mLastLowKey; mutable typename MapT::MapType::const_iterator mLastHighKey; boost::shared_ptr mKeys; ValueT mDefaultVal; }; struct LerpFunc { template inline ValueType operator()(const ValueType& a, const ValueType& b, float fraction) { return a + ((b - a) * fraction); } }; struct QuaternionSlerpFunc { inline osg::Quat operator()(const osg::Quat& a, const osg::Quat& b, float fraction) { osg::Quat result; result.slerp(fraction, a, b); return result; } }; typedef ValueInterpolator QuaternionInterpolator; typedef ValueInterpolator FloatInterpolator; typedef ValueInterpolator Vec3Interpolator; class ControllerFunction : public SceneUtil::ControllerFunction { private: float mFrequency; float mPhase; float mStartTime; float mStopTime; enum ExtrapolationMode { Cycle = 0, Reverse = 1, Constant = 2 }; ExtrapolationMode mExtrapolationMode; public: ControllerFunction(const Nif::Controller *ctrl); float calculate(float value) const; virtual float getMaximum() const; }; /// Must be set on an osgAnimation::MorphGeometry. class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller { public: GeomMorpherController(const Nif::NiMorphData* data); GeomMorpherController(); GeomMorpherController(const GeomMorpherController& copy, const osg::CopyOp& copyop); META_Object(NifOsg, GeomMorpherController) virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable); private: std::vector mKeyFrames; }; class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller { public: KeyframeController(const Nif::NiKeyframeData *data); KeyframeController(); KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop); META_Object(NifOsg, KeyframeController) virtual osg::Vec3f getTranslation(float time) const; virtual void operator() (osg::Node*, osg::NodeVisitor*); private: QuaternionInterpolator mRotations; FloatInterpolator mXRotations; FloatInterpolator mYRotations; FloatInterpolator mZRotations; Vec3Interpolator mTranslations; FloatInterpolator mScales; osg::Quat getXYZRotation(float time) const; }; class UVController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller { public: UVController(); UVController(const UVController&,const osg::CopyOp&); UVController(const Nif::NiUVData *data, std::set textureUnits); META_Object(NifOsg,UVController) virtual void setDefaults(osg::StateSet* stateset); virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv); private: FloatInterpolator mUTrans; FloatInterpolator mVTrans; FloatInterpolator mUScale; FloatInterpolator mVScale; std::set mTextureUnits; }; class VisController : public osg::NodeCallback, public SceneUtil::Controller { private: std::vector mData; bool calculate(float time) const; public: VisController(const Nif::NiVisData *data); VisController(); VisController(const VisController& copy, const osg::CopyOp& copyop); META_Object(NifOsg, VisController) virtual void operator() (osg::Node* node, osg::NodeVisitor* nv); }; class AlphaController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller { private: FloatInterpolator mData; public: AlphaController(const Nif::NiFloatData *data); AlphaController(); AlphaController(const AlphaController& copy, const osg::CopyOp& copyop); virtual void setDefaults(osg::StateSet* stateset); virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv); META_Object(NifOsg, AlphaController) }; class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller { private: Vec3Interpolator mData; public: MaterialColorController(const Nif::NiPosData *data); MaterialColorController(); MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop); META_Object(NifOsg, MaterialColorController) virtual void setDefaults(osg::StateSet* stateset); virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv); }; class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller { private: int mTexSlot; float mDelta; std::vector > mTextures; public: FlipController(const Nif::NiFlipController* ctrl, std::vector > textures); FlipController(int texSlot, float delta, std::vector > textures); FlipController(); FlipController(const FlipController& copy, const osg::CopyOp& copyop); META_Object(NifOsg, FlipController) virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv); }; class ParticleSystemController : public osg::NodeCallback, public SceneUtil::Controller { public: ParticleSystemController(const Nif::NiParticleSystemController* ctrl); ParticleSystemController(); ParticleSystemController(const ParticleSystemController& copy, const osg::CopyOp& copyop); META_Object(NifOsg, ParticleSystemController) virtual void operator() (osg::Node* node, osg::NodeVisitor* nv); private: float mEmitStart; float mEmitStop; }; } #endif openmw-openmw-0.38.0/components/nifosg/nifloader.cpp000066400000000000000000002137341264522266000225520ustar00rootroot00000000000000#include "nifloader.hpp" #include #include #include #include #include #include #include // resource #include #include #include // skel #include // particle #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "particle.hpp" #include "userdata.hpp" namespace { void getAllNiNodes(const Nif::Node* node, std::vector& outIndices) { const Nif::NiNode* ninode = dynamic_cast(node); if (ninode) { outIndices.push_back(ninode->recIndex); for (unsigned int i=0; ichildren.length(); ++i) if (!ninode->children[i].empty()) getAllNiNodes(ninode->children[i].getPtr(), outIndices); } } // Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the node hierarchy above it. void collectDrawableProperties(const Nif::Node* nifNode, std::vector& out) { const Nif::PropertyList& props = nifNode->props; for (size_t i = 0; i recType) { case Nif::RC_NiMaterialProperty: case Nif::RC_NiVertexColorProperty: case Nif::RC_NiSpecularProperty: case Nif::RC_NiAlphaProperty: out.push_back(props[i].getPtr()); break; default: break; } } } if (nifNode->parent) collectDrawableProperties(nifNode->parent, out); } class FrameSwitch : public osg::Group { public: FrameSwitch() { } FrameSwitch(const FrameSwitch& copy, const osg::CopyOp& copyop) : osg::Group(copy, copyop) { } META_Object(NifOsg, FrameSwitch) virtual void traverse(osg::NodeVisitor& nv) { if (nv.getTraversalMode() != osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN && nv.getVisitorType() != osg::NodeVisitor::UPDATE_VISITOR) osg::Group::traverse(nv); else { for (unsigned int i=0; iaccept(nv); } } } }; // NodeCallback used to have a node always oriented towards the camera. The node can have translation and scale // set just like a regular MatrixTransform, but the rotation set will be overridden in order to face the camera. // Must be set as a cull callback. class BillboardCallback : public osg::NodeCallback { public: BillboardCallback() { } BillboardCallback(const BillboardCallback& copy, const osg::CopyOp& copyop) : osg::NodeCallback(copy, copyop) { } META_Object(NifOsg, BillboardCallback) virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = dynamic_cast(nv); if (node && cv) { osg::Matrix modelView = *cv->getModelViewMatrix(); // attempt to preserve scale float mag[3]; for (int i=0;i<3;++i) { mag[i] = std::sqrt(modelView(0,i) * modelView(0,i) + modelView(1,i) * modelView(1,i) + modelView(2,i) * modelView(2,i)); } modelView.setRotate(osg::Quat()); modelView(0,0) = mag[0]; modelView(1,1) = mag[1]; modelView(2,2) = mag[2]; cv->pushModelViewMatrix(new osg::RefMatrix(modelView), osg::Transform::RELATIVE_RF); traverse(node, nv); cv->popModelViewMatrix(); } else traverse(node, nv); } }; struct UpdateMorphGeometry : public osg::Drawable::CullCallback { UpdateMorphGeometry() : mLastFrameNumber(0) { } UpdateMorphGeometry(const UpdateMorphGeometry& copy, const osg::CopyOp& copyop) : osg::Drawable::CullCallback(copy, copyop) , mLastFrameNumber(0) { } META_Object(NifOsg, UpdateMorphGeometry) virtual bool cull(osg::NodeVisitor* nv, osg::Drawable * drw, osg::State *) const { osgAnimation::MorphGeometry* geom = static_cast(drw); if (!geom) return false; if (mLastFrameNumber == nv->getTraversalNumber()) return false; mLastFrameNumber = nv->getTraversalNumber(); geom->transformSoftwareMethod(); return false; } private: mutable unsigned int mLastFrameNumber; }; // Callback to return a static bounding box for a MorphGeometry. The idea is to not recalculate the bounding box // every time the morph weights change. To do so we return a maximum containing box that is big enough for all possible combinations of morph targets. class StaticBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback { public: StaticBoundingBoxCallback() { } StaticBoundingBoxCallback(const osg::BoundingBox& bounds) : mBoundingBox(bounds) { } StaticBoundingBoxCallback(const StaticBoundingBoxCallback& copy, const osg::CopyOp& copyop) : osg::Drawable::ComputeBoundingBoxCallback(copy, copyop) , mBoundingBox(copy.mBoundingBox) { } META_Object(NifOsg, StaticBoundingBoxCallback) virtual osg::BoundingBox computeBound(const osg::Drawable&) const { return mBoundingBox; } private: osg::BoundingBox mBoundingBox; }; void extractTextKeys(const Nif::NiTextKeyExtraData *tk, NifOsg::TextKeyMap &textkeys) { for(size_t i = 0;i < tk->list.size();i++) { const std::string &str = tk->list[i].text; std::string::size_type pos = 0; while(pos < str.length()) { if(::isspace(str[pos])) { pos++; continue; } std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); if(nextpos != std::string::npos) { do { nextpos--; } while(nextpos > pos && ::isspace(str[nextpos])); nextpos++; } else if(::isspace(*str.rbegin())) { std::string::const_iterator last = str.end(); do { --last; } while(last != str.begin() && ::isspace(*last)); nextpos = std::distance(str.begin(), ++last); } std::string result = str.substr(pos, nextpos-pos); textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::lowerCase(result))); pos = nextpos; } } } } namespace NifOsg { bool Loader::sShowMarkers = false; void Loader::setShowMarkers(bool show) { sShowMarkers = show; } bool Loader::getShowMarkers() { return sShowMarkers; } class LoaderImpl { public: /// @param filename used for warning messages. LoaderImpl(const std::string& filename) : mFilename(filename) { } std::string mFilename; static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target) { if(nif->numRoots() < 1) { nif->warn("Found no root nodes"); return; } const Nif::Record *r = nif->getRoot(0); assert(r != NULL); if(r->recType != Nif::RC_NiSequenceStreamHelper) { nif->warn("First root was not a NiSequenceStreamHelper, but a "+ r->recName+"."); return; } const Nif::NiSequenceStreamHelper *seq = static_cast(r); Nif::ExtraPtr extra = seq->extra; if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) { nif->warn("First extra data was not a NiTextKeyExtraData, but a "+ (extra.empty() ? std::string("nil") : extra->recName)+"."); return; } extractTextKeys(static_cast(extra.getPtr()), target.mTextKeys); extra = extra->extra; Nif::ControllerPtr ctrl = seq->controller; for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) { if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) { nif->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName); continue; } if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); if(key->data.empty()) continue; osg::ref_ptr callback(new NifOsg::KeyframeController(key->data.getPtr())); callback->setFunction(boost::shared_ptr(new NifOsg::ControllerFunction(key))); target.mKeyframeControllers[strdata->string] = callback; } } osg::ref_ptr load(Nif::NIFFilePtr nif, Resource::TextureManager* textureManager) { if (nif->numRoots() < 1) nif->fail("Found no root nodes"); const Nif::Record* r = nif->getRoot(0); const Nif::Node* nifNode = dynamic_cast(r); if (nifNode == NULL) nif->fail("First root was not a node, but a " + r->recName); osg::ref_ptr textkeys (new TextKeyMapHolder); osg::ref_ptr created = handleNode(nifNode, NULL, textureManager, std::vector(), 0, 0, false, &textkeys->mTextKeys); if (nif->getUseSkinning()) { osg::ref_ptr skel = new SceneUtil::Skeleton; skel->addChild(created); created = skel; } created->getOrCreateUserDataContainer()->addUserObject(textkeys); return created; } void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::TextureManager* textureManager, std::vector& boundTextures, int animflags) { const Nif::PropertyList& props = nifNode->props; for (size_t i = 0; i setSource(boost::shared_ptr(new SceneUtil::FrameTimeSource)); toSetup->setFunction(boost::shared_ptr(new ControllerFunction(ctrl))); } void optimize (const Nif::Node* nifNode, osg::Group* node, bool skipMeshes) { // For nodes with an identity transform, remove the redundant Transform node if (node->getDataVariance() == osg::Object::STATIC // For TriShapes, we can only collapse the node, but not completely remove it, // if the link to animated collision shapes is supposed to stay intact. && (nifNode->recType != Nif::RC_NiTriShape || !skipMeshes)) { if (node->getNumParents() && nifNode->trafo.isIdentity()) { osg::Group* parent = node->getParent(0); // can be multiple children in case of ParticleSystems, with the extra ParticleSystemUpdater node for (unsigned int i=0; igetNumChildren(); ++i) { osg::Node* child = node->getChild(i); if (i == node->getNumChildren()-1) // FIXME: some nicer way to determine where our actual Drawable resides... { child->addUpdateCallback(node->getUpdateCallback()); child->setStateSet(node->getStateSet()); child->setName(node->getName()); // make sure to copy the UserDataContainer with the record index, so that connections to an animated collision shape don't break child->setUserDataContainer(node->getUserDataContainer()); } parent->addChild(child); } node->removeChildren(0, node->getNumChildren()); parent->removeChild(node); } } // For NiTriShapes *with* a valid transform, perhaps we could apply the transform to the vertices. // Need to make sure that won't break transparency sorting. Check what the original engine is doing? } osg::ref_ptr handleLodNode(const Nif::NiLODNode* niLodNode) { osg::ref_ptr lod (new osg::LOD); lod->setCenterMode(osg::LOD::USER_DEFINED_CENTER); lod->setCenter(niLodNode->lodCenter); for (unsigned int i=0; ilodLevels.size(); ++i) { const Nif::NiLODNode::LODRange& range = niLodNode->lodLevels[i]; lod->setRange(i, range.minRange, range.maxRange); } lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); return lod; } osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::TextureManager* textureManager, std::vector boundTextures, int animflags, int particleflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL) { osg::ref_ptr node = new osg::MatrixTransform(nifNode->trafo.toMatrix()); // Set a default DataVariance (used as hint by optimization routines). switch (nifNode->recType) { case Nif::RC_NiTriShape: case Nif::RC_NiAutoNormalParticles: case Nif::RC_NiRotatingParticles: // Leaf nodes in the NIF hierarchy, so won't be able to dynamically attach children. // No support for keyframe controllers (just crashes in the original engine). node->setDataVariance(osg::Object::STATIC); break; default: // could have new children attached at any time, or added external keyframe controllers from .kf files node->setDataVariance(osg::Object::DYNAMIC); break; } if (nifNode->recType == Nif::RC_NiBillboardNode) { node->addCullCallback(new BillboardCallback); } else if (!rootNode && nifNode->controller.empty() && nifNode->trafo.isIdentity()) { // The Root node can be created as a Group if no transformation is required. // This takes advantage of the fact root nodes can't have additional controllers // loaded from an external .kf file (original engine just throws "can't find node" errors if you try). node = new osg::Group; node->setDataVariance(osg::Object::STATIC); } node->setName(nifNode->name); if (parentNode) parentNode->addChild(node); if (!rootNode) rootNode = node; // UserData used for a variety of features: // - finding the correct emitter node for a particle system // - establishing connections to the animated collision shapes, which are handled in a separate loader // - finding a random child NiNode in NiBspArrayController // - storing the previous 3x3 rotation and scale values for when a KeyframeController wants to // change only certain elements of the 4x4 transform node->getOrCreateUserDataContainer()->addUserObject( new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation)); for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->extra) { if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys) { const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); extractTextKeys(tk, *textKeys); } else if(e->recType == Nif::RC_NiStringExtraData) { const Nif::NiStringExtraData *sd = static_cast(e.getPtr()); // String markers may contain important information // affecting the entire subtree of this obj if(sd->string == "MRK" && !Loader::getShowMarkers()) { // Marker objects. These meshes are only visible in the editor. skipMeshes = true; } } } if (nifNode->recType == Nif::RC_NiBSAnimationNode) animflags |= nifNode->flags; if (nifNode->recType == Nif::RC_NiBSParticleNode) particleflags |= nifNode->flags; // Hide collision shapes, but don't skip the subgraph // We still need to animate the hidden bones so the physics system can access them if (nifNode->recType == Nif::RC_RootCollisionNode) { skipMeshes = true; // Leave mask for UpdateVisitor enabled node->setNodeMask(0x1); } // We can skip creating meshes for hidden nodes if they don't have a VisController that // might make them visible later if (nifNode->flags & Nif::NiNode::Flag_Hidden) { bool hasVisController = false; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) hasVisController = (ctrl->recType == Nif::RC_NiVisController); if (!hasVisController) skipMeshes = true; // skip child meshes, but still create the child node hierarchy for animating collision shapes // now hide this node, but leave the mask for UpdateVisitor enabled so that KeyframeController works node->setNodeMask(0x1); } osg::ref_ptr composite = new SceneUtil::CompositeStateSetUpdater; applyNodeProperties(nifNode, node, composite, textureManager, boundTextures, animflags); if (nifNode->recType == Nif::RC_NiTriShape && !skipMeshes) { const Nif::NiTriShape* triShape = static_cast(nifNode); if (triShape->skin.empty()) handleTriShape(triShape, node, composite, boundTextures, animflags); else handleSkinnedTriShape(triShape, node, composite, boundTextures, animflags); if (!nifNode->controller.empty()) handleMeshControllers(nifNode, node, composite, boundTextures, animflags); } if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) handleParticleSystem(nifNode, node, composite, animflags, particleflags, rootNode); if (composite->getNumControllers() > 0) node->addUpdateCallback(composite); // Note: NiTriShapes are not allowed to have KeyframeControllers (the vanilla engine just crashes when there is one). // We can take advantage of this constraint for optimizations later. if (!nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC) handleNodeControllers(nifNode, static_cast(node.get()), animflags); // Optimization pass optimize(nifNode, node, skipMeshes); if (nifNode->recType == Nif::RC_NiLODNode) { const Nif::NiLODNode* niLodNode = static_cast(nifNode); osg::ref_ptr lod = handleLodNode(niLodNode); node->addChild(lod); // unsure if LOD should be above or below this node's transform node = lod; } const Nif::NiNode *ninode = dynamic_cast(nifNode); if(ninode) { const Nif::NodeList &children = ninode->children; for(size_t i = 0;i < children.length();++i) { if(!children[i].empty()) { handleNode(children[i].getPtr(), node, textureManager, boundTextures, animflags, particleflags, skipMeshes, textKeys, rootNode); } } } return node; } void handleMeshControllers(const Nif::Node *nifNode, osg::Node* node, SceneUtil::CompositeStateSetUpdater* composite, const std::vector &boundTextures, int animflags) { for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; if (ctrl->recType == Nif::RC_NiUVController) { const Nif::NiUVController *uvctrl = static_cast(ctrl.getPtr()); std::set texUnits; for (unsigned int i=0; i ctrl = new UVController(uvctrl->data.getPtr(), texUnits); setupController(uvctrl, ctrl, animflags); composite->addController(ctrl); } else if (ctrl->recType == Nif::RC_NiVisController) { handleVisController(static_cast(ctrl.getPtr()), node, animflags); } else if(ctrl->recType == Nif::RC_NiGeomMorpherController) {} // handled in handleTriShape else std::cerr << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename << std::endl; } } void handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, int animflags) { for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; if (ctrl->recType == Nif::RC_NiKeyframeController) { const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); if(!key->data.empty()) { osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); setupController(key, callback, animflags); transformNode->addUpdateCallback(callback); } } else if (ctrl->recType == Nif::RC_NiVisController) { handleVisController(static_cast(ctrl.getPtr()), transformNode, animflags); } else std::cerr << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename << std::endl; } } void handleVisController(const Nif::NiVisController* visctrl, osg::Node* node, int animflags) { osg::ref_ptr callback(new VisController(visctrl->data.getPtr())); setupController(visctrl, callback, animflags); node->addUpdateCallback(callback); } void handleMaterialControllers(const Nif::Property *materialProperty, SceneUtil::CompositeStateSetUpdater* composite, int animflags) { for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; if (ctrl->recType == Nif::RC_NiAlphaController) { const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); osg::ref_ptr ctrl(new AlphaController(alphactrl->data.getPtr())); setupController(alphactrl, ctrl, animflags); composite->addController(ctrl); } else if (ctrl->recType == Nif::RC_NiMaterialColorController) { const Nif::NiMaterialColorController* matctrl = static_cast(ctrl.getPtr()); osg::ref_ptr ctrl(new MaterialColorController(matctrl->data.getPtr())); setupController(matctrl, ctrl, animflags); composite->addController(ctrl); } else std::cerr << "Unexpected material controller " << ctrl->recType << " in " << mFilename << std::endl; } } void handleTextureControllers(const Nif::Property *texProperty, SceneUtil::CompositeStateSetUpdater* composite, Resource::TextureManager* textureManager, osg::StateSet *stateset, int animflags) { for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; if (ctrl->recType == Nif::RC_NiFlipController) { const Nif::NiFlipController* flipctrl = static_cast(ctrl.getPtr()); std::vector > textures; for (unsigned int i=0; imSources.length(); ++i) { Nif::NiSourceTexturePtr st = flipctrl->mSources[i]; if (st.empty()) continue; // inherit wrap settings from the target slot osg::Texture2D* inherit = dynamic_cast(stateset->getTextureAttribute(flipctrl->mTexSlot, osg::StateAttribute::TEXTURE)); osg::Texture2D::WrapMode wrapS = osg::Texture2D::CLAMP; osg::Texture2D::WrapMode wrapT = osg::Texture2D::CLAMP; if (inherit) { wrapS = inherit->getWrap(osg::Texture2D::WRAP_S); wrapT = inherit->getWrap(osg::Texture2D::WRAP_T); } std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, textureManager->getVFS()); osg::ref_ptr texture = textureManager->getTexture2D(filename, wrapS, wrapT); textures.push_back(texture); } osg::ref_ptr callback(new FlipController(flipctrl, textures)); setupController(ctrl.getPtr(), callback, animflags); composite->addController(callback); } else std::cerr << "Unexpected texture controller " << ctrl->recName << " in " << mFilename << std::endl; } } void handleParticlePrograms(Nif::ExtraPtr affectors, Nif::ExtraPtr colliders, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf) { osgParticle::ModularProgram* program = new osgParticle::ModularProgram; attachTo->addChild(program); program->setParticleSystem(partsys); program->setReferenceFrame(rf); for (; !affectors.empty(); affectors = affectors->extra) { if (affectors->recType == Nif::RC_NiParticleGrowFade) { const Nif::NiParticleGrowFade *gf = static_cast(affectors.getPtr()); program->addOperator(new GrowFadeAffector(gf->growTime, gf->fadeTime)); } else if (affectors->recType == Nif::RC_NiGravity) { const Nif::NiGravity* gr = static_cast(affectors.getPtr()); program->addOperator(new GravityAffector(gr)); } else if (affectors->recType == Nif::RC_NiParticleColorModifier) { const Nif::NiParticleColorModifier *cl = static_cast(affectors.getPtr()); const Nif::NiColorData *clrdata = cl->data.getPtr(); program->addOperator(new ParticleColorAffector(clrdata)); } else if (affectors->recType == Nif::RC_NiParticleRotation) { // unused } else std::cerr << "Unhandled particle modifier " << affectors->recName << " in " << mFilename << std::endl; } for (; !colliders.empty(); colliders = colliders->extra) { if (colliders->recType == Nif::RC_NiPlanarCollider) { const Nif::NiPlanarCollider* planarcollider = static_cast(colliders.getPtr()); program->addOperator(new PlanarCollider(planarcollider)); } } } // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and colors. void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) { const Nif::NiAutoNormalParticlesData *particledata = NULL; if(nifNode->recType == Nif::RC_NiAutoNormalParticles) particledata = static_cast(nifNode)->data.getPtr(); else if(nifNode->recType == Nif::RC_NiRotatingParticles) particledata = static_cast(nifNode)->data.getPtr(); else return; int i=0; for (std::vector::const_iterator it = partctrl->particles.begin(); iactiveCount && it != partctrl->particles.end(); ++it, ++i) { const Nif::NiParticleSystemController::Particle& particle = *it; ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime)); osgParticle::Particle* created = partsys->createParticle(&particletemplate); created->setLifeTime(std::max(0.f, particle.lifespan)); // Note this position and velocity is not correct for a particle system with absolute reference frame, // which can not be done in this loader since we are not attached to the scene yet. Will be fixed up post-load in the SceneManager. created->setVelocity(particle.velocity); created->setPosition(particledata->vertices->at(particle.vertex)); osg::Vec4f partcolor (1.f,1.f,1.f,1.f); if (particle.vertex < int(particledata->colors->size())) partcolor = particledata->colors->at(particle.vertex); float size = particledata->sizes.at(particle.vertex) * partctrl->size; created->setSizeRange(osgParticle::rangef(size, size)); } osg::BoundingBox box; box.expandBy(osg::BoundingSphere(osg::Vec3(0,0,0), particledata->radius)); partsys->setInitialBound(box); } osg::ref_ptr handleParticleEmitter(const Nif::NiParticleSystemController* partctrl) { std::vector targets; if (partctrl->recType == Nif::RC_NiBSPArrayController) { getAllNiNodes(partctrl->emitter.getPtr(), targets); } osg::ref_ptr emitter = new Emitter(targets); osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter; if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust) counter->setNumberOfParticlesPerSecondToCreate(partctrl->emitRate); else counter->setNumberOfParticlesPerSecondToCreate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2)); emitter->setCounter(counter); ParticleShooter* shooter = new ParticleShooter(partctrl->velocity - partctrl->velocityRandom*0.5f, partctrl->velocity + partctrl->velocityRandom*0.5f, partctrl->horizontalDir, partctrl->horizontalAngle, partctrl->verticalDir, partctrl->verticalAngle, partctrl->lifetime, partctrl->lifetimeRandom); emitter->setShooter(shooter); osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer; placer->setXRange(-partctrl->offsetRandom.x(), partctrl->offsetRandom.x()); placer->setYRange(-partctrl->offsetRandom.y(), partctrl->offsetRandom.y()); placer->setZRange(-partctrl->offsetRandom.z(), partctrl->offsetRandom.z()); emitter->setPlacer(placer); return emitter; } void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, int animflags, int particleflags, osg::Node* rootNode) { osg::ref_ptr partsys (new ParticleSystem); partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); const Nif::NiParticleSystemController* partctrl = NULL; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; if(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController) partctrl = static_cast(ctrl.getPtr()); else std::cerr << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename << std::endl; } if (!partctrl) { std::cerr << "No particle controller found in " << mFilename << std::endl; return; } osgParticle::ParticleProcessor::ReferenceFrame rf = (particleflags & Nif::NiNode::ParticleFlag_LocalSpace) ? osgParticle::ParticleProcessor::RELATIVE_RF : osgParticle::ParticleProcessor::ABSOLUTE_RF; // HACK: ParticleSystem has no setReferenceFrame method if (rf == osgParticle::ParticleProcessor::ABSOLUTE_RF) { partsys->getOrCreateUserDataContainer()->addDescription("worldspace"); } partsys->setParticleScaleReferenceFrame(osgParticle::ParticleSystem::LOCAL_COORDINATES); handleParticleInitialState(nifNode, partsys, partctrl); partsys->setQuota(partctrl->numParticles); partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->size, partctrl->size)); partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(osg::Vec4f(1.f,1.f,1.f,1.f), osg::Vec4f(1.f,1.f,1.f,1.f))); partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f)); partsys->setFreezeOnCull(true); if (!partctrl->emitter.empty()) { osg::ref_ptr emitter = handleParticleEmitter(partctrl); emitter->setParticleSystem(partsys); emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF); // Note: we assume that the Emitter node is placed *before* the Particle node in the scene graph. // This seems to be true for all NIF files in the game that I've checked, suggesting that NIFs work similar to OSG with regards to update order. // If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster. FindGroupByRecIndex find (partctrl->emitter->recIndex); rootNode->accept(find); if (!find.mFound) { std::cerr << "can't find emitter node, wrong node order? in " << mFilename << std::endl; return; } osg::Group* emitterNode = find.mFound; // Emitter attached to the emitter node. Note one side effect of the emitter using the CullVisitor is that hiding its node // actually causes the emitter to stop firing. Convenient, because MW behaves this way too! emitterNode->addChild(emitter); osg::ref_ptr callback(new ParticleSystemController(partctrl)); setupController(partctrl, callback, animflags); emitter->setUpdateCallback(callback); } // affectors must be attached *after* the emitter in the scene graph for correct update order // attach to same node as the ParticleSystem, we need osgParticle Operators to get the correct // localToWorldMatrix for transforming to particle space handleParticlePrograms(partctrl->affectors, partctrl->colliders, parentNode, partsys.get(), rf); std::vector drawableProps; collectDrawableProperties(nifNode, drawableProps); applyDrawableProperties(parentNode, drawableProps, composite, true, animflags); // Particles don't have normals, so can't be diffuse lit. osg::Material* mat = static_cast(parentNode->getStateSet()->getAttribute(osg::StateAttribute::MATERIAL)); if (mat) { // NB ignoring diffuse.a() mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1)); mat->setColorMode(osg::Material::AMBIENT); } // particle system updater (after the emitters and affectors in the scene graph) // I think for correct culling needs to be *before* the ParticleSystem, though osg examples do it the other way osg::ref_ptr updater = new osgParticle::ParticleSystemUpdater; updater->addParticleSystem(partsys); parentNode->addChild(updater); #if OSG_VERSION_LESS_THAN(3,3,3) osg::ref_ptr geode (new osg::Geode); geode->addDrawable(partsys); osg::Node* toAttach = geode.get(); #else osg::Node* toAttach = partsys.get(); #endif if (rf == osgParticle::ParticleProcessor::RELATIVE_RF) parentNode->addChild(toAttach); else { osg::MatrixTransform* trans = new osg::MatrixTransform; trans->setUpdateCallback(new InverseWorldMatrix); trans->addChild(toAttach); parentNode->addChild(trans); } } void triShapeToGeometry(const Nif::NiTriShape *triShape, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { const Nif::NiTriShapeData* data = triShape->data.getPtr(); geometry->setVertexArray(data->vertices); if (!data->normals->empty()) geometry->setNormalArray(data->normals); int textureStage = 0; for (std::vector::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it,++textureStage) { int uvSet = *it; if (uvSet >= (int)data->uvlist.size()) { std::cerr << "Warning: out of bounds UV set " << uvSet << " on TriShape \"" << triShape->name << "\" in " << mFilename << std::endl; if (data->uvlist.size()) geometry->setTexCoordArray(textureStage, data->uvlist[0]); continue; } geometry->setTexCoordArray(textureStage, data->uvlist[uvSet]); } if (!data->colors->empty()) geometry->setColorArray(data->colors); if (!data->triangles->empty()) geometry->addPrimitiveSet(data->triangles); // osg::Material properties are handled here for two reasons: // - if there are no vertex colors, we need to disable colorMode. // - there are 3 "overlapping" nif properties that all affect the osg::Material, handling them // above the actual renderable would be tedious. std::vector drawableProps; collectDrawableProperties(triShape, drawableProps); applyDrawableProperties(parentNode, drawableProps, composite, !data->colors->empty(), animflags); } void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { osg::ref_ptr geometry; for (Nif::ControllerPtr ctrl = triShape->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; if(ctrl->recType == Nif::RC_NiGeomMorpherController) { geometry = handleMorphGeometry(static_cast(ctrl.getPtr())); osg::ref_ptr morphctrl = new GeomMorpherController( static_cast(ctrl.getPtr())->data.getPtr()); setupController(ctrl.getPtr(), morphctrl, animflags); geometry->setUpdateCallback(morphctrl); break; } } if (!geometry.get()) geometry = new osg::Geometry; triShapeToGeometry(triShape, geometry, parentNode, composite, boundTextures, animflags); #if OSG_VERSION_LESS_THAN(3,3,3) osg::ref_ptr geode (new osg::Geode); geode->addDrawable(geometry); #endif if (geometry->getDataVariance() == osg::Object::DYNAMIC) { // Add a copy, we will alternate between the two copies every other frame using the FrameSwitch // This is so we can set the DataVariance as STATIC, giving a huge performance boost geometry->setDataVariance(osg::Object::STATIC); osg::ref_ptr frameswitch = new FrameSwitch; #if OSG_VERSION_LESS_THAN(3,3,3) osg::ref_ptr geode2 = static_cast(osg::clone(geode.get(), osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES)); frameswitch->addChild(geode); frameswitch->addChild(geode2); #else osg::ref_ptr geom2 = static_cast(osg::clone(geometry.get(), osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES)); frameswitch->addChild(geometry); frameswitch->addChild(geom2); #endif parentNode->addChild(frameswitch); } else #if OSG_VERSION_LESS_THAN(3,3,3) parentNode->addChild(geode); #else parentNode->addChild(geometry); #endif } osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher) { osg::ref_ptr morphGeom = new osgAnimation::MorphGeometry; morphGeom->setMethod(osgAnimation::MorphGeometry::RELATIVE); // No normals available in the MorphData morphGeom->setMorphNormals(false); morphGeom->setUpdateCallback(NULL); morphGeom->setCullCallback(new UpdateMorphGeometry); const std::vector& morphs = morpher->data.getPtr()->mMorphs; if (!morphs.size()) return morphGeom; // Note we are not interested in morph 0, which just contains the original vertices for (unsigned int i = 1; i < morphs.size(); ++i) { osg::ref_ptr morphTarget = new osg::Geometry; morphTarget->setVertexArray(morphs[i].mVertices); morphGeom->addMorphTarget(morphTarget, 0.f); } // build the bounding box containing all possible morph combinations std::vector vertBounds(morphs[0].mVertices->size()); // Since we don't know what combinations of morphs are being applied we need to keep track of a bounding box for each vertex. // The minimum/maximum of the box is the minimum/maximum offset the vertex can have from its starting position. // Start with zero offsets which will happen when no morphs are applied. for (unsigned int i=0; isize() && vertBounds.size(); ++j) { osg::BoundingBox& bounds = vertBounds[j]; bounds.expandBy(bounds._max + (*morphs[i].mVertices)[j]); bounds.expandBy(bounds._min + (*morphs[i].mVertices)[j]); } } osg::BoundingBox box; for (unsigned int i=0; isetComputeBoundingBoxCallback(new StaticBoundingBoxCallback(box)); return morphGeom; } void handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { osg::ref_ptr geometry (new osg::Geometry); triShapeToGeometry(triShape, geometry, parentNode, composite, boundTextures, animflags); osg::ref_ptr rig(new SceneUtil::RigGeometry); rig->setSourceGeometry(geometry); const Nif::NiSkinInstance *skin = triShape->skin.getPtr(); // Assign bone weights osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) { std::string boneName = bones[i].getPtr()->name; SceneUtil::RigGeometry::BoneInfluence influence; const std::vector &weights = data->bones[i].weights; //influence.mWeights.reserve(weights.size()); for(size_t j = 0;j < weights.size();j++) { std::pair indexWeight = std::make_pair(weights[j].vertex, weights[j].weight); influence.mWeights.insert(indexWeight); } influence.mInvBindMatrix = data->bones[i].trafo.toMatrix(); influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); map->mMap.insert(std::make_pair(boneName, influence)); } rig->setInfluenceMap(map); // Add a copy, we will alternate between the two copies every other frame using the FrameSwitch // This is so we can set the DataVariance as STATIC, giving a huge performance boost rig->setDataVariance(osg::Object::STATIC); osg::ref_ptr frameswitch = new FrameSwitch; #if OSG_VERSION_LESS_THAN(3,3,3) osg::ref_ptr geode (new osg::Geode); geode->addDrawable(rig); osg::Geode* geode2 = static_cast(osg::clone(geode.get(), osg::CopyOp::DEEP_COPY_NODES| osg::CopyOp::DEEP_COPY_DRAWABLES)); frameswitch->addChild(geode); frameswitch->addChild(geode2); #else SceneUtil::RigGeometry* rig2 = static_cast(osg::clone(rig.get(), osg::CopyOp::DEEP_COPY_NODES| osg::CopyOp::DEEP_COPY_DRAWABLES)); frameswitch->addChild(rig); frameswitch->addChild(rig2); #endif parentNode->addChild(frameswitch); } osg::BlendFunc::BlendFuncMode getBlendMode(int mode) { switch(mode) { case 0: return osg::BlendFunc::ONE; case 1: return osg::BlendFunc::ZERO; case 2: return osg::BlendFunc::SRC_COLOR; case 3: return osg::BlendFunc::ONE_MINUS_SRC_COLOR; case 4: return osg::BlendFunc::DST_COLOR; case 5: return osg::BlendFunc::ONE_MINUS_DST_COLOR; case 6: return osg::BlendFunc::SRC_ALPHA; case 7: return osg::BlendFunc::ONE_MINUS_SRC_ALPHA; case 8: return osg::BlendFunc::DST_ALPHA; case 9: return osg::BlendFunc::ONE_MINUS_DST_ALPHA; case 10: return osg::BlendFunc::SRC_ALPHA_SATURATE; default: std::cerr<< "Unexpected blend mode: "<< mode << " in " << mFilename << std::endl; return osg::BlendFunc::SRC_ALPHA; } } osg::AlphaFunc::ComparisonFunction getTestMode(int mode) { switch (mode) { case 0: return osg::AlphaFunc::ALWAYS; case 1: return osg::AlphaFunc::LESS; case 2: return osg::AlphaFunc::EQUAL; case 3: return osg::AlphaFunc::LEQUAL; case 4: return osg::AlphaFunc::GREATER; case 5: return osg::AlphaFunc::NOTEQUAL; case 6: return osg::AlphaFunc::GEQUAL; case 7: return osg::AlphaFunc::NEVER; default: std::cerr << "Unexpected blend mode: " << mode << " in " << mFilename << std::endl; return osg::AlphaFunc::LEQUAL; } } osg::Stencil::Function getStencilFunction(int func) { switch (func) { case 0: return osg::Stencil::NEVER; case 1: return osg::Stencil::LESS; case 2: return osg::Stencil::EQUAL; case 3: return osg::Stencil::LEQUAL; case 4: return osg::Stencil::GREATER; case 5: return osg::Stencil::NOTEQUAL; case 6: return osg::Stencil::GEQUAL; case 7: return osg::Stencil::NEVER; // NifSkope says this is GL_ALWAYS, but in MW it's GL_NEVER default: std::cerr << "Unexpected stencil function: " << func << " in " << mFilename << std::endl; return osg::Stencil::NEVER; } } osg::Stencil::Operation getStencilOperation(int op) { switch (op) { case 0: return osg::Stencil::KEEP; case 1: return osg::Stencil::ZERO; case 2: return osg::Stencil::REPLACE; case 3: return osg::Stencil::INCR; case 4: return osg::Stencil::DECR; case 5: return osg::Stencil::INVERT; default: std::cerr << "Unexpected stencil operation: " << op << " in " << mFilename << std::endl; return osg::Stencil::KEEP; } } void handleProperty(const Nif::Property *property, osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::TextureManager* textureManager, std::vector& boundTextures, int animflags) { switch (property->recType) { case Nif::RC_NiStencilProperty: { const Nif::NiStencilProperty* stencilprop = static_cast(property); osg::FrontFace* frontFace = new osg::FrontFace; switch (stencilprop->data.drawMode) { case 2: frontFace->setMode(osg::FrontFace::CLOCKWISE); break; case 0: case 1: default: frontFace->setMode(osg::FrontFace::COUNTER_CLOCKWISE); break; } osg::StateSet* stateset = node->getOrCreateStateSet(); stateset->setAttribute(frontFace, osg::StateAttribute::ON); stateset->setMode(GL_CULL_FACE, stencilprop->data.drawMode == 3 ? osg::StateAttribute::OFF : osg::StateAttribute::ON); if (stencilprop->data.enabled != 0) { osg::Stencil* stencil = new osg::Stencil; stencil->setFunction(getStencilFunction(stencilprop->data.compareFunc), stencilprop->data.stencilRef, stencilprop->data.stencilMask); stencil->setStencilFailOperation(getStencilOperation(stencilprop->data.failAction)); stencil->setStencilPassAndDepthFailOperation(getStencilOperation(stencilprop->data.zFailAction)); stencil->setStencilPassAndDepthPassOperation(getStencilOperation(stencilprop->data.zPassAction)); stateset->setAttributeAndModes(stencil, osg::StateAttribute::ON); } break; } case Nif::RC_NiWireframeProperty: { const Nif::NiWireframeProperty* wireprop = static_cast(property); osg::PolygonMode* mode = new osg::PolygonMode; mode->setMode(osg::PolygonMode::FRONT_AND_BACK, wireprop->flags == 0 ? osg::PolygonMode::FILL : osg::PolygonMode::LINE); node->getOrCreateStateSet()->setAttributeAndModes(mode, osg::StateAttribute::ON); break; } case Nif::RC_NiZBufferProperty: { const Nif::NiZBufferProperty* zprop = static_cast(property); // VER_MW doesn't support a DepthFunction according to NifSkope osg::Depth* depth = new osg::Depth; depth->setWriteMask((zprop->flags>>1)&1); node->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); break; } // OSG groups the material properties that NIFs have separate, so we have to parse them all again when one changed case Nif::RC_NiMaterialProperty: case Nif::RC_NiVertexColorProperty: case Nif::RC_NiSpecularProperty: { // Handled on drawable level so we know whether vertex colors are available break; } case Nif::RC_NiAlphaProperty: { // Handled on drawable level to prevent RenderBin nesting issues break; } case Nif::RC_NiTexturingProperty: { const Nif::NiTexturingProperty* texprop = static_cast(property); osg::StateSet* stateset = node->getOrCreateStateSet(); if (boundTextures.size()) { // overriding a parent NiTexturingProperty, so remove what was previously bound for (unsigned int i=0; isetTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); boundTextures.clear(); } for (int i=0; itextures[i].inUse) { switch(i) { //These are handled later on case Nif::NiTexturingProperty::BaseTexture: case Nif::NiTexturingProperty::GlowTexture: case Nif::NiTexturingProperty::DarkTexture: case Nif::NiTexturingProperty::DetailTexture: break; case Nif::NiTexturingProperty::GlossTexture: { std::cerr << "NiTexturingProperty::GlossTexture in " << mFilename << " not currently used." << std::endl; continue; } case Nif::NiTexturingProperty::BumpTexture: { std::cerr << "NiTexturingProperty::BumpTexture in " << mFilename << " not currently used." << std::endl; continue; } case Nif::NiTexturingProperty::DecalTexture: { std::cerr << "NiTexturingProperty::DecalTexture in " << mFilename << " not currently used." << std::endl; continue; } default: { std::cerr << "Warning: unhandled texture stage " << i << " in " << mFilename << std::endl; continue; } } const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i]; if(tex.texture.empty()) { std::cerr << "Warning: texture layer " << i << " is in use but empty in " << mFilename << std::endl; continue; } const Nif::NiSourceTexture *st = tex.texture.getPtr(); if (!st->external) { std::cerr << "Warning: unhandled internal texture in " << mFilename << std::endl; continue; } std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, textureManager->getVFS()); unsigned int clamp = static_cast(tex.clamp); int wrapT = (clamp) & 0x1; int wrapS = (clamp >> 1) & 0x1; osg::ref_ptr texture2d = textureManager->getTexture2D(filename, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); int texUnit = boundTextures.size(); stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); if (i == Nif::NiTexturingProperty::GlowTexture) { osg::TexEnvCombine* texEnv = new osg::TexEnvCombine; texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON); } else if (i == Nif::NiTexturingProperty::DarkTexture) { osg::TexEnv* texEnv = new osg::TexEnv; texEnv->setMode(osg::TexEnv::MODULATE); stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON); } else if (i == Nif::NiTexturingProperty::DetailTexture) { osg::TexEnvCombine* texEnv = new osg::TexEnvCombine; texEnv->setScale_RGB(2.f); texEnv->setCombine_Alpha(GL_MODULATE); texEnv->setOperand0_Alpha(GL_SRC_ALPHA); texEnv->setOperand1_Alpha(GL_SRC_ALPHA); texEnv->setSource0_Alpha(GL_PREVIOUS_ARB); texEnv->setSource1_Alpha(GL_TEXTURE); texEnv->setCombine_RGB(GL_MODULATE); texEnv->setOperand0_RGB(GL_SRC_COLOR); texEnv->setOperand1_RGB(GL_SRC_COLOR); texEnv->setSource0_RGB(GL_PREVIOUS_ARB); texEnv->setSource1_RGB(GL_TEXTURE); stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON); } boundTextures.push_back(tex.uvSet); } handleTextureControllers(texprop, composite, textureManager, stateset, animflags); } break; } // unused by mw case Nif::RC_NiShadeProperty: case Nif::RC_NiDitherProperty: case Nif::RC_NiFogProperty: { break; } default: std::cerr << "Unhandled " << property->recName << " in " << mFilename << std::endl; break; } } void applyDrawableProperties(osg::Node* node, const std::vector& properties, SceneUtil::CompositeStateSetUpdater* composite, bool hasVertexColors, int animflags) { osg::StateSet* stateset = node->getOrCreateStateSet(); int specFlags = 0; // Specular is disabled by default, even if there's a specular color in the NiMaterialProperty osg::ref_ptr mat (new osg::Material); mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF); // NIF material defaults don't match OpenGL defaults mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); for (std::vector::const_reverse_iterator it = properties.rbegin(); it != properties.rend(); ++it) { const Nif::Property* property = *it; switch (property->recType) { case Nif::RC_NiSpecularProperty: { specFlags = property->flags; break; } case Nif::RC_NiMaterialProperty: { const Nif::NiMaterialProperty* matprop = static_cast(property); mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.diffuse, matprop->data.alpha)); mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.ambient, 1.f)); mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.emissive, 1.f)); mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.specular, 1.f)); mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness); if (!matprop->controller.empty()) handleMaterialControllers(matprop, composite, animflags); break; } case Nif::RC_NiVertexColorProperty: { const Nif::NiVertexColorProperty* vertprop = static_cast(property); if (!hasVertexColors) break; switch (vertprop->flags) { case 0: mat->setColorMode(osg::Material::OFF); break; case 1: mat->setColorMode(osg::Material::EMISSION); break; case 2: mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); break; } break; } case Nif::RC_NiAlphaProperty: { const Nif::NiAlphaProperty* alphaprop = static_cast(property); if (alphaprop->flags&1) { stateset->setAttributeAndModes(new osg::BlendFunc(getBlendMode((alphaprop->flags>>1)&0xf), getBlendMode((alphaprop->flags>>5)&0xf)), osg::StateAttribute::ON); bool noSort = (alphaprop->flags>>13)&1; if (!noSort) stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); else stateset->setRenderBinToInherit(); } else { stateset->removeAttribute(osg::StateAttribute::BLENDFUNC); stateset->removeMode(GL_BLEND); stateset->setRenderBinToInherit(); } if((alphaprop->flags>>9)&1) { stateset->setAttributeAndModes(new osg::AlphaFunc(getTestMode((alphaprop->flags>>10)&0x7), alphaprop->data.threshold/255.f), osg::StateAttribute::ON); } else { stateset->removeAttribute(osg::StateAttribute::ALPHAFUNC); stateset->removeMode(GL_ALPHA_TEST); } break; } } } if (specFlags == 0) mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f,0.f,0.f,0.f)); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); } }; osg::ref_ptr Loader::load(Nif::NIFFilePtr file, Resource::TextureManager* textureManager) { LoaderImpl impl(file->getFilename()); return impl.load(file, textureManager); } void Loader::loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target) { LoaderImpl impl(kf->getFilename()); impl.loadKf(kf, target); } } openmw-openmw-0.38.0/components/nifosg/nifloader.hpp000066400000000000000000000043621264522266000225520ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_NIFOSG_LOADER #define OPENMW_COMPONENTS_NIFOSG_LOADER #include #include #include #include "controller.hpp" namespace osg { class Node; } namespace Resource { class TextureManager; } namespace NifOsg { typedef std::multimap TextKeyMap; struct TextKeyMapHolder : public osg::Object { public: TextKeyMapHolder() {} TextKeyMapHolder(const TextKeyMapHolder& copy, const osg::CopyOp& copyop) : osg::Object(copy, copyop) , mTextKeys(copy.mTextKeys) {} TextKeyMap mTextKeys; META_Object(NifOsg, TextKeyMapHolder) }; class KeyframeHolder : public osg::Object { public: KeyframeHolder() {} KeyframeHolder(const KeyframeHolder& copy, const osg::CopyOp& copyop) : mTextKeys(copy.mTextKeys) , mKeyframeControllers(copy.mKeyframeControllers) { } TextKeyMap mTextKeys; META_Object(OpenMW, KeyframeHolder) typedef std::map > KeyframeControllerMap; KeyframeControllerMap mKeyframeControllers; }; /// The main class responsible for loading NIF files into an OSG-Scenegraph. /// @par This scene graph is self-contained and can be cloned using osg::clone if desired. Particle emitters /// and programs hold a pointer to their ParticleSystem, which would need to be manually updated when cloning. class Loader { public: /// Create a scene graph for the given NIF. Auto-detects when skinning is used and wraps the graph in a Skeleton if so. static osg::ref_ptr load(Nif::NIFFilePtr file, Resource::TextureManager* textureManager); /// Load keyframe controllers from the given kf file. static void loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target); /// Set whether or not nodes marked as "MRK" should be shown. /// These should be hidden ingame, but visible in the editor. /// Default: false. static void setShowMarkers(bool show); static bool getShowMarkers(); private: static bool sShowMarkers; }; } #endif openmw-openmw-0.38.0/components/nifosg/particle.cpp000066400000000000000000000255101264522266000224030ustar00rootroot00000000000000#include "particle.hpp" #include #include #include #include "userdata.hpp" namespace NifOsg { ParticleSystem::ParticleSystem() : osgParticle::ParticleSystem() , mQuota(std::numeric_limits::max()) { } ParticleSystem::ParticleSystem(const ParticleSystem ©, const osg::CopyOp ©op) : osgParticle::ParticleSystem(copy, copyop) , mQuota(copy.mQuota) { // For some reason the osgParticle constructor doesn't copy the particles for (int i=0;igetVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) { osg::NodePath path = nv->getNodePath(); path.pop_back(); osg::MatrixTransform* trans = static_cast(node); osg::Matrix mat = osg::computeLocalToWorld( path ); mat.orthoNormalize(mat); // don't undo the scale mat.invert(mat); trans->setMatrix(mat); } traverse(node,nv); } ParticleShooter::ParticleShooter(float minSpeed, float maxSpeed, float horizontalDir, float horizontalAngle, float verticalDir, float verticalAngle, float lifetime, float lifetimeRandom) : mMinSpeed(minSpeed), mMaxSpeed(maxSpeed), mHorizontalDir(horizontalDir) , mHorizontalAngle(horizontalAngle), mVerticalDir(verticalDir), mVerticalAngle(verticalAngle) , mLifetime(lifetime), mLifetimeRandom(lifetimeRandom) { } ParticleShooter::ParticleShooter() : mMinSpeed(0.f), mMaxSpeed(0.f), mHorizontalDir(0.f) , mHorizontalAngle(0.f), mVerticalDir(0.f), mVerticalAngle(0.f) , mLifetime(0.f), mLifetimeRandom(0.f) { } ParticleShooter::ParticleShooter(const ParticleShooter ©, const osg::CopyOp ©op) : osgParticle::Shooter(copy, copyop) { *this = copy; } void ParticleShooter::shoot(osgParticle::Particle *particle) const { float hdir = mHorizontalDir + mHorizontalAngle * (2.f * (std::rand() / static_cast(RAND_MAX)) - 1.f); float vdir = mVerticalDir + mVerticalAngle * (2.f * (std::rand() / static_cast(RAND_MAX)) - 1.f); osg::Vec3f dir = (osg::Quat(vdir, osg::Vec3f(0,1,0)) * osg::Quat(hdir, osg::Vec3f(0,0,1))) * osg::Vec3f(0,0,1); float vel = mMinSpeed + (mMaxSpeed - mMinSpeed) * std::rand() / static_cast(RAND_MAX); particle->setVelocity(dir * vel); // Not supposed to set this here, but there doesn't seem to be a better way of doing it particle->setLifeTime(mLifetime + mLifetimeRandom * std::rand() / static_cast(RAND_MAX)); } GrowFadeAffector::GrowFadeAffector(float growTime, float fadeTime) : mGrowTime(growTime) , mFadeTime(fadeTime) , mCachedDefaultSize(0.f) { } GrowFadeAffector::GrowFadeAffector() : mGrowTime(0.f) , mFadeTime(0.f) , mCachedDefaultSize(0.f) { } GrowFadeAffector::GrowFadeAffector(const GrowFadeAffector& copy, const osg::CopyOp& copyop) : osgParticle::Operator(copy, copyop) { *this = copy; } void GrowFadeAffector::beginOperate(osgParticle::Program *program) { mCachedDefaultSize = program->getParticleSystem()->getDefaultParticleTemplate().getSizeRange().minimum; } void GrowFadeAffector::operate(osgParticle::Particle* particle, double /* dt */) { float size = mCachedDefaultSize; if (particle->getAge() < mGrowTime && mGrowTime != 0.f) size *= particle->getAge() / mGrowTime; if (particle->getLifeTime() - particle->getAge() < mFadeTime && mFadeTime != 0.f) size *= (particle->getLifeTime() - particle->getAge()) / mFadeTime; particle->setSizeRange(osgParticle::rangef(size, size)); } ParticleColorAffector::ParticleColorAffector(const Nif::NiColorData *clrdata) : mData(clrdata->mKeyMap, osg::Vec4f(1,1,1,1)) { } ParticleColorAffector::ParticleColorAffector() { } ParticleColorAffector::ParticleColorAffector(const ParticleColorAffector ©, const osg::CopyOp ©op) : osgParticle::Operator(copy, copyop) { *this = copy; } void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */) { float time = static_cast(particle->getAge()/particle->getLifeTime()); osg::Vec4f color = mData.interpKey(time); particle->setColorRange(osgParticle::rangev4(color, color)); } GravityAffector::GravityAffector(const Nif::NiGravity *gravity) : mForce(gravity->mForce) , mType(static_cast(gravity->mType)) , mPosition(gravity->mPosition) , mDirection(gravity->mDirection) , mDecay(gravity->mDecay) { } GravityAffector::GravityAffector() : mForce(0), mType(Type_Wind), mDecay(0.f) { } GravityAffector::GravityAffector(const GravityAffector ©, const osg::CopyOp ©op) : osgParticle::Operator(copy, copyop) { *this = copy; } void GravityAffector::beginOperate(osgParticle::Program* program) { bool absolute = (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF); if (mType == Type_Point || mDecay != 0.f) // we don't need the position for Wind gravity, except if decay is being applied mCachedWorldPosition = absolute ? program->transformLocalToWorld(mPosition) : mPosition; mCachedWorldDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection; mCachedWorldDirection.normalize(); } void GravityAffector::operate(osgParticle::Particle *particle, double dt) { const float magic = 1.6f; switch (mType) { case Type_Wind: { float decayFactor = 1.f; if (mDecay != 0.f) { osg::Plane gravityPlane(mCachedWorldDirection, mCachedWorldPosition); float distance = std::abs(gravityPlane.distance(particle->getPosition())); decayFactor = std::exp(-1.f * mDecay * distance); } particle->addVelocity(mCachedWorldDirection * mForce * dt * decayFactor * magic); break; } case Type_Point: { osg::Vec3f diff = mCachedWorldPosition - particle->getPosition(); float decayFactor = 1.f; if (mDecay != 0.f) decayFactor = std::exp(-1.f * mDecay * diff.length()); diff.normalize(); particle->addVelocity(diff * mForce * dt * decayFactor * magic); break; } } } Emitter::Emitter() : osgParticle::Emitter() { } Emitter::Emitter(const Emitter ©, const osg::CopyOp ©op) : osgParticle::Emitter(copy, copyop) , mTargets(copy.mTargets) , mPlacer(copy.mPlacer) , mShooter(copy.mShooter) // need a deep copy because the remainder is stored in the object , mCounter(osg::clone(copy.mCounter.get(), osg::CopyOp::DEEP_COPY_ALL)) { } Emitter::Emitter(const std::vector &targets) : mTargets(targets) { } void Emitter::setShooter(osgParticle::Shooter *shooter) { mShooter = shooter; } void Emitter::setPlacer(osgParticle::Placer *placer) { mPlacer = placer; } void Emitter::setCounter(osgParticle::Counter *counter) { mCounter = counter; } void Emitter::emitParticles(double dt) { int n = mCounter->numParticlesToCreate(dt); if (n == 0) return; osg::Matrix worldToPs; // maybe this could be optimized by halting at the lowest common ancestor of the particle and emitter nodes osg::MatrixList worldMats = getParticleSystem()->getWorldMatrices(); if (!worldMats.empty()) { const osg::Matrix psToWorld = worldMats[0]; worldToPs = osg::Matrix::inverse(psToWorld); } const osg::Matrix& ltw = getLocalToWorldMatrix(); osg::Matrix emitterToPs = ltw * worldToPs; if (!mTargets.empty()) { int randomRecIndex = mTargets[(std::rand() / (static_cast(RAND_MAX)+1.0)) * mTargets.size()]; // we could use a map here for faster lookup FindGroupByRecIndex visitor(randomRecIndex); getParent(0)->accept(visitor); if (!visitor.mFound) { std::cerr << "Emitter: Can't find emitter node" << randomRecIndex << std::endl; return; } osg::NodePath path = visitor.mFoundPath; path.erase(path.begin()); emitterToPs = osg::computeLocalToWorld(path) * emitterToPs; } emitterToPs.orthoNormalize(emitterToPs); for (int i=0; icreateParticle(0); if (P) { mPlacer->place(P); mShooter->shoot(P); P->transformPositionVelocity(emitterToPs); } } } FindGroupByRecIndex::FindGroupByRecIndex(int recIndex) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mFound(NULL) , mRecIndex(recIndex) { } void FindGroupByRecIndex::apply(osg::Node &searchNode) { if (searchNode.getUserDataContainer() && searchNode.getUserDataContainer()->getNumUserObjects()) { NodeUserData* holder = dynamic_cast(searchNode.getUserDataContainer()->getUserObject(0)); if (holder && holder->mIndex == mRecIndex) { osg::Group* group = searchNode.asGroup(); if (!group) group = searchNode.getParent(0); mFound = group; mFoundPath = getNodePath(); return; } } traverse(searchNode); } PlanarCollider::PlanarCollider(const Nif::NiPlanarCollider *collider) : mBounceFactor(collider->mBounceFactor) , mPlane(-collider->mPlaneNormal, collider->mPlaneDistance) { } PlanarCollider::PlanarCollider() : mBounceFactor(0.f) { } PlanarCollider::PlanarCollider(const PlanarCollider ©, const osg::CopyOp ©op) : osgParticle::Operator(copy, copyop) , mBounceFactor(copy.mBounceFactor) , mPlane(copy.mPlane) , mPlaneInParticleSpace(copy.mPlaneInParticleSpace) { } void PlanarCollider::beginOperate(osgParticle::Program *program) { mPlaneInParticleSpace = mPlane; if (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF) mPlaneInParticleSpace.transform(program->getLocalToWorldMatrix()); } void PlanarCollider::operate(osgParticle::Particle *particle, double dt) { float dotproduct = particle->getVelocity() * mPlaneInParticleSpace.getNormal(); if (dotproduct > 0) { osg::BoundingSphere bs(particle->getPosition(), 0.f); if (mPlaneInParticleSpace.intersect(bs) == 1) { osg::Vec3 reflectedVelocity = particle->getVelocity() - mPlaneInParticleSpace.getNormal() * (2 * dotproduct); reflectedVelocity *= mBounceFactor; particle->setVelocity(reflectedVelocity); } } } } openmw-openmw-0.38.0/components/nifosg/particle.hpp000066400000000000000000000145071264522266000224140ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_NIFOSG_PARTICLE_H #define OPENMW_COMPONENTS_NIFOSG_PARTICLE_H #include #include #include #include #include #include #include #include #include "controller.hpp" // ValueInterpolator namespace Nif { class NiGravity; class NiPlanarCollider; } namespace NifOsg { // Subclass ParticleSystem to support a limit on the number of active particles. class ParticleSystem : public osgParticle::ParticleSystem { public: ParticleSystem(); ParticleSystem(const ParticleSystem& copy, const osg::CopyOp& copyop); META_Object(NifOsg, ParticleSystem) virtual osgParticle::Particle* createParticle(const osgParticle::Particle *ptemplate); void setQuota(int quota); private: int mQuota; }; // HACK: Particle doesn't allow setting the initial age, but we need this for loading the particle system state class ParticleAgeSetter : public osgParticle::Particle { public: ParticleAgeSetter(float age) : Particle() { _t0 = age; } }; // Node callback used to set the inverse of the parent's world matrix on the MatrixTransform // that the callback is attached to. Used for certain particle systems, // so that the particles do not move with the node they are attached to. class InverseWorldMatrix : public osg::NodeCallback { public: InverseWorldMatrix() { } InverseWorldMatrix(const InverseWorldMatrix& copy, const osg::CopyOp& op) : osg::Object(), osg::NodeCallback() { } META_Object(NifOsg, InverseWorldMatrix) void operator()(osg::Node* node, osg::NodeVisitor* nv); }; class ParticleShooter : public osgParticle::Shooter { public: ParticleShooter(float minSpeed, float maxSpeed, float horizontalDir, float horizontalAngle, float verticalDir, float verticalAngle, float lifetime, float lifetimeRandom); ParticleShooter(); ParticleShooter(const ParticleShooter& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); META_Object(NifOsg, ParticleShooter) virtual void shoot(osgParticle::Particle* particle) const; private: float mMinSpeed; float mMaxSpeed; float mHorizontalDir; float mHorizontalAngle; float mVerticalDir; float mVerticalAngle; float mLifetime; float mLifetimeRandom; }; class PlanarCollider : public osgParticle::Operator { public: PlanarCollider(const Nif::NiPlanarCollider* collider); PlanarCollider(); PlanarCollider(const PlanarCollider& copy, const osg::CopyOp& copyop); META_Object(NifOsg, PlanarCollider) virtual void beginOperate(osgParticle::Program* program); virtual void operate(osgParticle::Particle* particle, double dt); private: float mBounceFactor; osg::Plane mPlane; osg::Plane mPlaneInParticleSpace; }; class GrowFadeAffector : public osgParticle::Operator { public: GrowFadeAffector(float growTime, float fadeTime); GrowFadeAffector(); GrowFadeAffector(const GrowFadeAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); META_Object(NifOsg, GrowFadeAffector) virtual void beginOperate(osgParticle::Program* program); virtual void operate(osgParticle::Particle* particle, double dt); private: float mGrowTime; float mFadeTime; float mCachedDefaultSize; }; typedef ValueInterpolator Vec4Interpolator; class ParticleColorAffector : public osgParticle::Operator { public: ParticleColorAffector(const Nif::NiColorData* clrdata); ParticleColorAffector(); ParticleColorAffector(const ParticleColorAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); META_Object(NifOsg, ParticleColorAffector) virtual void operate(osgParticle::Particle* particle, double dt); private: Vec4Interpolator mData; }; class GravityAffector : public osgParticle::Operator { public: GravityAffector(const Nif::NiGravity* gravity); GravityAffector(); GravityAffector(const GravityAffector& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); META_Object(NifOsg, GravityAffector) virtual void operate(osgParticle::Particle* particle, double dt); virtual void beginOperate(osgParticle::Program *); private: float mForce; enum ForceType { Type_Wind, Type_Point }; ForceType mType; osg::Vec3f mPosition; osg::Vec3f mDirection; float mDecay; osg::Vec3f mCachedWorldPosition; osg::Vec3f mCachedWorldDirection; }; // NodeVisitor to find a Group node with the given record index, stored in the node's user data container. // Alternatively, returns the node's parent Group if that node is not a Group (i.e. a leaf node). class FindGroupByRecIndex : public osg::NodeVisitor { public: FindGroupByRecIndex(int recIndex); virtual void apply(osg::Node &searchNode); osg::Group* mFound; osg::NodePath mFoundPath; private: int mRecIndex; }; // Subclass emitter to support randomly choosing one of the child node's transforms for the emit position of new particles. class Emitter : public osgParticle::Emitter { public: Emitter(const std::vector& targets); Emitter(); Emitter(const Emitter& copy, const osg::CopyOp& copyop); META_Object(NifOsg, Emitter) virtual void emitParticles(double dt); void setShooter(osgParticle::Shooter* shooter); void setPlacer(osgParticle::Placer* placer); void setCounter(osgParticle::Counter* counter); private: // NIF Record indices std::vector mTargets; osg::ref_ptr mPlacer; osg::ref_ptr mShooter; osg::ref_ptr mCounter; }; } #endif openmw-openmw-0.38.0/components/nifosg/userdata.hpp000066400000000000000000000030701264522266000224120ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_NIFOSG_USERDATA_H #define OPENMW_COMPONENTS_NIFOSG_USERDATA_H #include #include namespace NifOsg { // Note if you are copying a scene graph with this user data you should use the DEEP_COPY_USERDATA copyop. class NodeUserData : public osg::Object { public: NodeUserData(int index, float scale, const Nif::Matrix3& rotationScale) : mIndex(index), mScale(scale), mRotationScale(rotationScale) { } NodeUserData() : mIndex(0), mScale(0) { } NodeUserData(const NodeUserData& copy, const osg::CopyOp& copyop) : Object(copy, copyop) , mIndex(copy.mIndex) , mScale(copy.mScale) , mRotationScale(copy.mRotationScale) { } META_Object(NifOsg, NodeUserData) // NIF record index int mIndex; // Hack: account for Transform differences between OSG and NIFs. // OSG uses a 4x4 matrix, NIF's use a 3x3 rotationScale, float scale, and vec3 position. // Decomposing the original components from the 4x4 matrix isn't possible, which causes // problems when a KeyframeController wants to change only one of these components. So // we store the scale and rotation components separately here. // Note for a cleaner solution it would be possible to write a custom Transform node, // but then we have to fork osgAnimation :/ float mScale; Nif::Matrix3 mRotationScale; }; } #endif openmw-openmw-0.38.0/components/process/000077500000000000000000000000001264522266000202625ustar00rootroot00000000000000openmw-openmw-0.38.0/components/process/processinvoker.cpp000066400000000000000000000144771264522266000240570ustar00rootroot00000000000000#include "processinvoker.hpp" #include #include #include #include #include #include #include #include Process::ProcessInvoker::ProcessInvoker() { mProcess = new QProcess(this); connect(mProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); connect(mProcess, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); mName = QString(); mArguments = QStringList(); } Process::ProcessInvoker::~ProcessInvoker() { } //void Process::ProcessInvoker::setProcessName(const QString &name) //{ // mName = name; //} //void Process::ProcessInvoker::setProcessArguments(const QStringList &arguments) //{ // mArguments = arguments; //} QProcess* Process::ProcessInvoker::getProcess() { return mProcess; } //QString Process::ProcessInvoker::getProcessName() //{ // return mName; //} //QStringList Process::ProcessInvoker::getProcessArguments() //{ // return mArguments; //} bool Process::ProcessInvoker::startProcess(const QString &name, const QStringList &arguments, bool detached) { // mProcess = new QProcess(this); mName = name; mArguments = arguments; QString path(name); #ifdef Q_OS_WIN path.append(QLatin1String(".exe")); #elif defined(Q_OS_MAC) QDir dir(QCoreApplication::applicationDirPath()); path = dir.absoluteFilePath(name); #else path.prepend(QLatin1String("./")); #endif QFileInfo info(path); if (!info.exists()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error starting executable")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not find %1

\

The application is not found.

\

Please make sure OpenMW is installed correctly and try again.

").arg(info.fileName())); msgBox.exec(); return false; } if (!info.isExecutable()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error starting executable")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not start %1

\

The application is not executable.

\

Please make sure you have the right permissions and try again.

").arg(info.fileName())); msgBox.exec(); return false; } // Start the executable if (detached) { if (!mProcess->startDetached(path, arguments)) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error starting executable")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not start %1

\

An error occurred while starting %1.

\

Press \"Show Details...\" for more information.

").arg(info.fileName())); msgBox.setDetailedText(mProcess->errorString()); msgBox.exec(); return false; } } else { mProcess->start(path, arguments); /* if (!mProcess->waitForFinished()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error starting executable")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Could not start %1

\

An error occurred while starting %1.

\

Press \"Show Details...\" for more information.

").arg(info.fileName())); msgBox.setDetailedText(mProcess->errorString()); msgBox.exec(); return false; } if (mProcess->exitCode() != 0 || mProcess->exitStatus() == QProcess::CrashExit) { QString error(mProcess->readAllStandardError()); error.append(tr("\nArguments:\n")); error.append(arguments.join(" ")); QMessageBox msgBox; msgBox.setWindowTitle(tr("Error running executable")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Executable %1 returned an error

\

An error occurred while running %1.

\

Press \"Show Details...\" for more information.

").arg(info.fileName())); msgBox.setDetailedText(error); msgBox.exec(); return false; } */ } return true; } void Process::ProcessInvoker::processError(QProcess::ProcessError error) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error running executable")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Executable %1 returned an error

\

An error occurred while running %1.

\

Press \"Show Details...\" for more information.

").arg(mName)); msgBox.setDetailedText(mProcess->errorString()); msgBox.exec(); } void Process::ProcessInvoker::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode != 0 || exitStatus == QProcess::CrashExit) { QString error(mProcess->readAllStandardError()); error.append(tr("\nArguments:\n")); error.append(mArguments.join(" ")); QMessageBox msgBox; msgBox.setWindowTitle(tr("Error running executable")); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("

Executable %1 returned an error

\

An error occurred while running %1.

\

Press \"Show Details...\" for more information.

").arg(mName)); msgBox.setDetailedText(error); msgBox.exec(); } } openmw-openmw-0.38.0/components/process/processinvoker.hpp000066400000000000000000000021661264522266000240540ustar00rootroot00000000000000#ifndef PROCESSINVOKER_HPP #define PROCESSINVOKER_HPP #include #include #include namespace Process { class ProcessInvoker : public QObject { Q_OBJECT public: ProcessInvoker(); ~ProcessInvoker(); // void setProcessName(const QString &name); // void setProcessArguments(const QStringList &arguments); QProcess* getProcess(); // QString getProcessName(); // QStringList getProcessArguments(); // inline bool startProcess(bool detached = false) { return startProcess(mName, mArguments, detached); } inline bool startProcess(const QString &name, bool detached = false) { return startProcess(name, QStringList(), detached); } bool startProcess(const QString &name, const QStringList &arguments, bool detached = false); private: QProcess *mProcess; QString mName; QStringList mArguments; private slots: void processError(QProcess::ProcessError error); void processFinished(int exitCode, QProcess::ExitStatus exitStatus); }; } #endif // PROCESSINVOKER_HPP openmw-openmw-0.38.0/components/resource/000077500000000000000000000000001264522266000204335ustar00rootroot00000000000000openmw-openmw-0.38.0/components/resource/bulletshape.cpp000066400000000000000000000065211264522266000234530ustar00rootroot00000000000000#include "bulletshape.hpp" #include #include #include #include #include namespace Resource { BulletShape::BulletShape() : mCollisionShape(NULL) { } BulletShape::~BulletShape() { deleteShape(mCollisionShape); } void BulletShape::deleteShape(btCollisionShape* shape) { if(shape!=NULL) { if(shape->isCompound()) { btCompoundShape* ms = static_cast(shape); int a = ms->getNumChildShapes(); for(int i=0; i getChildShape(i)); } delete shape; } } btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape) const { if(shape->isCompound()) { btCompoundShape *comp = static_cast(shape); btCompoundShape *newShape = new btCompoundShape; int numShapes = comp->getNumChildShapes(); for(int i = 0;i < numShapes;++i) { btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i)); btTransform trans = comp->getChildTransform(i); newShape->addChildShape(trans, child); } return newShape; } if(btBvhTriangleMeshShape* trishape = dynamic_cast(shape)) { #if BT_BULLET_VERSION >= 283 btScaledBvhTriangleMeshShape* newShape = new btScaledBvhTriangleMeshShape(trishape, btVector3(1.f, 1.f, 1.f)); #else // work around btScaledBvhTriangleMeshShape bug ( https://code.google.com/p/bullet/issues/detail?id=371 ) in older bullet versions btTriangleMesh* oldMesh = static_cast(trishape->getMeshInterface()); btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh); // Do not build a new bvh (not needed, since it's the same as the original shape's bvh) bool buildBvh = true; if (trishape->getOptimizedBvh()) buildBvh = false; TriangleMeshShape* newShape = new TriangleMeshShape(newMesh, true, buildBvh); // Set original shape's bvh via pointer // The pointer is safe because the BulletShapeInstance keeps a ref_ptr to the original BulletShape if (!buildBvh) newShape->setOptimizedBvh(trishape->getOptimizedBvh()); #endif return newShape; } if (btBoxShape* boxshape = dynamic_cast(shape)) { return new btBoxShape(*boxshape); } throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName()); } btCollisionShape *BulletShape::getCollisionShape() { return mCollisionShape; } osg::ref_ptr BulletShape::makeInstance() { osg::ref_ptr instance (new BulletShapeInstance(this)); return instance; } BulletShapeInstance::BulletShapeInstance(osg::ref_ptr source) : BulletShape() , mSource(source) { mCollisionBoxHalfExtents = source->mCollisionBoxHalfExtents; mCollisionBoxTranslate = source->mCollisionBoxTranslate; mAnimatedShapes = source->mAnimatedShapes; if (source->mCollisionShape) mCollisionShape = duplicateCollisionShape(source->mCollisionShape); } } openmw-openmw-0.38.0/components/resource/bulletshape.hpp000066400000000000000000000046451264522266000234650ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H #define OPENMW_COMPONENTS_RESOURCE_BULLETSHAPE_H #include #include #include #include #include class btCollisionShape; namespace Resource { class BulletShapeInstance; class BulletShape : public osg::Referenced { public: BulletShape(); virtual ~BulletShape(); btCollisionShape* mCollisionShape; // Used for actors. Note, ideally actors would use a separate loader - as it is // we have to keep a redundant copy of the actor model around in mCollisionShape, which isn't used. // For now, use one file <-> one resource for simplicity. osg::Vec3f mCollisionBoxHalfExtents; osg::Vec3f mCollisionBoxTranslate; // Stores animated collision shapes. If any collision nodes in the NIF are animated, then mCollisionShape // will be a btCompoundShape (which consists of one or more child shapes). // In this map, for each animated collision shape, // we store the node's record index mapped to the child index of the shape in the btCompoundShape. std::map mAnimatedShapes; osg::ref_ptr makeInstance(); btCollisionShape* duplicateCollisionShape(btCollisionShape* shape) const; btCollisionShape* getCollisionShape(); private: void deleteShape(btCollisionShape* shape); }; // An instance of a BulletShape that may have its own unique scaling set on the mCollisionShape. // Vertex data is shallow-copied where possible. A ref_ptr to the original shape is held to keep vertex pointers intact. class BulletShapeInstance : public BulletShape { public: BulletShapeInstance(osg::ref_ptr source); private: osg::ref_ptr mSource; }; // Subclass btBhvTriangleMeshShape to auto-delete the meshInterface struct TriangleMeshShape : public btBvhTriangleMeshShape { TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression, bool buildBvh = true) : btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression, buildBvh) { } virtual ~TriangleMeshShape() { delete getTriangleInfoMap(); delete m_meshInterface; } }; } #endif openmw-openmw-0.38.0/components/resource/bulletshapemanager.cpp000066400000000000000000000074761264522266000250200ustar00rootroot00000000000000#include "bulletshapemanager.hpp" #include #include #include #include #include #include #include "bulletshape.hpp" #include "scenemanager.hpp" #include "niffilemanager.hpp" namespace Resource { struct GetTriangleFunctor { GetTriangleFunctor() : mTriMesh(NULL) { } void setTriMesh(btTriangleMesh* triMesh) { mTriMesh = triMesh; } void setMatrix(const osg::Matrixf& matrix) { mMatrix = matrix; } inline btVector3 toBullet(const osg::Vec3f& vec) { return btVector3(vec.x(), vec.y(), vec.z()); } void inline operator()( const osg::Vec3 v1, const osg::Vec3 v2, const osg::Vec3 v3, bool _temp ) { if (mTriMesh) mTriMesh->addTriangle( toBullet(mMatrix.preMult(v1)), toBullet(mMatrix.preMult(v2)), toBullet(mMatrix.preMult(v3))); } btTriangleMesh* mTriMesh; osg::Matrixf mMatrix; }; /// Creates a BulletShape out of a Node hierarchy. class NodeToShapeVisitor : public osg::NodeVisitor { public: NodeToShapeVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mTriangleMesh(NULL) { } virtual void apply(osg::Geode& geode) { for (unsigned int i=0; i functor; functor.setTriMesh(mTriangleMesh); functor.setMatrix(worldMat); drawable.accept(functor); } osg::ref_ptr getShape() { if (!mTriangleMesh) return osg::ref_ptr(); osg::ref_ptr shape (new BulletShape); TriangleMeshShape* meshShape = new TriangleMeshShape(mTriangleMesh, true); shape->mCollisionShape = meshShape; mTriangleMesh = NULL; return shape; } private: btTriangleMesh* mTriangleMesh; }; BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager) : mVFS(vfs) , mSceneManager(sceneMgr) , mNifFileManager(nifFileManager) { } BulletShapeManager::~BulletShapeManager() { } osg::ref_ptr BulletShapeManager::createInstance(const std::string &name) { std::string normalized = name; mVFS->normalizeFilename(normalized); osg::ref_ptr shape; Index::iterator it = mIndex.find(normalized); if (it == mIndex.end()) { size_t extPos = normalized.find_last_of('.'); std::string ext; if (extPos != std::string::npos && extPos+1 < normalized.size()) ext = normalized.substr(extPos+1); if (ext == "nif") { NifBullet::BulletNifLoader loader; shape = loader.load(mNifFileManager->get(normalized)); } else { // TODO: support .bullet shape files osg::ref_ptr constNode (mSceneManager->getTemplate(normalized)); osg::ref_ptr node (const_cast(constNode.get())); // const-trickery required because there is no const version of NodeVisitor NodeToShapeVisitor visitor; node->accept(visitor); shape = visitor.getShape(); if (!shape) return osg::ref_ptr(); } mIndex[normalized] = shape; } else shape = it->second; osg::ref_ptr instance = shape->makeInstance(); return instance; } } openmw-openmw-0.38.0/components/resource/bulletshapemanager.hpp000066400000000000000000000015311264522266000250070ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H #define OPENMW_COMPONENTS_BULLETSHAPEMANAGER_H #include #include #include #include "bulletshape.hpp" namespace VFS { class Manager; } namespace Resource { class SceneManager; class NifFileManager; class BulletShape; class BulletShapeInstance; class BulletShapeManager { public: BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager); ~BulletShapeManager(); osg::ref_ptr createInstance(const std::string& name); private: const VFS::Manager* mVFS; SceneManager* mSceneManager; NifFileManager* mNifFileManager; typedef std::map > Index; Index mIndex; }; } #endif openmw-openmw-0.38.0/components/resource/keyframemanager.cpp000066400000000000000000000021151264522266000242740ustar00rootroot00000000000000#include "keyframemanager.hpp" #include #include #include "objectcache.hpp" namespace Resource { KeyframeManager::KeyframeManager(const VFS::Manager* vfs) : mCache(new osgDB::ObjectCache) , mVFS(vfs) { } KeyframeManager::~KeyframeManager() { } osg::ref_ptr KeyframeManager::get(const std::string &name) { std::string normalized = name; mVFS->normalizeFilename(normalized); osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); if (obj) return osg::ref_ptr(static_cast(obj.get())); else { osg::ref_ptr loaded (new NifOsg::KeyframeHolder); NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(mVFS->getNormalized(normalized), normalized)), *loaded.get()); mCache->addEntryToObjectCache(normalized, loaded); return loaded; } } } openmw-openmw-0.38.0/components/resource/keyframemanager.hpp000066400000000000000000000015671264522266000243130ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_KEYFRAMEMANAGER_H #define OPENMW_COMPONENTS_KEYFRAMEMANAGER_H #include #include namespace VFS { class Manager; } namespace osgDB { class ObjectCache; } namespace NifOsg { class KeyframeHolder; } namespace Resource { /// @brief Managing of keyframe resources class KeyframeManager { public: KeyframeManager(const VFS::Manager* vfs); ~KeyframeManager(); void clearCache(); /// Retrieve a read-only keyframe resource by name (case-insensitive). /// @note This method is safe to call from any thread. /// @note Throws an exception if the resource is not found. osg::ref_ptr get(const std::string& name); private: osg::ref_ptr mCache; const VFS::Manager* mVFS; }; } #endif openmw-openmw-0.38.0/components/resource/niffilemanager.cpp000066400000000000000000000027311264522266000241110ustar00rootroot00000000000000#include "niffilemanager.hpp" #include #include "objectcache.hpp" namespace Resource { class NifFileHolder : public osg::Object { public: NifFileHolder(const Nif::NIFFilePtr& file) : mNifFile(file) { } NifFileHolder(const NifFileHolder& copy, const osg::CopyOp& copyop) : mNifFile(copy.mNifFile) { } NifFileHolder() { } META_Object(Resource, NifFileHolder) Nif::NIFFilePtr mNifFile; }; NifFileManager::NifFileManager(const VFS::Manager *vfs) : mVFS(vfs) { mCache = new osgDB::ObjectCache; } NifFileManager::~NifFileManager() { } void NifFileManager::clearCache() { // NIF files aren't needed any more when the converted objects are cached in SceneManager / BulletShapeManager, // so we'll simply drop all nif files here, unlikely to need them again mCache->clear(); } Nif::NIFFilePtr NifFileManager::get(const std::string &name) { osg::ref_ptr obj = mCache->getRefFromObjectCache(name); if (obj) return static_cast(obj.get())->mNifFile; else { Nif::NIFFilePtr file (new Nif::NIFFile(mVFS->getNormalized(name), name)); obj = new NifFileHolder(file); mCache->addEntryToObjectCache(name, obj); return file; } } } openmw-openmw-0.38.0/components/resource/niffilemanager.hpp000066400000000000000000000020261264522266000241130ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_RESOURCE_NIFFILEMANAGER_H #define OPENMW_COMPONENTS_RESOURCE_NIFFILEMANAGER_H #include #include namespace VFS { class Manager; } namespace osgDB { class ObjectCache; } namespace Resource { /// @brief Handles caching of NIFFiles. /// @note The NifFileManager is completely thread safe. class NifFileManager { public: NifFileManager(const VFS::Manager* vfs); ~NifFileManager(); void clearCache(); /// Retrieve a NIF file from the cache, or load it from the VFS if not cached yet. /// @note For performance reasons the NifFileManager does not handle case folding, needs /// to be done in advance by other managers accessing the NifFileManager. Nif::NIFFilePtr get(const std::string& name); private: // Use the osgDB::ObjectCache so objects are retrieved in thread safe way osg::ref_ptr mCache; const VFS::Manager* mVFS; }; } #endif openmw-openmw-0.38.0/components/resource/objectcache.cpp000066400000000000000000000105601264522266000233730ustar00rootroot00000000000000/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * 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 * OpenSceneGraph Public License for more details. */ #include #if OSG_VERSION_LESS_THAN(3,3,3) #include "objectcache.hpp" using namespace osgDB; //////////////////////////////////////////////////////////////////////////////////////////// // // ObjectCache // ObjectCache::ObjectCache(): osg::Referenced(true) { // OSG_NOTICE<<"Constructed ObjectCache"< lock1(_objectCacheMutex); OpenThreads::ScopedLock lock2(objectCache->_objectCacheMutex); // OSG_NOTICE<<"Inserting objects to main ObjectCache "<_objectCache.size()<_objectCache.begin(), objectCache->_objectCache.end()); } void ObjectCache::addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp) { OpenThreads::ScopedLock lock(_objectCacheMutex); _objectCache[filename]=ObjectTimeStampPair(object,timestamp); } osg::Object* ObjectCache::getFromObjectCache(const std::string& fileName) { OpenThreads::ScopedLock lock(_objectCacheMutex); ObjectCacheMap::iterator itr = _objectCache.find(fileName); if (itr!=_objectCache.end()) return itr->second.first.get(); else return 0; } osg::ref_ptr ObjectCache::getRefFromObjectCache(const std::string& fileName) { OpenThreads::ScopedLock lock(_objectCacheMutex); ObjectCacheMap::iterator itr = _objectCache.find(fileName); if (itr!=_objectCache.end()) { // OSG_NOTICE<<"Found "<second.first; } else return 0; } void ObjectCache::updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime) { OpenThreads::ScopedLock lock(_objectCacheMutex); // look for objects with external references and update their time stamp. for(ObjectCacheMap::iterator itr=_objectCache.begin(); itr!=_objectCache.end(); ++itr) { // if ref count is greater the 1 the object has an external reference. if (itr->second.first->referenceCount()>1) { // so update it time stamp. itr->second.second = referenceTime; } } } void ObjectCache::removeExpiredObjectsInCache(double expiryTime) { OpenThreads::ScopedLock lock(_objectCacheMutex); // Remove expired entries from object cache ObjectCacheMap::iterator oitr = _objectCache.begin(); while(oitr != _objectCache.end()) { if (oitr->second.second<=expiryTime) { _objectCache.erase(oitr++); } else { ++oitr; } } } void ObjectCache::removeFromObjectCache(const std::string& fileName) { OpenThreads::ScopedLock lock(_objectCacheMutex); ObjectCacheMap::iterator itr = _objectCache.find(fileName); if (itr!=_objectCache.end()) _objectCache.erase(itr); } void ObjectCache::clear() { OpenThreads::ScopedLock lock(_objectCacheMutex); _objectCache.clear(); } void ObjectCache::releaseGLObjects(osg::State* state) { OpenThreads::ScopedLock lock(_objectCacheMutex); for(ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr) { osg::Object* object = itr->second.first.get(); object->releaseGLObjects(state); } } #endif openmw-openmw-0.38.0/components/resource/objectcache.hpp000066400000000000000000000067771264522266000234170ustar00rootroot00000000000000/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * 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 * OpenSceneGraph Public License for more details. */ // Wrapper for osgDB/ObjectCache. Works around ObjectCache not being available in old OSG 3.2. // Use "#include objectcache.hpp" in place of "#include #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) #include #else #include #include #include #include namespace osgDB { class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced { public: ObjectCache(); /** For each object in the cache which has an reference count greater than 1 * (and therefore referenced by elsewhere in the application) set the time stamp * for that object in the cache to specified time. * This would typically be called once per frame by applications which are doing database paging, * and need to prune objects that are no longer required. * The time used should be taken from the FrameStamp::getReferenceTime().*/ void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime); /** Removed object in the cache which have a time stamp at or before the specified expiry time. * This would typically be called once per frame by applications which are doing database paging, * and need to prune objects that are no longer required, and called after the a called * after the call to updateTimeStampOfObjectsInCacheWithExternalReferences(expirtyTime).*/ void removeExpiredObjectsInCache(double expiryTime); /** Remove all objects in the cache regardless of having external references or expiry times.*/ void clear(); /** Add contents of specified ObjectCache to this object cache.*/ void addObjectCache(ObjectCache* object); /** Add a filename,object,timestamp triple to the Registry::ObjectCache.*/ void addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp = 0.0); /** Remove Object from cache.*/ void removeFromObjectCache(const std::string& fileName); /** Get an Object from the object cache*/ osg::Object* getFromObjectCache(const std::string& fileName); /** Get an ref_ptr from the object cache*/ osg::ref_ptr getRefFromObjectCache(const std::string& fileName); /** call rleaseGLObjects on all objects attached to the object cache.*/ void releaseGLObjects(osg::State* state); protected: virtual ~ObjectCache(); typedef std::pair, double > ObjectTimeStampPair; typedef std::map ObjectCacheMap; ObjectCacheMap _objectCache; OpenThreads::Mutex _objectCacheMutex; }; } #endif #endif openmw-openmw-0.38.0/components/resource/resourcesystem.cpp000066400000000000000000000023761264522266000242430ustar00rootroot00000000000000#include "resourcesystem.hpp" #include "scenemanager.hpp" #include "texturemanager.hpp" #include "niffilemanager.hpp" #include "keyframemanager.hpp" namespace Resource { ResourceSystem::ResourceSystem(const VFS::Manager *vfs) : mVFS(vfs) { mNifFileManager.reset(new NifFileManager(vfs)); mKeyframeManager.reset(new KeyframeManager(vfs)); mTextureManager.reset(new TextureManager(vfs)); mSceneManager.reset(new SceneManager(vfs, mTextureManager.get(), mNifFileManager.get())); } ResourceSystem::~ResourceSystem() { // this has to be defined in the .cpp file as we can't delete incomplete types } SceneManager* ResourceSystem::getSceneManager() { return mSceneManager.get(); } TextureManager* ResourceSystem::getTextureManager() { return mTextureManager.get(); } NifFileManager* ResourceSystem::getNifFileManager() { return mNifFileManager.get(); } KeyframeManager* ResourceSystem::getKeyframeManager() { return mKeyframeManager.get(); } void ResourceSystem::clearCache() { mNifFileManager->clearCache(); } const VFS::Manager* ResourceSystem::getVFS() const { return mVFS; } } openmw-openmw-0.38.0/components/resource/resourcesystem.hpp000066400000000000000000000026641264522266000242500ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H #define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H #include namespace VFS { class Manager; } namespace Resource { class SceneManager; class TextureManager; class NifFileManager; class KeyframeManager; /// @brief Wrapper class that constructs and provides access to the most commonly used resource subsystems. /// @par Resource subsystems can be used with multiple OpenGL contexts, just like the OSG equivalents, but /// are built around the use of a single virtual file system. class ResourceSystem { public: ResourceSystem(const VFS::Manager* vfs); ~ResourceSystem(); SceneManager* getSceneManager(); TextureManager* getTextureManager(); NifFileManager* getNifFileManager(); KeyframeManager* getKeyframeManager(); /// Indicates to each resource manager to clear the cache, i.e. to drop cached objects that are no longer referenced. void clearCache(); const VFS::Manager* getVFS() const; private: std::auto_ptr mSceneManager; std::auto_ptr mTextureManager; std::auto_ptr mNifFileManager; std::auto_ptr mKeyframeManager; const VFS::Manager* mVFS; ResourceSystem(const ResourceSystem&); void operator = (const ResourceSystem&); }; } #endif openmw-openmw-0.38.0/components/resource/scenemanager.cpp000066400000000000000000000227471264522266000236030ustar00rootroot00000000000000#include "scenemanager.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "texturemanager.hpp" #include "niffilemanager.hpp" namespace { class InitWorldSpaceParticlesVisitor : public osg::NodeVisitor { public: /// @param mask The node mask to set on ParticleSystem nodes. InitWorldSpaceParticlesVisitor(unsigned int mask) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mMask(mask) { } bool isWorldSpaceParticleSystem(osgParticle::ParticleSystem* partsys) { // HACK: ParticleSystem has no getReferenceFrame() return (partsys->getUserDataContainer() && partsys->getUserDataContainer()->getNumDescriptions() > 0 && partsys->getUserDataContainer()->getDescriptions()[0] == "worldspace"); } void apply(osg::Geode& geode) { for (unsigned int i=0;i(geode.getDrawable(i))) { if (isWorldSpaceParticleSystem(partsys)) { // HACK: Ignore the InverseWorldMatrix transform the geode is attached to if (geode.getNumParents() && geode.getParent(0)->getNumParents()) transformInitialParticles(partsys, geode.getParent(0)->getParent(0)); } geode.setNodeMask(mMask); } } } #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) // in OSG 3.3 and up Drawables can be directly in the scene graph without a Geode decorating them. void apply(osg::Drawable& drw) { if (osgParticle::ParticleSystem* partsys = dynamic_cast(&drw)) { if (isWorldSpaceParticleSystem(partsys)) { // HACK: Ignore the InverseWorldMatrix transform the particle system is attached to if (partsys->getNumParents() && partsys->getParent(0)->getNumParents()) transformInitialParticles(partsys, partsys->getParent(0)->getParent(0)); } partsys->setNodeMask(mMask); } } #endif void transformInitialParticles(osgParticle::ParticleSystem* partsys, osg::Node* node) { osg::MatrixList mats = node->getWorldMatrices(); if (mats.empty()) return; osg::Matrixf worldMat = mats[0]; worldMat.orthoNormalize(worldMat); // scale is already applied on the particle node for (int i=0; inumParticles(); ++i) { partsys->getParticle(i)->transformPositionVelocity(worldMat); } // transform initial bounds to worldspace osg::BoundingSphere sphere(partsys->getInitialBound()); SceneUtil::transformBoundingSphere(worldMat, sphere); osg::BoundingBox box; box.expandBy(sphere); partsys->setInitialBound(box); } private: unsigned int mMask; }; } namespace Resource { SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager) : mVFS(vfs) , mTextureManager(textureManager) , mNifFileManager(nifFileManager) , mParticleSystemMask(~0u) { } SceneManager::~SceneManager() { // this has to be defined in the .cpp file as we can't delete incomplete types } /// @brief Callback to read image files from the VFS. class ImageReadCallback : public osgDB::ReadFileCallback { public: ImageReadCallback(Resource::TextureManager* textureMgr) : mTextureManager(textureMgr) { } virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& filename, const osgDB::Options* options) { try { return osgDB::ReaderWriter::ReadResult(mTextureManager->getImage(filename), osgDB::ReaderWriter::ReadResult::FILE_LOADED); } catch (std::exception& e) { return osgDB::ReaderWriter::ReadResult(e.what()); } } private: Resource::TextureManager* mTextureManager; }; std::string getFileExtension(const std::string& file) { size_t extPos = file.find_last_of('.'); if (extPos != std::string::npos && extPos+1 < file.size()) return file.substr(extPos+1); return std::string(); } osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::TextureManager* textureMgr, Resource::NifFileManager* nifFileManager) { std::string ext = getFileExtension(normalizedFilename); if (ext == "nif") return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), textureMgr); else { osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); if (!reader) { std::stringstream errormsg; errormsg << "Error loading " << normalizedFilename << ": no readerwriter for '" << ext << "' found" << std::endl; throw std::runtime_error(errormsg.str()); } osg::ref_ptr options (new osgDB::Options); // Set a ReadFileCallback so that image files referenced in the model are read from our virtual file system instead of the osgDB. // Note, for some formats (.obj/.mtl) that reference other (non-image) files a findFileCallback would be necessary. // but findFileCallback does not support virtual files, so we can't implement it. options->setReadFileCallback(new ImageReadCallback(textureMgr)); osgDB::ReaderWriter::ReadResult result = reader->readNode(*file, options); if (!result.success()) { std::stringstream errormsg; errormsg << "Error loading " << normalizedFilename << ": " << result.message() << " code " << result.status() << std::endl; throw std::runtime_error(errormsg.str()); } return result.getNode(); } } osg::ref_ptr SceneManager::getTemplate(const std::string &name) { std::string normalized = name; mVFS->normalizeFilename(normalized); Index::iterator it = mIndex.find(normalized); if (it == mIndex.end()) { osg::ref_ptr loaded; try { Files::IStreamPtr file = mVFS->get(normalized); loaded = load(file, normalized, mTextureManager, mNifFileManager); } catch (std::exception& e) { std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error.nif instead" << std::endl; Files::IStreamPtr file = mVFS->get("meshes/marker_error.nif"); normalized = "meshes/marker_error.nif"; loaded = load(file, normalized, mTextureManager, mNifFileManager); } osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get()); if (mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); mIndex[normalized] = loaded; return loaded; } else return it->second; } osg::ref_ptr SceneManager::createInstance(const std::string &name) { osg::ref_ptr scene = getTemplate(name); osg::ref_ptr cloned = osg::clone(scene.get(), SceneUtil::CopyOp()); return cloned; } osg::ref_ptr SceneManager::createInstance(const std::string &name, osg::Group* parentNode) { osg::ref_ptr cloned = createInstance(name); attachTo(cloned, parentNode); return cloned; } void SceneManager::attachTo(osg::Node *instance, osg::Group *parentNode) const { parentNode->addChild(instance); notifyAttached(instance); } void SceneManager::releaseGLObjects(osg::State *state) { for (Index::iterator it = mIndex.begin(); it != mIndex.end(); ++it) { it->second->releaseGLObjects(state); } } void SceneManager::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation *ico) { mIncrementalCompileOperation = ico; } void SceneManager::notifyAttached(osg::Node *node) const { InitWorldSpaceParticlesVisitor visitor (mParticleSystemMask); node->accept(visitor); } const VFS::Manager* SceneManager::getVFS() const { return mVFS; } Resource::TextureManager* SceneManager::getTextureManager() { return mTextureManager; } void SceneManager::setParticleSystemMask(unsigned int mask) { mParticleSystemMask = mask; } } openmw-openmw-0.38.0/components/resource/scenemanager.hpp000066400000000000000000000061311264522266000235750ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_RESOURCE_SCENEMANAGER_H #define OPENMW_COMPONENTS_RESOURCE_SCENEMANAGER_H #include #include #include #include namespace Resource { class TextureManager; class NifFileManager; } namespace VFS { class Manager; } namespace osgUtil { class IncrementalCompileOperation; } namespace Resource { /// @brief Handles loading and caching of scenes, e.g. NIF files class SceneManager { public: SceneManager(const VFS::Manager* vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager); ~SceneManager(); /// Get a read-only copy of this scene "template" /// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead. /// If even the error marker mesh can not be found, an exception is thrown. osg::ref_ptr getTemplate(const std::string& name); /// Create an instance of the given scene template /// @see getTemplate osg::ref_ptr createInstance(const std::string& name); /// Create an instance of the given scene template and immediately attach it to a parent node /// @see getTemplate osg::ref_ptr createInstance(const std::string& name, osg::Group* parentNode); /// Attach the given scene instance to the given parent node /// @note You should have the parentNode in its intended position before calling this method, /// so that world space particles of the \a instance get transformed correctly. /// @note Assumes the given instance was not attached to any parents before. void attachTo(osg::Node* instance, osg::Group* parentNode) const; /// Manually release created OpenGL objects for the given graphics context. This may be required /// in cases where multiple contexts are used over the lifetime of the application. void releaseGLObjects(osg::State* state); /// Set up an IncrementalCompileOperation for background compiling of loaded scenes. void setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico); /// @note SceneManager::attachTo calls this method automatically, only needs to be called by users if manually attaching void notifyAttached(osg::Node* node) const; const VFS::Manager* getVFS() const; Resource::TextureManager* getTextureManager(); /// @param mask The node mask to apply to loaded particle system nodes. void setParticleSystemMask(unsigned int mask); private: const VFS::Manager* mVFS; Resource::TextureManager* mTextureManager; Resource::NifFileManager* mNifFileManager; osg::ref_ptr mIncrementalCompileOperation; unsigned int mParticleSystemMask; // observer_ptr? typedef std::map > Index; Index mIndex; SceneManager(const SceneManager&); void operator = (const SceneManager&); }; } #endif openmw-openmw-0.38.0/components/resource/texturemanager.cpp000066400000000000000000000267221264522266000242030ustar00rootroot00000000000000#include "texturemanager.hpp" #include #include #include #include #include #include #ifdef OSG_LIBRARY_STATIC // This list of plugins should match with the list in the top-level CMakelists.txt. USE_OSGPLUGIN(png) USE_OSGPLUGIN(tga) USE_OSGPLUGIN(dds) USE_OSGPLUGIN(jpeg) #endif namespace { osg::ref_ptr createWarningTexture() { osg::ref_ptr warningImage = new osg::Image; int width = 8, height = 8; warningImage->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE); assert (warningImage->isDataContiguous()); unsigned char* data = warningImage->data(); for (int i=0;i warningTexture = new osg::Texture2D; warningTexture->setImage(warningImage); return warningTexture; } } namespace Resource { TextureManager::TextureManager(const VFS::Manager *vfs) : mVFS(vfs) , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) , mMagFilter(osg::Texture::LINEAR) , mMaxAnisotropy(1) , mWarningTexture(createWarningTexture()) , mUnRefImageDataAfterApply(false) { } TextureManager::~TextureManager() { } void TextureManager::setUnRefImageDataAfterApply(bool unref) { mUnRefImageDataAfterApply = unref; } void TextureManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter, const std::string &mipmap, int maxAnisotropy, osgViewer::Viewer *viewer) { osg::Texture::FilterMode min = osg::Texture::LINEAR; osg::Texture::FilterMode mag = osg::Texture::LINEAR; if(magfilter == "nearest") mag = osg::Texture::NEAREST; else if(magfilter != "linear") std::cerr<< "Invalid texture mag filter: "<stopThreading(); setFilterSettings(min, mag, maxAnisotropy); if(viewer) viewer->startThreading(); } void TextureManager::setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy) { mMinFilter = minFilter; mMagFilter = magFilter; mMaxAnisotropy = std::max(1, maxAnisotropy); for (std::map >::iterator it = mTextures.begin(); it != mTextures.end(); ++it) { osg::ref_ptr tex = it->second; // Keep mip-mapping disabled if the texture creator explicitely requested no mipmapping. osg::Texture::FilterMode oldMin = tex->getFilter(osg::Texture::MIN_FILTER); if (oldMin == osg::Texture::LINEAR || oldMin == osg::Texture::NEAREST) { osg::Texture::FilterMode newMin = osg::Texture::LINEAR; switch (mMinFilter) { case osg::Texture::LINEAR: case osg::Texture::LINEAR_MIPMAP_LINEAR: case osg::Texture::LINEAR_MIPMAP_NEAREST: newMin = osg::Texture::LINEAR; break; case osg::Texture::NEAREST: case osg::Texture::NEAREST_MIPMAP_LINEAR: case osg::Texture::NEAREST_MIPMAP_NEAREST: newMin = osg::Texture::NEAREST; break; } tex->setFilter(osg::Texture::MIN_FILTER, newMin); } else tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); tex->setMaxAnisotropy(static_cast(mMaxAnisotropy)); } } /* osg::ref_ptr TextureManager::getImage(const std::string &filename) { } */ bool checkSupported(osg::Image* image, const std::string& filename) { switch(image->getPixelFormat()) { case(GL_COMPRESSED_RGB_S3TC_DXT1_EXT): case(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT): case(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT): case(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT): { #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) osg::GLExtensions* exts = osg::GLExtensions::Get(0, false); if (exts && !exts->isTextureCompressionS3TCSupported // This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a patch to OSG. && !osg::isGLExtensionSupported(0, "GL_S3_s3tc")) #else osg::Texture::Extensions* exts = osg::Texture::getExtensions(0, false); if (exts && !exts->isTextureCompressionS3TCSupported() // This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a patch to OSG. && !osg::isGLExtensionSupported(0, "GL_S3_s3tc")) #endif { std::cerr << "Error loading " << filename << ": no S3TC texture compression support installed" << std::endl; return false; } break; } // not bothering with checks for other compression formats right now, we are unlikely to ever use those anyway default: return true; } return true; } osg::ref_ptr TextureManager::getImage(const std::string &filename) { std::string normalized = filename; mVFS->normalizeFilename(normalized); std::map >::iterator found = mImages.find(normalized); if (found != mImages.end()) return found->second; else { Files::IStreamPtr stream; try { stream = mVFS->get(normalized.c_str()); } catch (std::exception& e) { std::cerr << "Failed to open image: " << e.what() << std::endl; return NULL; } osg::ref_ptr opts (new osgDB::Options); opts->setOptionString("dds_dxt1_detect_rgba"); // tx_creature_werewolf.dds isn't loading in the correct format without this option size_t extPos = normalized.find_last_of('.'); std::string ext; if (extPos != std::string::npos && extPos+1 < normalized.size()) ext = normalized.substr(extPos+1); osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); if (!reader) { std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl; return NULL; } osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, opts); if (!result.success()) { std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; return NULL; } osg::Image* image = result.getImage(); if (!checkSupported(image, filename)) { return NULL; } mImages.insert(std::make_pair(normalized, image)); return image; } } osg::ref_ptr TextureManager::getTexture2D(const std::string &filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT) { std::string normalized = filename; mVFS->normalizeFilename(normalized); MapKey key = std::make_pair(std::make_pair(wrapS, wrapT), normalized); std::map >::iterator found = mTextures.find(key); if (found != mTextures.end()) { return found->second; } else { Files::IStreamPtr stream; try { stream = mVFS->get(normalized.c_str()); } catch (std::exception& e) { std::cerr << "Failed to open texture: " << e.what() << std::endl; return mWarningTexture; } osg::ref_ptr opts (new osgDB::Options); opts->setOptionString("dds_dxt1_detect_rgba"); // tx_creature_werewolf.dds isn't loading in the correct format without this option size_t extPos = normalized.find_last_of('.'); std::string ext; if (extPos != std::string::npos && extPos+1 < normalized.size()) ext = normalized.substr(extPos+1); osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); if (!reader) { std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl; return mWarningTexture; } osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, opts); if (!result.success()) { std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; return mWarningTexture; } osg::Image* image = result.getImage(); if (!checkSupported(image, filename)) { return mWarningTexture; } // We need to flip images, because the Morrowind texture coordinates use the DirectX convention (top-left image origin), // but OpenGL uses bottom left as the image origin. // For some reason this doesn't concern DDS textures, which are already flipped when loaded. if (ext != "dds") { image->flipVertical(); } osg::ref_ptr texture(new osg::Texture2D); texture->setImage(image); texture->setWrap(osg::Texture::WRAP_S, wrapS); texture->setWrap(osg::Texture::WRAP_T, wrapT); texture->setFilter(osg::Texture::MIN_FILTER, mMinFilter); texture->setFilter(osg::Texture::MAG_FILTER, mMagFilter); texture->setMaxAnisotropy(mMaxAnisotropy); texture->setUnRefImageDataAfterApply(mUnRefImageDataAfterApply); mTextures.insert(std::make_pair(key, texture)); return texture; } } osg::Texture2D* TextureManager::getWarningTexture() { return mWarningTexture.get(); } } openmw-openmw-0.38.0/components/resource/texturemanager.hpp000066400000000000000000000044421264522266000242030ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_RESOURCE_TEXTUREMANAGER_H #define OPENMW_COMPONENTS_RESOURCE_TEXTUREMANAGER_H #include #include #include #include #include namespace osgViewer { class Viewer; } namespace VFS { class Manager; } namespace Resource { /// @brief Handles loading/caching of Images and Texture StateAttributes. class TextureManager { public: TextureManager(const VFS::Manager* vfs); ~TextureManager(); void setFilterSettings(const std::string &magfilter, const std::string &minfilter, const std::string &mipmap, int maxAnisotropy, osgViewer::Viewer *view); /// Keep a copy of the texture data around in system memory? This is needed when using multiple graphics contexts, /// otherwise should be disabled to reduce memory usage. void setUnRefImageDataAfterApply(bool unref); /// Create or retrieve a Texture2D using the specified image filename, and wrap parameters. /// Returns the dummy texture if the given texture is not found. osg::ref_ptr getTexture2D(const std::string& filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT); /// Create or retrieve an Image osg::ref_ptr getImage(const std::string& filename); const VFS::Manager* getVFS() { return mVFS; } osg::Texture2D* getWarningTexture(); private: const VFS::Manager* mVFS; osg::Texture::FilterMode mMinFilter; osg::Texture::FilterMode mMagFilter; int mMaxAnisotropy; typedef std::pair, std::string> MapKey; std::map > mImages; std::map > mTextures; osg::ref_ptr mWarningTexture; bool mUnRefImageDataAfterApply; /// @warning It is unsafe to call this function when a draw thread is using the textures. Call stopThreading() first! void setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode maxFilter, int maxAnisotropy); TextureManager(const TextureManager&); void operator = (const TextureManager&); }; } #endif openmw-openmw-0.38.0/components/sceneutil/000077500000000000000000000000001264522266000205775ustar00rootroot00000000000000openmw-openmw-0.38.0/components/sceneutil/attach.cpp000066400000000000000000000075361264522266000225620ustar00rootroot00000000000000#include "attach.hpp" #include #include #include #include #include #include #include #include #include #include "visitor.hpp" namespace SceneUtil { class CopyRigVisitor : public osg::NodeVisitor { public: CopyRigVisitor(osg::ref_ptr parent, const std::string& filter) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mParent(parent) , mFilter(Misc::StringUtils::lowerCase(filter)) { mFilter2 = "tri " + mFilter; } virtual void apply(osg::Node& node) { std::string lowerName = Misc::StringUtils::lowerCase(node.getName()); if ((lowerName.size() >= mFilter.size() && lowerName.compare(0, mFilter.size(), mFilter) == 0) || (lowerName.size() >= mFilter2.size() && lowerName.compare(0, mFilter2.size(), mFilter2) == 0)) { mParent->addChild(&node); } else traverse(node); } private: osg::ref_ptr mParent; std::string mFilter; std::string mFilter2; }; osg::ref_ptr attach(osg::ref_ptr toAttach, osg::Node *master, const std::string &filter, const std::string &attachNode) { if (dynamic_cast(toAttach.get())) { osg::ref_ptr handle = new osg::Group; CopyRigVisitor copyVisitor(handle, filter); toAttach->accept(copyVisitor); master->asGroup()->addChild(handle); return handle; } else { FindByNameVisitor find(attachNode); master->accept(find); if (!find.mFoundNode) throw std::runtime_error(std::string("Can't find attachment node ") + attachNode); FindByNameVisitor findBoneOffset("BoneOffset"); toAttach->accept(findBoneOffset); osg::ref_ptr trans; if (findBoneOffset.mFoundNode) { osg::MatrixTransform* boneOffset = dynamic_cast(findBoneOffset.mFoundNode); if (!boneOffset) throw std::runtime_error("BoneOffset must be a MatrixTransform"); trans = new osg::PositionAttitudeTransform; trans->setPosition(boneOffset->getMatrix().getTrans()); // The BoneOffset rotation seems to be incorrect trans->setAttitude(osg::Quat(osg::DegreesToRadians(-90.f), osg::Vec3f(1,0,0))); } if (attachNode.find("Left") != std::string::npos) { if (!trans) trans = new osg::PositionAttitudeTransform; trans->setScale(osg::Vec3f(-1.f, 1.f, 1.f)); // Need to invert culling because of the negative scale // Note: for absolute correctness we would need to check the current front face for every mesh then invert it // However MW isn't doing this either, so don't. Assuming all meshes are using backface culling is more efficient. osg::FrontFace* frontFace = new osg::FrontFace; frontFace->setMode(osg::FrontFace::CLOCKWISE); trans->getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON); } if (trans) { find.mFoundNode->addChild(trans); trans->addChild(toAttach); return trans; } else { find.mFoundNode->addChild(toAttach); return toAttach; } } } } openmw-openmw-0.38.0/components/sceneutil/attach.hpp000066400000000000000000000015711264522266000225600ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SCENEUTIL_ATTACH_H #define OPENMW_COMPONENTS_SCENEUTIL_ATTACH_H #include #include namespace osg { class Node; } namespace SceneUtil { /// Attach parts of the \a toAttach scenegraph to the \a master scenegraph, using the specified filter and attachment node. /// If the \a toAttach scene graph contains skinned objects, we will attach only those (filtered by the \a filter). /// Otherwise, just attach all of the toAttach scenegraph to the attachment node on the master scenegraph, with no filtering. /// @note The master scene graph is expected to include a skeleton. /// @return A newly created node that is directly attached to the master scene graph osg::ref_ptr attach(osg::ref_ptr toAttach, osg::Node* master, const std::string& filter, const std::string& attachNode); } #endif openmw-openmw-0.38.0/components/sceneutil/clone.cpp000066400000000000000000000113071264522266000224050ustar00rootroot00000000000000#include "clone.hpp" #include #include #include #include #include #include #include #include namespace SceneUtil { CopyOp::CopyOp() { setCopyFlags(osg::CopyOp::DEEP_COPY_NODES // Controller might need different inputs per scene instance | osg::CopyOp::DEEP_COPY_CALLBACKS | osg::CopyOp::DEEP_COPY_USERDATA); } osg::StateSet* CopyOp::operator ()(const osg::StateSet* stateset) const { if (!stateset) return NULL; if (stateset->getDataVariance() == osg::StateSet::DYNAMIC) return osg::clone(stateset, *this); return const_cast(stateset); } osg::Node* CopyOp::operator ()(const osg::Node* node) const { if (const osgParticle::ParticleProcessor* processor = dynamic_cast(node)) return operator()(processor); if (const osgParticle::ParticleSystemUpdater* updater = dynamic_cast(node)) { osgParticle::ParticleSystemUpdater* cloned = osg::clone(updater, *this); mMap2[cloned] = updater->getParticleSystem(0); return cloned; } return osg::CopyOp::operator()(node); } osg::Drawable* CopyOp::operator ()(const osg::Drawable* drawable) const { if (const osgParticle::ParticleSystem* partsys = dynamic_cast(drawable)) return operator()(partsys); if (dynamic_cast(drawable)) { osg::CopyOp copyop = *this; copyop.setCopyFlags(copyop.getCopyFlags()|osg::CopyOp::DEEP_COPY_ARRAYS); #if OSG_VERSION_LESS_THAN(3,5,0) /* Deep copy of primitives required to work around the following (bad?) code in osg::Geometry copy constructor: if ((copyop.getCopyFlags() & osg::CopyOp::DEEP_COPY_ARRAYS)) { if (_useVertexBufferObjects) { // copying of arrays doesn't set up buffer objects so we'll need to force // Geometry to assign these, we'll do this by switching off VBO's then renabling them. setUseVertexBufferObjects(false); setUseVertexBufferObjects(true); } } In case of DEEP_COPY_PRIMITIVES=Off, DEEP_COPY_ARRAYS=On, the above code makes a modification to the original const Geometry& we copied from, causing problems if we relied on the original Geometry to remain static such as when it was added to an osgUtil::IncrementalCompileOperation. Fixed in OSG 3.5 ( http://forum.openscenegraph.org/viewtopic.php?t=15217 ). */ copyop.setCopyFlags(copyop.getCopyFlags()|osg::CopyOp::DEEP_COPY_PRIMITIVES); #endif osg::Drawable* cloned = osg::clone(drawable, copyop); if (cloned->getUpdateCallback()) cloned->setUpdateCallback(osg::clone(cloned->getUpdateCallback(), *this)); return cloned; } if (dynamic_cast(drawable)) { return osg::clone(drawable, *this); } return osg::CopyOp::operator()(drawable); } osgParticle::ParticleProcessor* CopyOp::operator() (const osgParticle::ParticleProcessor* processor) const { osgParticle::ParticleProcessor* cloned = osg::clone(processor, *this); mMap[cloned] = processor->getParticleSystem(); return cloned; } osgParticle::ParticleSystem* CopyOp::operator ()(const osgParticle::ParticleSystem* partsys) const { osgParticle::ParticleSystem* cloned = osg::clone(partsys, *this); for (std::map::const_iterator it = mMap.begin(); it != mMap.end(); ++it) { if (it->second == partsys) { it->first->setParticleSystem(cloned); } } for (std::map::const_iterator it = mMap2.begin(); it != mMap2.end(); ++it) { if (it->second == partsys) { osgParticle::ParticleSystemUpdater* updater = it->first; updater->removeParticleSystem(updater->getParticleSystem(0)); updater->addParticleSystem(cloned); } } return cloned; } } openmw-openmw-0.38.0/components/sceneutil/clone.hpp000066400000000000000000000030601264522266000224070ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SCENEUTIL_CLONE_H #define OPENMW_COMPONENTS_SCENEUTIL_CLONE_H #include #include namespace osgParticle { class ParticleProcessor; class ParticleSystem; class ParticleSystemUpdater; } namespace SceneUtil { /// @par Defines the cloning behaviour we need: /// * Assigns updated ParticleSystem pointers on cloned emitters and programs. /// * Creates deep copy of StateSets if they have a DYNAMIC data variance. /// * Deep copies RigGeometry and MorphGeometry so they can animate without affecting clones. /// @warning Do not use an object of this class for more than one copy operation. class CopyOp : public osg::CopyOp { public: CopyOp(); virtual osgParticle::ParticleSystem* operator() (const osgParticle::ParticleSystem* partsys) const; virtual osgParticle::ParticleProcessor* operator() (const osgParticle::ParticleProcessor* processor) const; virtual osg::Node* operator() (const osg::Node* node) const; virtual osg::Drawable* operator() (const osg::Drawable* drawable) const; virtual osg::StateSet* operator() (const osg::StateSet* stateset) const; private: // maps new ParticleProcessor to their old ParticleSystem pointer // a little messy, but I think this should be the most efficient way mutable std::map mMap; mutable std::map mMap2; }; } #endif openmw-openmw-0.38.0/components/sceneutil/controller.cpp000066400000000000000000000071451264522266000234750ustar00rootroot00000000000000#include "controller.hpp" #include "statesetupdater.hpp" #include #include #include #include namespace SceneUtil { Controller::Controller() { } bool Controller::hasInput() const { return mSource.get() != NULL; } float Controller::getInputValue(osg::NodeVisitor* nv) { if (mFunction) return mFunction->calculate(mSource->getValue(nv)); else return mSource->getValue(nv); } void Controller::setSource(boost::shared_ptr source) { mSource = source; } void Controller::setFunction(boost::shared_ptr function) { mFunction = function; } boost::shared_ptr Controller::getSource() const { return mSource; } boost::shared_ptr Controller::getFunction() const { return mFunction; } FrameTimeSource::FrameTimeSource() { } float FrameTimeSource::getValue(osg::NodeVisitor *nv) { return nv->getFrameStamp()->getSimulationTime(); } ControllerVisitor::ControllerVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { } void ControllerVisitor::apply(osg::Node &node) { #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) osg::Callback* callback = node.getUpdateCallback(); #else osg::NodeCallback* callback = node.getUpdateCallback(); #endif while (callback) { if (Controller* ctrl = dynamic_cast(callback)) visit(node, *ctrl); if (CompositeStateSetUpdater* composite = dynamic_cast(callback)) { for (unsigned int i=0; igetNumControllers(); ++i) { StateSetUpdater* statesetcontroller = composite->getController(i); if (Controller* ctrl = dynamic_cast(statesetcontroller)) visit(node, *ctrl); } } callback = callback->getNestedCallback(); } traverse(node); } void ControllerVisitor::apply(osg::Geode &geode) { for (unsigned int i=0; igetUpdateCallback(); #else osg::Drawable::UpdateCallback* callback = drw->getUpdateCallback(); #endif if (Controller* ctrl = dynamic_cast(callback)) visit(geode, *ctrl); } apply(static_cast(geode)); } AssignControllerSourcesVisitor::AssignControllerSourcesVisitor() : ControllerVisitor() { } AssignControllerSourcesVisitor::AssignControllerSourcesVisitor(boost::shared_ptr toAssign) : ControllerVisitor() , mToAssign(toAssign) { } void AssignControllerSourcesVisitor::visit(osg::Node&, Controller &ctrl) { if (!ctrl.getSource()) ctrl.setSource(mToAssign); } FindMaxControllerLengthVisitor::FindMaxControllerLengthVisitor() : SceneUtil::ControllerVisitor() , mMaxLength(0) { } void FindMaxControllerLengthVisitor::visit(osg::Node &, Controller &ctrl) { if (ctrl.getFunction()) mMaxLength = std::max(mMaxLength, ctrl.getFunction()->getMaximum()); } float FindMaxControllerLengthVisitor::getMaxLength() const { return mMaxLength; } } openmw-openmw-0.38.0/components/sceneutil/controller.hpp000066400000000000000000000056621264522266000235040ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SCENEUTIL_CONTROLLER_H #define OPENMW_COMPONENTS_SCENEUTIL_CONTROLLER_H #include #include namespace SceneUtil { class ControllerSource { public: virtual ~ControllerSource() { } virtual float getValue(osg::NodeVisitor* nv) = 0; }; class FrameTimeSource : public ControllerSource { public: FrameTimeSource(); virtual float getValue(osg::NodeVisitor* nv); }; /// @note ControllerFunctions may be shared - you should not hold any state in it. That is why all its methods are declared const. class ControllerFunction { public: virtual float calculate(float input) const = 0; /// Get the "stop time" of the controller function, typically the maximum of the calculate() function. /// May not be meaningful for all types of controller functions. virtual float getMaximum() const = 0; }; class Controller { public: Controller(); virtual ~Controller() {} bool hasInput() const; float getInputValue(osg::NodeVisitor* nv); void setSource(boost::shared_ptr source); void setFunction(boost::shared_ptr function); boost::shared_ptr getSource() const; boost::shared_ptr getFunction() const; private: boost::shared_ptr mSource; // The source value gets passed through this function before it's passed on to the DestValue. boost::shared_ptr mFunction; }; /// Pure virtual base class - visit() all controllers that are attached as UpdateCallbacks in a scene graph. class ControllerVisitor : public osg::NodeVisitor { public: ControllerVisitor(); virtual void apply(osg::Node& node); virtual void apply(osg::Geode& geode); virtual void visit(osg::Node& node, Controller& ctrl) = 0; }; class AssignControllerSourcesVisitor : public ControllerVisitor { public: AssignControllerSourcesVisitor(); AssignControllerSourcesVisitor(boost::shared_ptr toAssign); /// Assign the wanted ControllerSource. May be overridden in derived classes. /// By default assigns the ControllerSource passed to the constructor of this class if no ControllerSource is assigned to that controller yet. virtual void visit(osg::Node& node, Controller& ctrl); private: boost::shared_ptr mToAssign; }; /// Finds the maximum of all controller functions in the given scene graph class FindMaxControllerLengthVisitor : public ControllerVisitor { public: FindMaxControllerLengthVisitor(); virtual void visit(osg::Node& , Controller& ctrl); float getMaxLength() const; private: float mMaxLength; }; } #endif openmw-openmw-0.38.0/components/sceneutil/lightcontroller.cpp000066400000000000000000000073201264522266000245200ustar00rootroot00000000000000#include "lightcontroller.hpp" #include #include #include #include namespace { float pulseAmplitude(float time) { return std::sin(time); } float flickerAmplitude(float time) { static const float fb = 1.17024f; static const float f[3] = { 1.5708f, 4.18774f, 5.19934f }; static const float o[3] = { 0.804248f, 2.11115f, 3.46832f }; static const float m[3] = { 1.0f, 0.785f, 0.876f }; static const float s = 0.394f; float v = 0.0f; for(int i = 0;i < 3;++i) v += std::sin(fb*time*f[i] + o[1])*m[i]; return v * s; } float flickerFrequency(float phase) { static const float fa = 0.785398f; static const float tdo = 0.94f; static const float tdm = 2.48f; return tdo + tdm*std::sin(fa * phase); } } namespace SceneUtil { LightController::LightController() : mType(LT_Normal) , mPhase((Misc::Rng::rollClosedProbability() * 2.f - 1.f) * 500.f) , mDeltaCount(0.f) , mDirection(1.f) , mLastTime(0.0) { } void LightController::setType(LightController::LightType type) { mType = type; } void LightController::operator ()(osg::Node* node, osg::NodeVisitor* nv) { double time = nv->getFrameStamp()->getSimulationTime(); // disabled early out, light state needs to be set every frame regardless of change, due to the double buffering //if (time == mLastTime) // return; float dt = static_cast(time - mLastTime); mLastTime = time; float brightness = 1.0f; float cycle_time; float time_distortion; const float pi = 3.14159265359; if(mType == LT_Pulse || mType == LT_PulseSlow) { cycle_time = 2.0f * pi; time_distortion = mType == LT_Pulse ? 20.0f : 4.f; } else { static const float fa = 0.785398f; static const float phase_wavelength = 120.0f * pi / fa; cycle_time = 500.0f; mPhase = std::fmod(mPhase + dt, phase_wavelength); time_distortion = flickerFrequency(mPhase); } mDeltaCount += mDirection*dt*time_distortion; if(mDirection > 0 && mDeltaCount > +cycle_time) { mDirection = -1.0f; mDeltaCount = 2.0f*cycle_time - mDeltaCount; } if(mDirection < 0 && mDeltaCount < -cycle_time) { mDirection = +1.0f; mDeltaCount = -2.0f*cycle_time - mDeltaCount; } static const float fast = 4.0f/1.0f; static const float slow = 1.0f/1.0f; // These formulas are just guesswork, but they work pretty well if(mType == LT_Normal) { // Less than 1/255 light modifier for a constant light: brightness = 1.0f + flickerAmplitude(mDeltaCount*slow)/255.0f; } else if(mType == LT_Flicker) brightness = 0.75f + flickerAmplitude(mDeltaCount*fast)*0.25f; else if(mType == LT_FlickerSlow) brightness = 0.75f + flickerAmplitude(mDeltaCount*slow)*0.25f; else if(mType == LT_Pulse) brightness = 0.7f + pulseAmplitude(mDeltaCount*fast)*0.3f; else if(mType == LT_PulseSlow) brightness = 0.7f + pulseAmplitude(mDeltaCount*slow)*0.3f; static_cast(node)->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * brightness); traverse(node, nv); } void LightController::setDiffuse(const osg::Vec4f& color) { mDiffuseColor = color; } } openmw-openmw-0.38.0/components/sceneutil/lightcontroller.hpp000066400000000000000000000016331264522266000245260ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SCENEUTIL_LIGHTCONTROLLER_H #define OPENMW_COMPONENTS_SCENEUTIL_LIGHTCONTROLLER_H #include #include namespace SceneUtil { /// @brief Controller class to handle a pulsing and/or flickering light /// @note Must be set on a SceneUtil::LightSource. class LightController : public osg::NodeCallback { public: enum LightType { LT_Normal, LT_Flicker, LT_FlickerSlow, LT_Pulse, LT_PulseSlow }; LightController(); void setType(LightType type); void setDiffuse(const osg::Vec4f& color); virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); private: LightType mType; osg::Vec4f mDiffuseColor; float mPhase; float mDeltaCount; int mDirection; double mLastTime; }; } #endif openmw-openmw-0.38.0/components/sceneutil/lightmanager.cpp000066400000000000000000000323401264522266000237470ustar00rootroot00000000000000#include "lightmanager.hpp" #include #include #include #include #include #include namespace SceneUtil { // Resets the modelview matrix to just the view matrix before applying lights. class LightStateAttribute : public osg::StateAttribute { public: LightStateAttribute() : mIndex(0) {} LightStateAttribute(unsigned int index, const std::vector >& lights) : mIndex(index), mLights(lights) {} LightStateAttribute(const LightStateAttribute& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY) : osg::StateAttribute(copy,copyop), mIndex(copy.mIndex), mLights(copy.mLights) {} unsigned int getMember() const { return mIndex; } virtual bool getModeUsage(ModeUsage & usage) const { for (unsigned int i=0; isetLightNum(i+mIndex); mLights[i]->apply(state); } state.applyModelViewMatrix(modelViewMatrix); } private: unsigned int mIndex; std::vector > mLights; }; LightManager* findLightManager(const osg::NodePath& path) { for (unsigned int i=0;i(path[i])) return lightManager; } return NULL; } // Set on a LightSource. Adds the light source to its light manager for the current frame. // This allows us to keep track of the current lights in the scene graph without tying creation & destruction to the manager. class CollectLightCallback : public osg::NodeCallback { public: CollectLightCallback() : mLightManager(0) { } CollectLightCallback(const CollectLightCallback& copy, const osg::CopyOp& copyop) : osg::NodeCallback(copy, copyop) , mLightManager(0) { } META_Object(SceneUtil, SceneUtil::CollectLightCallback) virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { if (!mLightManager) { mLightManager = findLightManager(nv->getNodePath()); if (!mLightManager) throw std::runtime_error("can't find parent LightManager"); } mLightManager->addLight(static_cast(node), osg::computeLocalToWorld(nv->getNodePath()), nv->getTraversalNumber()); traverse(node, nv); } private: LightManager* mLightManager; }; // Set on a LightManager. Clears the data from the previous frame. class LightManagerUpdateCallback : public osg::NodeCallback { public: LightManagerUpdateCallback() { } LightManagerUpdateCallback(const LightManagerUpdateCallback& copy, const osg::CopyOp& copyop) : osg::NodeCallback(copy, copyop) { } META_Object(SceneUtil, SceneUtil::LightManagerUpdateCallback) virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { LightManager* lightManager = static_cast(node); lightManager->update(); traverse(node, nv); } }; LightManager::LightManager() : mStartLight(0) , mLightingMask(~0u) { setUpdateCallback(new LightManagerUpdateCallback); } LightManager::LightManager(const LightManager ©, const osg::CopyOp ©op) : osg::Group(copy, copyop) , mStartLight(copy.mStartLight) , mLightingMask(copy.mLightingMask) { } void LightManager::setLightingMask(unsigned int mask) { mLightingMask = mask; } unsigned int LightManager::getLightingMask() const { return mLightingMask; } void LightManager::update() { mLights.clear(); mLightsInViewSpace.clear(); // do an occasional cleanup for orphaned lights for (int i=0; i<2; ++i) { if (mStateSetCache[i].size() > 5000) mStateSetCache[i].clear(); } } void LightManager::addLight(LightSource* lightSource, const osg::Matrixf& worldMat, unsigned int frameNum) { LightSourceTransform l; l.mLightSource = lightSource; l.mWorldMatrix = worldMat; lightSource->getLight(frameNum)->setPosition(osg::Vec4f(worldMat.getTrans().x(), worldMat.getTrans().y(), worldMat.getTrans().z(), 1.f)); mLights.push_back(l); } osg::ref_ptr LightManager::getLightListStateSet(const LightList &lightList, unsigned int frameNum) { // possible optimization: return a StateSet containing all requested lights plus some extra lights (if a suitable one exists) size_t hash = 0; for (unsigned int i=0; imLightSource->getId()); LightStateSetMap& stateSetCache = mStateSetCache[frameNum%2]; LightStateSetMap::iterator found = stateSetCache.find(hash); if (found != stateSetCache.end()) return found->second; else { std::vector > lights; for (unsigned int i=0; imLightSource->getLight(frameNum)); osg::ref_ptr attr = new LightStateAttribute(mStartLight, lights); osg::ref_ptr stateset = new osg::StateSet; // don't use setAttributeAndModes, that does not support light indices! stateset->setAttribute(attr, osg::StateAttribute::ON); stateset->setAssociatedModes(attr, osg::StateAttribute::ON); stateSetCache.insert(std::make_pair(hash, stateset)); return stateset; } } const std::vector& LightManager::getLights() const { return mLights; } const std::vector& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix) { osg::observer_ptr camPtr (camera); std::map, LightSourceViewBoundCollection>::iterator it = mLightsInViewSpace.find(camPtr); if (it == mLightsInViewSpace.end()) { it = mLightsInViewSpace.insert(std::make_pair(camPtr, LightSourceViewBoundCollection())).first; for (std::vector::iterator lightIt = mLights.begin(); lightIt != mLights.end(); ++lightIt) { osg::Matrixf worldViewMat = lightIt->mWorldMatrix * (*viewMatrix); osg::BoundingSphere viewBound = osg::BoundingSphere(osg::Vec3f(0,0,0), lightIt->mLightSource->getRadius()); transformBoundingSphere(worldViewMat, viewBound); LightSourceViewBound l; l.mLightSource = lightIt->mLightSource; l.mViewBound = viewBound; it->second.push_back(l); } } return it->second; } void LightManager::setStartLight(int start) { mStartLight = start; } int LightManager::getStartLight() const { return mStartLight; } static int sLightId = 0; LightSource::LightSource() : mRadius(0.f) { setUpdateCallback(new CollectLightCallback); mId = sLightId++; } LightSource::LightSource(const LightSource ©, const osg::CopyOp ©op) : osg::Node(copy, copyop) , mRadius(copy.mRadius) { mId = sLightId++; for (int i=0; i<2; ++i) mLight[i] = osg::clone(copy.mLight[i].get(), copyop); } bool sortLights (const LightManager::LightSourceViewBound* left, const LightManager::LightSourceViewBound* right) { return left->mViewBound.center().length2() - left->mViewBound.radius2()/4.f < right->mViewBound.center().length2() - right->mViewBound.radius2()/4.f; } void LightListCallback::operator()(osg::Node *node, osg::NodeVisitor *nv) { osgUtil::CullVisitor* cv = static_cast(nv); if (!mLightManager) { mLightManager = findLightManager(nv->getNodePath()); if (!mLightManager) { traverse(node, nv); return; } } if (!(cv->getCurrentCamera()->getCullMask() & mLightManager->getLightingMask())) { traverse(node, nv); return; } // Possible optimizations: // - cull list of lights by the camera frustum // - organize lights in a quad tree // update light list if necessary // makes sure we don't update it more than once per frame when rendering with multiple cameras if (mLastFrameNumber != nv->getTraversalNumber()) { mLastFrameNumber = nv->getTraversalNumber(); // Don't use Camera::getViewMatrix, that one might be relative to another camera! const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix(); const std::vector& lights = mLightManager->getLightsInViewSpace(cv->getCurrentCamera(), viewMatrix); // we do the intersections in view space osg::BoundingSphere nodeBound = node->getBound(); osg::Matrixf mat = *cv->getModelViewMatrix(); transformBoundingSphere(mat, nodeBound); mLightList.clear(); for (unsigned int i=0; i (8 - mLightManager->getStartLight()); osg::StateSet* stateset = NULL; if (mLightList.size() > maxLights) { // remove lights culled by this camera LightManager::LightList lightList = mLightList; for (LightManager::LightList::iterator it = lightList.begin(); it != lightList.end() && lightList.size() > maxLights; ) { osg::CullStack::CullingStack& stack = cv->getModelViewCullingStack(); osg::BoundingSphere bs = (*it)->mViewBound; bs._radius = bs._radius*2; osg::CullingSet& cullingSet = stack.front(); if (cullingSet.isCulled(bs)) { it = lightList.erase(it); continue; } else ++it; } if (lightList.size() > maxLights) { // sort by proximity to camera, then get rid of furthest away lights std::sort(lightList.begin(), lightList.end(), sortLights); while (lightList.size() > maxLights) lightList.pop_back(); } stateset = mLightManager->getLightListStateSet(lightList, nv->getTraversalNumber()); } else stateset = mLightManager->getLightListStateSet(mLightList, nv->getTraversalNumber()); cv->pushStateSet(stateset); traverse(node, nv); cv->popStateSet(); } else traverse(node, nv); } void configureLight(osg::Light *light, float radius, bool isExterior, bool outQuadInLin, bool useQuadratic, float quadraticValue, float quadraticRadiusMult, bool useLinear, float linearRadiusMult, float linearValue) { bool quadratic = useQuadratic && (!outQuadInLin || isExterior); float quadraticAttenuation = 0; float linearAttenuation = 0; if (quadratic) { float r = radius * quadraticRadiusMult; quadraticAttenuation = quadraticValue / std::pow(r, 2); } if (useLinear) { float r = radius * linearRadiusMult; linearAttenuation = linearValue / r; } light->setLinearAttenuation(linearAttenuation); light->setQuadraticAttenuation(quadraticAttenuation); light->setConstantAttenuation(0.f); } } openmw-openmw-0.38.0/components/sceneutil/lightmanager.hpp000066400000000000000000000157041264522266000237610ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H #define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H #include #include #include namespace SceneUtil { /// LightSource managed by a LightManager. /// @par Typically used for point lights. Spot lights are not supported yet. Directional lights affect the whole scene /// so do not need to be managed by a LightManager - so for directional lights use a plain osg::LightSource instead. /// @note LightSources must be decorated by a LightManager node in order to have an effect. Typical use would /// be one LightManager as the root of the scene graph. /// @note One needs to attach LightListCallback's to the scene to have objects receive lighting from LightSources. /// See the documentation of LightListCallback for more information. /// @note The position of the contained osg::Light is automatically updated based on the LightSource's world position. class LightSource : public osg::Node { // double buffered osg::Light's, since one of them may be in use by the draw thread at any given time osg::ref_ptr mLight[2]; // LightSource will affect objects within this radius float mRadius; int mId; public: META_Node(SceneUtil, SceneUtil::LightSource) LightSource(); LightSource(const LightSource& copy, const osg::CopyOp& copyop); float getRadius() const { return mRadius; } /// The LightSource will affect objects within this radius. void setRadius(float radius) { mRadius = radius; } /// Get the osg::Light safe for modification in the given frame. /// @par May be used externally to animate the light's color/attenuation properties, /// and is used internally to synchronize the light's position with the position of the LightSource. osg::Light* getLight(unsigned int frame) { return mLight[frame % 2]; } /// @warning It is recommended not to replace an existing osg::Light, because there might still be /// references to it in the light StateSet cache that are associated with this LightSource's ID. /// These references will stay valid due to ref_ptr but will point to the old object. /// @warning Do not modify the \a light after you've called this function. void setLight(osg::Light* light) { mLight[0] = light; mLight[1] = osg::clone(light); } /// Get the unique ID for this light source. int getId() const { return mId; } }; /// @brief Decorator node implementing the rendering of any number of LightSources that can be anywhere in the subgraph. class LightManager : public osg::Group { public: META_Node(SceneUtil, SceneUtil::LightManager) LightManager(); LightManager(const LightManager& copy, const osg::CopyOp& copyop); /// @param mask This mask is compared with the current Camera's cull mask to determine if lighting is desired. /// By default, it's ~0u i.e. always on. /// If you have some views that do not require lighting, then set the Camera's cull mask to not include /// the lightingMask for a much faster cull and rendering. void setLightingMask (unsigned int mask); unsigned int getLightingMask() const; /// Set the first light index that should be used by this manager, typically the number of directional lights in the scene. void setStartLight(int start); int getStartLight() const; /// Internal use only, called automatically by the LightManager's UpdateCallback void update(); /// Internal use only, called automatically by the LightSource's UpdateCallback void addLight(LightSource* lightSource, const osg::Matrixf& worldMat, unsigned int frameNum); struct LightSourceTransform { LightSource* mLightSource; osg::Matrixf mWorldMatrix; }; const std::vector& getLights() const; struct LightSourceViewBound { LightSource* mLightSource; osg::BoundingSphere mViewBound; }; const std::vector& getLightsInViewSpace(osg::Camera* camera, const osg::RefMatrix* viewMatrix); typedef std::vector LightList; osg::ref_ptr getLightListStateSet(const LightList& lightList, unsigned int frameNum); private: // Lights collected from the scene graph. Only valid during the cull traversal. std::vector mLights; typedef std::vector LightSourceViewBoundCollection; std::map, LightSourceViewBoundCollection> mLightsInViewSpace; // < Light list hash , StateSet > typedef std::map > LightStateSetMap; LightStateSetMap mStateSetCache[2]; int mStartLight; unsigned int mLightingMask; }; /// To receive lighting, objects must be decorated by a LightListCallback. Light list callbacks must be added via /// node->addCullCallback(new LightListCallback). Once a light list callback is added to a node, that node and all /// its child nodes can receive lighting. /// @par The placement of these LightListCallbacks affects the granularity of light lists. Having too fine grained /// light lists can result in degraded performance. Too coarse grained light lists can result in lights no longer /// rendering when the size of a light list exceeds the OpenGL limit on the number of concurrent lights (8). A good /// starting point is to attach a LightListCallback to each game object's base node. /// @note Not thread safe for CullThreadPerCamera threading mode. class LightListCallback : public osg::NodeCallback { public: LightListCallback() : mLightManager(NULL) , mLastFrameNumber(0) {} LightListCallback(const LightListCallback& copy, const osg::CopyOp& copyop) : osg::Object(copy, copyop), osg::NodeCallback(copy, copyop) , mLightManager(copy.mLightManager) , mLastFrameNumber(0) {} META_Object(SceneUtil, LightListCallback) void operator()(osg::Node* node, osg::NodeVisitor* nv); private: LightManager* mLightManager; unsigned int mLastFrameNumber; LightManager::LightList mLightList; }; /// @brief Configures a light's attenuation according to vanilla Morrowind attenuation settings. void configureLight(osg::Light* light, float radius, bool isExterior, bool outQuadInLin, bool useQuadratic, float quadraticValue, float quadraticRadiusMult, bool useLinear, float linearRadiusMult, float linearValue); } #endif openmw-openmw-0.38.0/components/sceneutil/positionattitudetransform.cpp000066400000000000000000000024211264522266000266460ustar00rootroot00000000000000#include "positionattitudetransform.hpp" #include namespace SceneUtil { PositionAttitudeTransform::PositionAttitudeTransform(): _scale(1.0,1.0,1.0) { } bool PositionAttitudeTransform::computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor*) const { if (_referenceFrame==RELATIVE_RF) { matrix.preMultTranslate(_position); matrix.preMultRotate(_attitude); matrix.preMultScale(_scale); } else // absolute { matrix.makeRotate(_attitude); matrix.postMultTranslate(_position); matrix.preMultScale(_scale); } return true; } bool PositionAttitudeTransform::computeWorldToLocalMatrix(osg::Matrix& matrix, osg::NodeVisitor*) const { if (_scale.x() == 0.0 || _scale.y() == 0.0 || _scale.z() == 0.0) return false; if (_referenceFrame==RELATIVE_RF) { matrix.postMultTranslate(-_position); matrix.postMultRotate(_attitude.inverse()); matrix.postMultScale(osg::Vec3f(1.0/_scale.x(), 1.0/_scale.y(), 1.0/_scale.z())); } else // absolute { matrix.makeRotate(_attitude.inverse()); matrix.preMultTranslate(-_position); matrix.postMultScale(osg::Vec3f(1.0/_scale.x(), 1.0/_scale.y(), 1.0/_scale.z())); } return true; } } openmw-openmw-0.38.0/components/sceneutil/positionattitudetransform.hpp000066400000000000000000000031221264522266000266520ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_POSITIONATTITUDE_TRANSFORM_H #define OPENMW_COMPONENTS_POSITIONATTITUDE_TRANSFORM_H #include namespace SceneUtil { /// @brief A customized version of osg::PositionAttitudeTransform optimized for speed. /// Uses single precision values. Also removed _pivotPoint which we don't need. class PositionAttitudeTransform : public osg::Transform { public : PositionAttitudeTransform(); PositionAttitudeTransform(const PositionAttitudeTransform& pat,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY): Transform(pat,copyop), _position(pat._position), _attitude(pat._attitude), _scale(pat._scale){} META_Node(SceneUtil, PositionAttitudeTransform) inline void setPosition(const osg::Vec3f& pos) { _position = pos; dirtyBound(); } inline const osg::Vec3f& getPosition() const { return _position; } inline void setAttitude(const osg::Quat& quat) { _attitude = quat; dirtyBound(); } inline const osg::Quat& getAttitude() const { return _attitude; } inline void setScale(const osg::Vec3f& scale) { _scale = scale; dirtyBound(); } inline const osg::Vec3f& getScale() const { return _scale; } virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const; virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const; protected : virtual ~PositionAttitudeTransform() {} osg::Vec3f _position; osg::Quat _attitude; osg::Vec3f _scale; }; } #endif openmw-openmw-0.38.0/components/sceneutil/riggeometry.cpp000066400000000000000000000230771264522266000236510ustar00rootroot00000000000000#include "riggeometry.hpp" #include #include #include #include #include #include "skeleton.hpp" #include "util.hpp" namespace SceneUtil { class UpdateRigBounds : public osg::Drawable::UpdateCallback { public: UpdateRigBounds() { } UpdateRigBounds(const UpdateRigBounds& copy, const osg::CopyOp& copyop) : osg::Drawable::UpdateCallback(copy, copyop) { } META_Object(SceneUtil, UpdateRigBounds) void update(osg::NodeVisitor* nv, osg::Drawable* drw) { RigGeometry* rig = static_cast(drw); rig->updateBounds(nv); } }; // TODO: make threadsafe for multiple cull threads class UpdateRigGeometry : public osg::Drawable::CullCallback { public: UpdateRigGeometry() { } UpdateRigGeometry(const UpdateRigGeometry& copy, const osg::CopyOp& copyop) : osg::Drawable::CullCallback(copy, copyop) { } META_Object(SceneUtil, UpdateRigGeometry) virtual bool cull(osg::NodeVisitor* nv, osg::Drawable* drw, osg::State*) const { RigGeometry* geom = static_cast(drw); geom->update(nv); return false; } }; // We can't compute the bounds without a NodeVisitor, since we need the current geomToSkelMatrix. // So we return nothing. Bounds are updated every frame in the UpdateCallback. class DummyComputeBoundCallback : public osg::Drawable::ComputeBoundingBoxCallback { public: virtual osg::BoundingBox computeBound(const osg::Drawable&) const { return osg::BoundingBox(); } }; RigGeometry::RigGeometry() : mSkeleton(NULL) , mLastFrameNumber(0) , mBoundsFirstFrame(true) { setCullCallback(new UpdateRigGeometry); setUpdateCallback(new UpdateRigBounds); setSupportsDisplayList(false); setComputeBoundingBoxCallback(new DummyComputeBoundCallback); } RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) : osg::Geometry(copy, copyop) , mSkeleton(NULL) , mInfluenceMap(copy.mInfluenceMap) , mLastFrameNumber(0) , mBoundsFirstFrame(true) { setSourceGeometry(copy.mSourceGeometry); } void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) { mSourceGeometry = sourceGeometry; osg::Geometry& from = *sourceGeometry; if (from.getStateSet()) setStateSet(from.getStateSet()); // copy over primitive sets. getPrimitiveSetList() = from.getPrimitiveSetList(); if (from.getColorArray()) setColorArray(from.getColorArray()); if (from.getSecondaryColorArray()) setSecondaryColorArray(from.getSecondaryColorArray()); if (from.getFogCoordArray()) setFogCoordArray(from.getFogCoordArray()); for(unsigned int ti=0;ti(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL))); setNormalArray(dynamic_cast(from.getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL)), osg::Array::BIND_PER_VERTEX); } bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv) { const osg::NodePath& path = nv->getNodePath(); for (osg::NodePath::const_reverse_iterator it = path.rbegin(); it != path.rend(); ++it) { osg::Node* node = *it; if (Skeleton* skel = dynamic_cast(node)) { mSkeleton = skel; break; } } if (!mSkeleton) { std::cerr << "A RigGeometry did not find its parent skeleton" << std::endl; return false; } if (!mInfluenceMap) { std::cerr << "No InfluenceMap set on RigGeometry" << std::endl; return false; } typedef std::map > Vertex2BoneMap; Vertex2BoneMap vertex2BoneMap; for (std::map::const_iterator it = mInfluenceMap->mMap.begin(); it != mInfluenceMap->mMap.end(); ++it) { Bone* bone = mSkeleton->getBone(it->first); if (!bone) { std::cerr << "RigGeometry did not find bone " << it->first << std::endl; continue; } mBoneSphereMap[bone] = it->second.mBoundSphere; const BoneInfluence& bi = it->second; const std::map& weights = it->second.mWeights; for (std::map::const_iterator weightIt = weights.begin(); weightIt != weights.end(); ++weightIt) { std::vector& vec = vertex2BoneMap[weightIt->first]; BoneWeight b = std::make_pair(std::make_pair(bone, bi.mInvBindMatrix), weightIt->second); vec.push_back(b); } } for (Vertex2BoneMap::iterator it = vertex2BoneMap.begin(); it != vertex2BoneMap.end(); ++it) { mBone2VertexMap[it->second].push_back(it->first); } return true; } void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, float weight, osg::Matrixf& result) { osg::Matrixf m = invBindMatrix * matrix; float* ptr = m.ptr(); float* ptrresult = result.ptr(); ptrresult[0] += ptr[0] * weight; ptrresult[1] += ptr[1] * weight; ptrresult[2] += ptr[2] * weight; ptrresult[4] += ptr[4] * weight; ptrresult[5] += ptr[5] * weight; ptrresult[6] += ptr[6] * weight; ptrresult[8] += ptr[8] * weight; ptrresult[9] += ptr[9] * weight; ptrresult[10] += ptr[10] * weight; ptrresult[12] += ptr[12] * weight; ptrresult[13] += ptr[13] * weight; ptrresult[14] += ptr[14] * weight; } void RigGeometry::update(osg::NodeVisitor* nv) { if (!mSkeleton) { std::cerr << "RigGeometry rendering with no skeleton, should have been initialized by UpdateVisitor" << std::endl; // try to recover anyway, though rendering is likely to be incorrect. if (!initFromParentSkeleton(nv)) return; } if (!mSkeleton->getActive() && mLastFrameNumber != 0) return; if (mLastFrameNumber == nv->getTraversalNumber()) return; mLastFrameNumber = nv->getTraversalNumber(); mSkeleton->updateBoneMatrices(nv); // skinning osg::Vec3Array* positionSrc = static_cast(mSourceGeometry->getVertexArray()); osg::Vec3Array* normalSrc = static_cast(mSourceGeometry->getNormalArray()); osg::Vec3Array* positionDst = static_cast(getVertexArray()); osg::Vec3Array* normalDst = static_cast(getNormalArray()); for (Bone2VertexMap::const_iterator it = mBone2VertexMap.begin(); it != mBone2VertexMap.end(); ++it) { osg::Matrixf resultMat (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); for (std::vector::const_iterator weightIt = it->first.begin(); weightIt != it->first.end(); ++weightIt) { Bone* bone = weightIt->first.first; const osg::Matrix& invBindMatrix = weightIt->first.second; float weight = weightIt->second; const osg::Matrixf& boneMatrix = bone->mMatrixInSkeletonSpace; accumulateMatrix(invBindMatrix, boneMatrix, weight, resultMat); } resultMat = resultMat * mGeomToSkelMatrix; for (std::vector::const_iterator vertexIt = it->second.begin(); vertexIt != it->second.end(); ++vertexIt) { unsigned short vertex = *vertexIt; (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]); (*normalDst)[vertex] = osg::Matrix::transform3x3((*normalSrc)[vertex], resultMat); } } positionDst->dirty(); normalDst->dirty(); } void RigGeometry::updateBounds(osg::NodeVisitor *nv) { if (!mSkeleton) { if (!initFromParentSkeleton(nv)) return; } if (!mSkeleton->getActive() && !mBoundsFirstFrame) return; mBoundsFirstFrame = false; mSkeleton->updateBoneMatrices(nv); updateGeomToSkelMatrix(nv); osg::BoundingBox box; for (BoneSphereMap::const_iterator it = mBoneSphereMap.begin(); it != mBoneSphereMap.end(); ++it) { Bone* bone = it->first; osg::BoundingSpheref bs = it->second; transformBoundingSphere(bone->mMatrixInSkeletonSpace * mGeomToSkelMatrix, bs); box.expandBy(bs); } _boundingBox = box; _boundingBoxComputed = true; #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) // in OSG 3.3.3 and up Drawable inherits from Node, so has a bounding sphere as well. _boundingSphere = osg::BoundingSphere(_boundingBox); _boundingSphereComputed = true; #endif for (unsigned int i=0; idirtyBound(); } void RigGeometry::updateGeomToSkelMatrix(osg::NodeVisitor *nv) { mSkelToGeomPath.clear(); bool foundSkel = false; for (osg::NodePath::const_iterator it = nv->getNodePath().begin(); it != nv->getNodePath().end(); ++it) { if (!foundSkel) { if (*it == mSkeleton) foundSkel = true; } else mSkelToGeomPath.push_back(*it); } mGeomToSkelMatrix = osg::computeWorldToLocal(mSkelToGeomPath); } void RigGeometry::setInfluenceMap(osg::ref_ptr influenceMap) { mInfluenceMap = influenceMap; } } openmw-openmw-0.38.0/components/sceneutil/riggeometry.hpp000066400000000000000000000043051264522266000236470ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_NIFOSG_RIGGEOMETRY_H #define OPENMW_COMPONENTS_NIFOSG_RIGGEOMETRY_H #include #include namespace SceneUtil { class Skeleton; class Bone; /// @brief Mesh skinning implementation. /// @note A RigGeometry may be attached directly to a Skeleton, or somewhere below a Skeleton. /// Note though that the RigGeometry ignores any transforms below the Skeleton, so the attachment point is not that important. class RigGeometry : public osg::Geometry { public: RigGeometry(); RigGeometry(const RigGeometry& copy, const osg::CopyOp& copyop); META_Object(SceneUtil, RigGeometry) struct BoneInfluence { osg::Matrixf mInvBindMatrix; osg::BoundingSpheref mBoundSphere; // std::map mWeights; }; struct InfluenceMap : public osg::Referenced { std::map mMap; }; void setInfluenceMap(osg::ref_ptr influenceMap); void setSourceGeometry(osg::ref_ptr sourceGeom); // Called automatically by our CullCallback void update(osg::NodeVisitor* nv); // Called automatically by our UpdateCallback void updateBounds(osg::NodeVisitor* nv); private: osg::ref_ptr mSourceGeometry; Skeleton* mSkeleton; osg::NodePath mSkelToGeomPath; osg::Matrixf mGeomToSkelMatrix; osg::ref_ptr mInfluenceMap; typedef std::pair BoneBindMatrixPair; typedef std::pair BoneWeight; typedef std::vector VertexList; typedef std::map, VertexList> Bone2VertexMap; Bone2VertexMap mBone2VertexMap; typedef std::map BoneSphereMap; BoneSphereMap mBoneSphereMap; unsigned int mLastFrameNumber; bool mBoundsFirstFrame; bool initFromParentSkeleton(osg::NodeVisitor* nv); void updateGeomToSkelMatrix(osg::NodeVisitor* nv); }; } #endif openmw-openmw-0.38.0/components/sceneutil/skeleton.cpp000066400000000000000000000103341264522266000231300ustar00rootroot00000000000000#include "skeleton.hpp" #include #include #include #include namespace SceneUtil { class InitBoneCacheVisitor : public osg::NodeVisitor { public: InitBoneCacheVisitor(std::map >& cache) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mCache(cache) { } void apply(osg::Transform &node) { osg::MatrixTransform* bone = node.asMatrixTransform(); if (!bone) return; mCache[Misc::StringUtils::lowerCase(bone->getName())] = std::make_pair(getNodePath(), bone); traverse(node); } private: std::map >& mCache; }; Skeleton::Skeleton() : mBoneCacheInit(false) , mNeedToUpdateBoneMatrices(true) , mActive(true) , mLastFrameNumber(0) { } Skeleton::Skeleton(const Skeleton ©, const osg::CopyOp ©op) : osg::Group(copy, copyop) , mBoneCacheInit(false) , mNeedToUpdateBoneMatrices(true) , mActive(copy.mActive) , mLastFrameNumber(0) { } Bone* Skeleton::getBone(const std::string &name) { if (!mBoneCacheInit) { InitBoneCacheVisitor visitor(mBoneCache); accept(visitor); mBoneCacheInit = true; } BoneCache::iterator found = mBoneCache.find(Misc::StringUtils::lowerCase(name)); if (found == mBoneCache.end()) return NULL; // find or insert in the bone hierarchy if (!mRootBone.get()) { mRootBone.reset(new Bone); } const osg::NodePath& path = found->second.first; Bone* bone = mRootBone.get(); for (osg::NodePath::const_iterator it = path.begin(); it != path.end(); ++it) { osg::MatrixTransform* matrixTransform = dynamic_cast(*it); if (!matrixTransform) continue; Bone* child = NULL; for (unsigned int i=0; imChildren.size(); ++i) { if (bone->mChildren[i]->mNode == *it) { child = bone->mChildren[i]; break; } } if (!child) { child = new Bone; bone->mChildren.push_back(child); mNeedToUpdateBoneMatrices = true; } bone = child; bone->mNode = matrixTransform; } return bone; } void Skeleton::updateBoneMatrices(osg::NodeVisitor* nv) { if (nv->getTraversalNumber() != mLastFrameNumber) mNeedToUpdateBoneMatrices = true; mLastFrameNumber = nv->getTraversalNumber(); if (mNeedToUpdateBoneMatrices) { if (mRootBone.get()) { for (unsigned int i=0; imChildren.size(); ++i) mRootBone->mChildren[i]->update(NULL); } else std::cerr << "no root bone" << std::endl; mNeedToUpdateBoneMatrices = false; } } void Skeleton::setActive(bool active) { mActive = active; } bool Skeleton::getActive() const { return mActive; } void Skeleton::traverse(osg::NodeVisitor& nv) { if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR // need to process at least 2 frames before shutting off update, since we need to have both frame-alternating RigGeometries initialized // this would be more naturally handled if the double-buffering was implemented in RigGeometry itself rather than in a FrameSwitch decorator node && mLastFrameNumber != 0 && mLastFrameNumber+2 <= nv.getTraversalNumber()) return; osg::Group::traverse(nv); } Bone::Bone() : mNode(NULL) { } Bone::~Bone() { for (unsigned int i=0; igetMatrix() * (*parentMatrixInSkeletonSpace); else mMatrixInSkeletonSpace = mNode->getMatrix(); for (unsigned int i=0; iupdate(&mMatrixInSkeletonSpace); } } } openmw-openmw-0.38.0/components/sceneutil/skeleton.hpp000066400000000000000000000043441264522266000231410ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_NIFOSG_SKELETON_H #define OPENMW_COMPONENTS_NIFOSG_SKELETON_H #include #include namespace SceneUtil { /// @brief Defines a Bone hierarchy, used for updating of skeleton-space bone matrices. /// @note To prevent unnecessary updates, only bones that are used for skinning will be added to this hierarchy. class Bone { public: Bone(); ~Bone(); osg::Matrixf mMatrixInSkeletonSpace; osg::MatrixTransform* mNode; std::vector mChildren; /// Update the skeleton-space matrix of this bone and all its children. void update(const osg::Matrixf* parentMatrixInSkeletonSpace); private: Bone(const Bone&); void operator=(const Bone&); }; /// @brief Handles the bone matrices for any number of child RigGeometries. /// @par Bones should be created as osg::MatrixTransform children of the skeleton. /// To be a referenced by a RigGeometry, a bone needs to have a unique name. class Skeleton : public osg::Group { public: Skeleton(); Skeleton(const Skeleton& copy, const osg::CopyOp& copyop); META_Node(SceneUtil, Skeleton) /// Retrieve a bone by name. Bone* getBone(const std::string& name); /// Request an update of bone matrices. May be a no-op if already updated in this frame. void updateBoneMatrices(osg::NodeVisitor* nv); /// Set the skinning active flag. Inactive skeletons will not have their child rigs updated. /// You should set this flag to false if you know that bones are not currently moving. void setActive(bool active); bool getActive() const; void traverse(osg::NodeVisitor& nv); private: // The root bone is not a "real" bone, it has no corresponding node in the scene graph. // As far as the scene graph goes we support multiple root bones. std::auto_ptr mRootBone; typedef std::map > BoneCache; BoneCache mBoneCache; bool mBoneCacheInit; bool mNeedToUpdateBoneMatrices; bool mActive; unsigned int mLastFrameNumber; }; } #endif openmw-openmw-0.38.0/components/sceneutil/statesetupdater.cpp000066400000000000000000000046571264522266000245400ustar00rootroot00000000000000#include "statesetupdater.hpp" #include #include namespace SceneUtil { void StateSetUpdater::operator()(osg::Node* node, osg::NodeVisitor* nv) { if (!mStateSets[0]) { // first time setup osg::StateSet* src = node->getOrCreateStateSet(); for (int i=0; i<2; ++i) // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first // This can be done conveniently in user implementations of the setDefaults() method { mStateSets[i] = static_cast(osg::clone(src, osg::CopyOp::SHALLOW_COPY)); setDefaults(mStateSets[i]); } } osg::StateSet* stateset = mStateSets[nv->getTraversalNumber()%2]; node->setStateSet(stateset); apply(stateset, nv); traverse(node, nv); } void StateSetUpdater::reset() { mStateSets[0] = NULL; mStateSets[1] = NULL; } StateSetUpdater::StateSetUpdater() { } StateSetUpdater::StateSetUpdater(const StateSetUpdater ©, const osg::CopyOp ©op) : osg::NodeCallback(copy, copyop) { } // ---------------------------------------------------------------------------------- void CompositeStateSetUpdater::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) { for (unsigned int i=0; iapply(stateset, nv); } void CompositeStateSetUpdater::setDefaults(osg::StateSet *stateset) { for (unsigned int i=0; isetDefaults(stateset); } CompositeStateSetUpdater::CompositeStateSetUpdater() { } CompositeStateSetUpdater::CompositeStateSetUpdater(const CompositeStateSetUpdater ©, const osg::CopyOp ©op) : StateSetUpdater(copy, copyop) { for (unsigned int i=0; i(osg::clone(copy.mCtrls[i].get(), copyop))); } unsigned int CompositeStateSetUpdater::getNumControllers() { return mCtrls.size(); } StateSetUpdater* CompositeStateSetUpdater::getController(int i) { return mCtrls[i]; } void CompositeStateSetUpdater::addController(StateSetUpdater *ctrl) { mCtrls.push_back(ctrl); } } openmw-openmw-0.38.0/components/sceneutil/statesetupdater.hpp000066400000000000000000000055631264522266000245420ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SCENEUTIL_STATESETCONTROLLER_H #define OPENMW_COMPONENTS_SCENEUTIL_STATESETCONTROLLER_H #include namespace SceneUtil { /// @brief Implements efficient per-frame updating of StateSets. /// @par With a naive update there would be race conditions when the OSG draw thread of the last frame /// queues up a StateSet that we want to modify for the next frame. To solve this we could set the StateSet to /// DYNAMIC data variance but that would undo all the benefits of the threading model - having the cull and draw /// traversals run in parallel can yield up to 200% framerates. /// @par Race conditions are prevented using a "double buffering" scheme - we have two StateSets that take turns, /// one StateSet we can write to, the second one is currently in use by the draw traversal of the last frame. /// @par Must be set as UpdateCallback on a Node. /// @note Do not add the same StateSetUpdater to multiple nodes. /// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetUpdater. class StateSetUpdater : public osg::NodeCallback { public: StateSetUpdater(); StateSetUpdater(const StateSetUpdater& copy, const osg::CopyOp& copyop); META_Object(SceneUtil, StateSetUpdater) virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); /// Apply state - to override in derived classes /// @note Due to the double buffering approach you *have* to apply all state /// even if it has not changed since the last frame. virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) {} /// Set default state - optionally override in derived classes /// @par May be used e.g. to allocate StateAttributes. virtual void setDefaults(osg::StateSet* stateset) {} protected: /// Reset mStateSets, forcing a setDefaults() on the next frame. Can be used to change the defaults if needed. void reset(); private: osg::ref_ptr mStateSets[2]; }; /// @brief A variant of the StateSetController that can be made up of multiple controllers all controlling the same target. class CompositeStateSetUpdater : public StateSetUpdater { public: CompositeStateSetUpdater(); CompositeStateSetUpdater(const CompositeStateSetUpdater& copy, const osg::CopyOp& copyop); META_Object(SceneUtil, CompositeStateSetUpdater) unsigned int getNumControllers(); StateSetUpdater* getController(int i); void addController(StateSetUpdater* ctrl); virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv); protected: virtual void setDefaults(osg::StateSet *stateset); std::vector > mCtrls; }; } #endif openmw-openmw-0.38.0/components/sceneutil/util.cpp000066400000000000000000000030461264522266000222630ustar00rootroot00000000000000#include "util.hpp" namespace SceneUtil { void transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere) { osg::BoundingSphere::vec_type xdash = bsphere._center; xdash.x() += bsphere._radius; xdash = xdash*matrix; osg::BoundingSphere::vec_type ydash = bsphere._center; ydash.y() += bsphere._radius; ydash = ydash*matrix; osg::BoundingSphere::vec_type zdash = bsphere._center; zdash.z() += bsphere._radius; zdash = zdash*matrix; bsphere._center = bsphere._center*matrix; xdash -= bsphere._center; osg::BoundingSphere::value_type sqrlen_xdash = xdash.length2(); ydash -= bsphere._center; osg::BoundingSphere::value_type sqrlen_ydash = ydash.length2(); zdash -= bsphere._center; osg::BoundingSphere::value_type sqrlen_zdash = zdash.length2(); bsphere._radius = sqrlen_xdash; if (bsphere._radius> 0) & 0xFF) / 255.0f, ((clr >> 8) & 0xFF) / 255.0f, ((clr >> 16) & 0xFF) / 255.0f, 1.f); return colour; } osg::Vec4f colourFromRGBA(unsigned int clr) { osg::Vec4f colour(((clr >> 0) & 0xFF) / 255.0f, ((clr >> 8) & 0xFF) / 255.0f, ((clr >> 16) & 0xFF) / 255.0f, ((clr >> 24) & 0xFF) / 255.0f); return colour; } } openmw-openmw-0.38.0/components/sceneutil/util.hpp000066400000000000000000000010121264522266000222570ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SCENEUTIL_UTIL_H #define OPENMW_COMPONENTS_SCENEUTIL_UTIL_H #include #include #include namespace SceneUtil { // Transform a bounding sphere by a matrix // based off private code in osg::Transform // TODO: patch osg to make public void transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere); osg::Vec4f colourFromRGB (unsigned int clr); osg::Vec4f colourFromRGBA (unsigned int clr); } #endif openmw-openmw-0.38.0/components/sceneutil/visitor.cpp000066400000000000000000000014241264522266000230030ustar00rootroot00000000000000#include "visitor.hpp" #include #include #include namespace SceneUtil { void FindByNameVisitor::apply(osg::Group &group) { if (Misc::StringUtils::ciEqual(group.getName(), mNameToFind)) { mFoundNode = &group; return; } traverse(group); } void DisableFreezeOnCullVisitor::apply(osg::Geode &geode) { for (unsigned int i=0; i(&drw)) partsys->setFreezeOnCull(false); } } openmw-openmw-0.38.0/components/sceneutil/visitor.hpp000066400000000000000000000020111264522266000230010ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H #define OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H #include // Commonly used scene graph visitors namespace SceneUtil { // Find a Group by name, case-insensitive // If not found, mFoundNode will be NULL class FindByNameVisitor : public osg::NodeVisitor { public: FindByNameVisitor(const std::string& nameToFind) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mNameToFind(nameToFind) , mFoundNode(NULL) { } virtual void apply(osg::Group& group); std::string mNameToFind; osg::Group* mFoundNode; }; // Disable freezeOnCull for all visited particlesystems class DisableFreezeOnCullVisitor : public osg::NodeVisitor { public: DisableFreezeOnCullVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { } virtual void apply(osg::Geode &geode); virtual void apply(osg::Drawable& drw); }; } #endif openmw-openmw-0.38.0/components/sceneutil/workqueue.cpp000066400000000000000000000042601264522266000233340ustar00rootroot00000000000000#include "workqueue.hpp" namespace SceneUtil { void WorkTicket::waitTillDone() { if (mDone > 0) return; OpenThreads::ScopedLock lock(mMutex); while (mDone == 0) { mCondition.wait(&mMutex); } } void WorkTicket::signalDone() { { OpenThreads::ScopedLock lock(mMutex); mDone.exchange(1); } mCondition.broadcast(); } WorkItem::WorkItem() : mTicket(new WorkTicket) { mTicket->setThreadSafeRefUnref(true); } WorkItem::~WorkItem() { } void WorkItem::doWork() { mTicket->signalDone(); } osg::ref_ptr WorkItem::getTicket() { return mTicket; } WorkQueue::WorkQueue(int workerThreads) : mIsReleased(false) { for (int i=0; istartThread(); } } WorkQueue::~WorkQueue() { { OpenThreads::ScopedLock lock(mMutex); while (!mQueue.empty()) { WorkItem* item = mQueue.front(); delete item; mQueue.pop(); } mIsReleased = true; mCondition.broadcast(); } for (unsigned int i=0; ijoin(); delete mThreads[i]; } } osg::ref_ptr WorkQueue::addWorkItem(WorkItem *item) { osg::ref_ptr ticket = item->getTicket(); OpenThreads::ScopedLock lock(mMutex); mQueue.push(item); mCondition.signal(); return ticket; } WorkItem *WorkQueue::removeWorkItem() { OpenThreads::ScopedLock lock(mMutex); while (mQueue.empty() && !mIsReleased) { mCondition.wait(&mMutex); } if (mQueue.size()) { WorkItem* item = mQueue.front(); mQueue.pop(); return item; } else return NULL; } WorkThread::WorkThread(WorkQueue *workQueue) : mWorkQueue(workQueue) { } void WorkThread::run() { while (true) { WorkItem* item = mWorkQueue->removeWorkItem(); if (!item) return; item->doWork(); delete item; } } } openmw-openmw-0.38.0/components/sceneutil/workqueue.hpp000066400000000000000000000041371264522266000233440ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SCENEUTIL_WORKQUEUE_H #define OPENMW_COMPONENTS_SCENEUTIL_WORKQUEUE_H #include #include #include #include #include #include #include namespace SceneUtil { class WorkTicket : public osg::Referenced { public: void waitTillDone(); void signalDone(); private: OpenThreads::Atomic mDone; OpenThreads::Mutex mMutex; OpenThreads::Condition mCondition; }; class WorkItem { public: WorkItem(); virtual ~WorkItem(); /// Override in a derived WorkItem to perform actual work. /// By default, just signals the ticket that the work is done. virtual void doWork(); osg::ref_ptr getTicket(); protected: osg::ref_ptr mTicket; }; class WorkQueue; class WorkThread : public OpenThreads::Thread { public: WorkThread(WorkQueue* workQueue); virtual void run(); private: WorkQueue* mWorkQueue; }; /// @brief A work queue that users can push work items onto, to be completed by one or more background threads. class WorkQueue { public: WorkQueue(int numWorkerThreads=1); ~WorkQueue(); /// Add a new work item to the back of the queue. /// @par The returned WorkTicket may be used by the caller to wait until the work is complete. osg::ref_ptr addWorkItem(WorkItem* item); /// Get the next work item from the front of the queue. If the queue is empty, waits until a new item is added. /// If the workqueue is in the process of being destroyed, may return NULL. /// @note The caller must free the returned WorkItem WorkItem* removeWorkItem(); void runThread(); private: bool mIsReleased; std::queue mQueue; OpenThreads::Mutex mMutex; OpenThreads::Condition mCondition; std::vector mThreads; }; } #endif openmw-openmw-0.38.0/components/sdlutil/000077500000000000000000000000001264522266000202645ustar00rootroot00000000000000openmw-openmw-0.38.0/components/sdlutil/OISCompat.hpp000066400000000000000000000133331264522266000225760ustar00rootroot00000000000000#ifndef OIS_SDL_COMPAT_H #define OIS_SDL_COMPAT_H #include #include namespace OIS { //! Keyboard scan codes enum KeyCode { KC_UNASSIGNED = 0x00, KC_ESCAPE = 0x01, KC_1 = 0x02, KC_2 = 0x03, KC_3 = 0x04, KC_4 = 0x05, KC_5 = 0x06, KC_6 = 0x07, KC_7 = 0x08, KC_8 = 0x09, KC_9 = 0x0A, KC_0 = 0x0B, KC_MINUS = 0x0C, // - on main keyboard KC_EQUALS = 0x0D, KC_BACK = 0x0E, // backspace KC_TAB = 0x0F, KC_Q = 0x10, KC_W = 0x11, KC_E = 0x12, KC_R = 0x13, KC_T = 0x14, KC_Y = 0x15, KC_U = 0x16, KC_I = 0x17, KC_O = 0x18, KC_P = 0x19, KC_LBRACKET = 0x1A, KC_RBRACKET = 0x1B, KC_RETURN = 0x1C, // Enter on main keyboard KC_LCONTROL = 0x1D, KC_A = 0x1E, KC_S = 0x1F, KC_D = 0x20, KC_F = 0x21, KC_G = 0x22, KC_H = 0x23, KC_J = 0x24, KC_K = 0x25, KC_L = 0x26, KC_SEMICOLON = 0x27, KC_APOSTROPHE = 0x28, KC_GRAVE = 0x29, // accent KC_LSHIFT = 0x2A, KC_BACKSLASH = 0x2B, KC_Z = 0x2C, KC_X = 0x2D, KC_C = 0x2E, KC_V = 0x2F, KC_B = 0x30, KC_N = 0x31, KC_M = 0x32, KC_COMMA = 0x33, KC_PERIOD = 0x34, // . on main keyboard KC_SLASH = 0x35, // / on main keyboard KC_RSHIFT = 0x36, KC_MULTIPLY = 0x37, // * on numeric keypad KC_LMENU = 0x38, // left Alt KC_SPACE = 0x39, KC_CAPITAL = 0x3A, KC_F1 = 0x3B, KC_F2 = 0x3C, KC_F3 = 0x3D, KC_F4 = 0x3E, KC_F5 = 0x3F, KC_F6 = 0x40, KC_F7 = 0x41, KC_F8 = 0x42, KC_F9 = 0x43, KC_F10 = 0x44, KC_NUMLOCK = 0x45, KC_SCROLL = 0x46, // Scroll Lock KC_NUMPAD7 = 0x47, KC_NUMPAD8 = 0x48, KC_NUMPAD9 = 0x49, KC_SUBTRACT = 0x4A, // - on numeric keypad KC_NUMPAD4 = 0x4B, KC_NUMPAD5 = 0x4C, KC_NUMPAD6 = 0x4D, KC_ADD = 0x4E, // + on numeric keypad KC_NUMPAD1 = 0x4F, KC_NUMPAD2 = 0x50, KC_NUMPAD3 = 0x51, KC_NUMPAD0 = 0x52, KC_DECIMAL = 0x53, // . on numeric keypad KC_OEM_102 = 0x56, // < > | on UK/Germany keyboards KC_F11 = 0x57, KC_F12 = 0x58, KC_F13 = 0x64, // (NEC PC98) KC_F14 = 0x65, // (NEC PC98) KC_F15 = 0x66, // (NEC PC98) KC_KANA = 0x70, // (Japanese keyboard) KC_ABNT_C1 = 0x73, // / ? on Portugese (Brazilian) keyboards KC_CONVERT = 0x79, // (Japanese keyboard) KC_NOCONVERT = 0x7B, // (Japanese keyboard) KC_YEN = 0x7D, // (Japanese keyboard) KC_ABNT_C2 = 0x7E, // Numpad . on Portugese (Brazilian) keyboards KC_NUMPADEQUALS= 0x8D, // = on numeric keypad (NEC PC98) KC_PREVTRACK = 0x90, // Previous Track (KC_CIRCUMFLEX on Japanese keyboard) KC_AT = 0x91, // (NEC PC98) KC_COLON = 0x92, // (NEC PC98) KC_UNDERLINE = 0x93, // (NEC PC98) KC_KANJI = 0x94, // (Japanese keyboard) KC_STOP = 0x95, // (NEC PC98) KC_AX = 0x96, // (Japan AX) KC_UNLABELED = 0x97, // (J3100) KC_NEXTTRACK = 0x99, // Next Track KC_NUMPADENTER = 0x9C, // Enter on numeric keypad KC_RCONTROL = 0x9D, KC_MUTE = 0xA0, // Mute KC_CALCULATOR = 0xA1, // Calculator KC_PLAYPAUSE = 0xA2, // Play / Pause KC_MEDIASTOP = 0xA4, // Media Stop KC_VOLUMEDOWN = 0xAE, // Volume - KC_VOLUMEUP = 0xB0, // Volume + KC_WEBHOME = 0xB2, // Web home KC_NUMPADCOMMA = 0xB3, // , on numeric keypad (NEC PC98) KC_DIVIDE = 0xB5, // / on numeric keypad KC_SYSRQ = 0xB7, KC_RMENU = 0xB8, // right Alt KC_PAUSE = 0xC5, // Pause KC_HOME = 0xC7, // Home on arrow keypad KC_UP = 0xC8, // UpArrow on arrow keypad KC_PGUP = 0xC9, // PgUp on arrow keypad KC_LEFT = 0xCB, // LeftArrow on arrow keypad KC_RIGHT = 0xCD, // RightArrow on arrow keypad KC_END = 0xCF, // End on arrow keypad KC_DOWN = 0xD0, // DownArrow on arrow keypad KC_PGDOWN = 0xD1, // PgDn on arrow keypad KC_INSERT = 0xD2, // Insert on arrow keypad KC_DELETE = 0xD3, // Delete on arrow keypad KC_LWIN = 0xDB, // Left Windows key KC_RWIN = 0xDC, // Right Windows key KC_APPS = 0xDD, // AppMenu key KC_POWER = 0xDE, // System Power KC_SLEEP = 0xDF, // System Sleep KC_WAKE = 0xE3, // System Wake KC_WEBSEARCH = 0xE5, // Web Search KC_WEBFAVORITES= 0xE6, // Web Favorites KC_WEBREFRESH = 0xE7, // Web Refresh KC_WEBSTOP = 0xE8, // Web Stop KC_WEBFORWARD = 0xE9, // Web Forward KC_WEBBACK = 0xEA, // Web Back KC_MYCOMPUTER = 0xEB, // My Computer KC_MAIL = 0xEC, // Mail KC_MEDIASELECT = 0xED // Media Select }; } #endif openmw-openmw-0.38.0/components/sdlutil/events.hpp000066400000000000000000000037641264522266000223130ustar00rootroot00000000000000#ifndef _SFO_EVENTS_H #define _SFO_EVENTS_H #include #include //////////// // Events // //////////// namespace SDLUtil { /** Extended mouse event struct where we treat the wheel like an axis, like everyone expects */ struct MouseMotionEvent : SDL_MouseMotionEvent { Sint32 zrel; Sint32 z; }; /////////////// // Listeners // /////////////// class MouseListener { public: virtual ~MouseListener() {} virtual void mouseMoved( const MouseMotionEvent &arg ) = 0; virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; }; class KeyListener { public: virtual ~KeyListener() {} virtual void textInput (const SDL_TextInputEvent& arg) {} virtual void keyPressed(const SDL_KeyboardEvent &arg) = 0; virtual void keyReleased(const SDL_KeyboardEvent &arg) = 0; }; class ControllerListener { public: virtual ~ControllerListener() {} /** @remarks Joystick button down event */ virtual void buttonPressed(int deviceID, const SDL_ControllerButtonEvent &evt) = 0; /** @remarks Joystick button up event */ virtual void buttonReleased(int deviceID, const SDL_ControllerButtonEvent &evt) = 0; /** @remarks Joystick axis moved event */ virtual void axisMoved(int deviceID, const SDL_ControllerAxisEvent &arg) = 0; /** @remarks Joystick Added **/ virtual void controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &arg) = 0; /** @remarks Joystick Removed **/ virtual void controllerRemoved(const SDL_ControllerDeviceEvent &arg) = 0; }; class WindowListener { public: virtual ~WindowListener() {} /** @remarks The window's visibility changed */ virtual void windowVisibilityChange( bool visible ) {} /** @remarks The window got / lost input focus */ virtual void windowFocusChange( bool have_focus ) {} virtual void windowClosed () {} virtual void windowResized (int x, int y) {} }; } #endif openmw-openmw-0.38.0/components/sdlutil/imagetosurface.cpp000066400000000000000000000015561264522266000237750ustar00rootroot00000000000000#include "imagetosurface.hpp" #include #include namespace SDLUtil { SDL_Surface* imageToSurface(osg::Image *image, bool flip) { int width = image->s(); int height = image->t(); SDL_Surface* surface = SDL_CreateRGBSurface(0, width, height, 32, 0xFF000000,0x00FF0000,0x0000FF00,0x000000FF); for(int x = 0; x < width; ++x) for(int y = 0; y < height; ++y) { osg::Vec4f clr = image->getColor(x, flip ? ((height-1)-y) : y); int bpp = surface->format->BytesPerPixel; Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; *(Uint32*)(p) = SDL_MapRGBA(surface->format, static_cast(clr.r() * 255), static_cast(clr.g() * 255), static_cast(clr.b() * 255), static_cast(clr.a() * 255)); } return surface; } } openmw-openmw-0.38.0/components/sdlutil/imagetosurface.hpp000066400000000000000000000005761264522266000240030ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SDLUTIL_IMAGETOSURFACE_H #define OPENMW_COMPONENTS_SDLUTIL_IMAGETOSURFACE_H struct SDL_Surface; namespace osg { class Image; } namespace SDLUtil { /// Convert an osg::Image to an SDL_Surface. /// @note The returned surface must be freed using SDL_FreeSurface. SDL_Surface* imageToSurface(osg::Image* image, bool flip=false); } #endif openmw-openmw-0.38.0/components/sdlutil/sdlcursormanager.cpp000066400000000000000000000166261264522266000243560ustar00rootroot00000000000000#include "sdlcursormanager.hpp" #include #include #include #include #include #include #include #include #include #include #include "imagetosurface.hpp" #ifdef OSG_LIBRARY_STATIC // Sets the default windowing system interface according to the OS. // Necessary for OpenSceneGraph to do some things, like decompression. USE_GRAPHICSWINDOW() #endif namespace { class MyGraphicsContext { public: MyGraphicsContext(int w, int h) { osg::ref_ptr traits = new osg::GraphicsContext::Traits; traits->x = 0; traits->y = 0; traits->width = w; traits->height = h; traits->red = 8; traits->green = 8; traits->blue = 8; traits->alpha = 8; traits->windowDecoration = false; traits->doubleBuffer = false; traits->sharedContext = 0; traits->pbuffer = true; osg::GraphicsContext::ScreenIdentifier si; si.readDISPLAY(); if (si.displayNum<0) si.displayNum = 0; traits->displayNum = si.displayNum; traits->screenNum = si.screenNum; traits->hostName = si.hostName; _gc = osg::GraphicsContext::createGraphicsContext(traits.get()); if (!_gc) { std::cerr << "Failed to create pbuffer, failing back to normal graphics window." << std::endl; traits->pbuffer = false; _gc = osg::GraphicsContext::createGraphicsContext(traits.get()); if (!_gc) throw std::runtime_error("Failed to create graphics context for image decompression"); } if (_gc.valid()) { _gc->realize(); _gc->makeCurrent(); } } osg::ref_ptr getContext() { return _gc; } bool valid() const { return _gc.valid() && _gc->isRealized(); } private: osg::ref_ptr _gc; }; osg::ref_ptr decompress (osg::ref_ptr source, float rotDegrees) { int width = source->s(); int height = source->t(); MyGraphicsContext context(width, height); osg::ref_ptr state = context.getContext()->getState(); osg::ref_ptr texture = new osg::Texture2D; texture->setImage(source); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER); texture->setBorderColor(osg::Vec4f(0.f, 0.f, 0.f, 0.f)); osg::ref_ptr texmat = new osg::TexMat; osg::Matrix texRot (osg::Matrix::identity()); float theta ( osg::DegreesToRadians(-rotDegrees) ); float cosTheta = std::cos(theta); float sinTheta = std::sin(theta); texRot(0,0) = cosTheta; texRot(1,0) = -sinTheta; texRot(0,1) = sinTheta; texRot(1,1) = cosTheta; // Offset center of rotation to center of texture texRot(3,0) = 0.5f + ( (-0.5f * cosTheta) - (-0.5f * sinTheta) ); texRot(3,1) = 0.5f + ( (-0.5f * sinTheta) + (-0.5f * cosTheta) ); texmat->setMatrix(texRot); state->applyTextureAttribute(0, texmat); osg::ref_ptr identity (new osg::RefMatrix(osg::Matrix::identity())); state->applyModelViewMatrix(identity); state->applyProjectionMatrix(identity); state->applyMode(GL_TEXTURE_2D, true); state->applyTextureAttribute(0, texture); osg::ref_ptr resultImage = new osg::Image; resultImage->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE); osg::RenderInfo renderInfo; renderInfo.setState(state); glViewport(0, 0, width, height); osg::ref_ptr geom; #if defined(__APPLE__) // Extra flip needed on Intel graphics OS X systems due to a driver bug std::string vendorString = (const char*)glGetString(GL_VENDOR); if (vendorString.find("Intel") != std::string::npos) geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,1,0), osg::Vec3(2,0,0), osg::Vec3(0,-2,0)); else #endif geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,-1,0), osg::Vec3(2,0,0), osg::Vec3(0,2,0)); geom->drawImplementation(renderInfo); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, resultImage->data()); geom->releaseGLObjects(); source->releaseGLObjects(); texture->releaseGLObjects(); return resultImage; } } namespace SDLUtil { SDLCursorManager::SDLCursorManager() : mEnabled(false), mInitialized(false) { } SDLCursorManager::~SDLCursorManager() { CursorMap::const_iterator curs_iter = mCursorMap.begin(); while(curs_iter != mCursorMap.end()) { SDL_FreeCursor(curs_iter->second); ++curs_iter; } mCursorMap.clear(); } void SDLCursorManager::setEnabled(bool enabled) { if(mInitialized && enabled == mEnabled) return; mInitialized = true; mEnabled = enabled; //turn on hardware cursors if(enabled) { _setGUICursor(mCurrentCursor); } //turn off hardware cursors else { SDL_ShowCursor(SDL_FALSE); } } void SDLCursorManager::cursorChanged(const std::string& name) { mCurrentCursor = name; CursorMap::const_iterator curs_iter = mCursorMap.find(name); if(curs_iter != mCursorMap.end()) { //we have this cursor _setGUICursor(name); } } void SDLCursorManager::_setGUICursor(const std::string &name) { SDL_SetCursor(mCursorMap.find(name)->second); } void SDLCursorManager::createCursor(const std::string& name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) { _createCursorFromResource(name, rotDegrees, image, size_x, size_y, hotspot_x, hotspot_y); } void SDLCursorManager::_createCursorFromResource(const std::string& name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) { osg::ref_ptr decompressed; if (mCursorMap.find(name) != mCursorMap.end()) return; try { decompressed = decompress(image, static_cast(rotDegrees)); } catch (std::exception& e) { std::cerr << e.what() << std::endl; std::cerr <<"Using default cursor."< #include #include struct SDL_Cursor; struct SDL_Surface; namespace osg { class Image; } namespace SDLUtil { class SDLCursorManager { public: SDLCursorManager(); virtual ~SDLCursorManager(); /// \brief sets whether to actively manage cursors or not virtual void setEnabled(bool enabled); /// \brief Tell the manager that the cursor has changed, giving the /// name of the cursor we changed to ("arrow", "ibeam", etc) virtual void cursorChanged(const std::string &name); virtual void createCursor(const std::string &name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y); private: void _createCursorFromResource(const std::string &name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y); void _putPixel(SDL_Surface *surface, int x, int y, Uint32 pixel); void _setGUICursor(const std::string& name); typedef std::map CursorMap; CursorMap mCursorMap; std::string mCurrentCursor; bool mEnabled; bool mInitialized; }; } #endif openmw-openmw-0.38.0/components/sdlutil/sdlgraphicswindow.cpp000066400000000000000000000111671264522266000245310ustar00rootroot00000000000000#include "sdlgraphicswindow.hpp" #include #include namespace SDLUtil { GraphicsWindowSDL2::~GraphicsWindowSDL2() { close(true); } GraphicsWindowSDL2::GraphicsWindowSDL2(osg::GraphicsContext::Traits *traits) : mWindow(0) , mContext(0) , mValid(false) , mRealized(false) , mOwnsWindow(false) { _traits = traits; init(); if(valid()) { setState(new osg::State); getState()->setGraphicsContext(this); if(_traits.valid() && _traits->sharedContext.valid()) { getState()->setContextID(_traits->sharedContext->getState()->getContextID()); incrementContextIDUsageCount(getState()->getContextID()); } else { getState()->setContextID(osg::GraphicsContext::createNewContextID()); } } } bool GraphicsWindowSDL2::setWindowDecorationImplementation(bool flag) { if(!mWindow) return false; SDL_SetWindowBordered(mWindow, flag ? SDL_TRUE : SDL_FALSE); return true; } bool GraphicsWindowSDL2::setWindowRectangleImplementation(int x, int y, int width, int height) { if(!mWindow) return false; SDL_SetWindowPosition(mWindow, x, y); SDL_SetWindowSize(mWindow, width, height); return true; } void GraphicsWindowSDL2::setWindowName(const std::string &name) { if(!mWindow) return; SDL_SetWindowTitle(mWindow, name.c_str()); _traits->windowName = name; } void GraphicsWindowSDL2::setCursor(MouseCursor mouseCursor) { _traits->useCursor = false; } void GraphicsWindowSDL2::init() { if(mValid) return; if(!_traits.valid()) return; WindowData *inheritedWindowData = dynamic_cast(_traits->inheritedWindowData.get()); mWindow = inheritedWindowData ? inheritedWindowData->mWindow : NULL; mOwnsWindow = (mWindow == 0); if(mOwnsWindow) { OSG_FATAL<<"Error: No SDL window provided."<vsync ? 1 : 0); SDL_GL_MakeCurrent(oldWin, oldCtx); mValid = true; #if OSG_VERSION_GREATER_OR_EQUAL(3,3,4) getEventQueue()->syncWindowRectangleWithGraphicsContext(); #else getEventQueue()->syncWindowRectangleWithGraphcisContext(); #endif } bool GraphicsWindowSDL2::realizeImplementation() { if(mRealized) { OSG_NOTICE<< "GraphicsWindowSDL2::realizeImplementation() Already realized" <syncWindowRectangleWithGraphicsContext(); #else getEventQueue()->syncWindowRectangleWithGraphcisContext(); #endif mRealized = true; return true; } bool GraphicsWindowSDL2::makeCurrentImplementation() { if(!mRealized) { OSG_WARN<<"Warning: GraphicsWindow not realized, cannot do makeCurrent."< #include namespace SDLUtil { class GraphicsWindowSDL2 : public osgViewer::GraphicsWindow { SDL_Window* mWindow; SDL_GLContext mContext; bool mValid; bool mRealized; bool mOwnsWindow; void init(); virtual ~GraphicsWindowSDL2(); public: GraphicsWindowSDL2(osg::GraphicsContext::Traits *traits); virtual bool isSameKindAs(const Object* object) const { return dynamic_cast(object)!=0; } virtual const char* libraryName() const { return "osgViewer"; } virtual const char* className() const { return "GraphicsWindowSDL2"; } virtual bool valid() const { return mValid; } /** Realise the GraphicsContext.*/ virtual bool realizeImplementation(); /** Return true if the graphics context has been realised and is ready to use.*/ virtual bool isRealizedImplementation() const { return mRealized; } /** Close the graphics context.*/ virtual void closeImplementation(); /** Make this graphics context current.*/ virtual bool makeCurrentImplementation(); /** Release the graphics context.*/ virtual bool releaseContextImplementation(); /** Swap the front and back buffers.*/ virtual void swapBuffersImplementation(); /** Set sync-to-vblank. */ virtual void setSyncToVBlank(bool on); /** Set Window decoration.*/ virtual bool setWindowDecorationImplementation(bool flag); /** Raise specified window */ virtual void raiseWindow(); /** Set the window's position and size.*/ virtual bool setWindowRectangleImplementation(int x, int y, int width, int height); /** Set the name of the window */ virtual void setWindowName(const std::string &name); /** Set mouse cursor to a specific shape.*/ virtual void setCursor(MouseCursor cursor); /** Get focus.*/ virtual void grabFocus() {} /** Get focus on if the pointer is in this window.*/ virtual void grabFocusIfPointerInWindow() {} /** WindowData is used to pass in the SDL2 window handle attached to the GraphicsContext::Traits structure. */ struct WindowData : public osg::Referenced { WindowData(SDL_Window *window) : mWindow(window) { } SDL_Window *mWindow; }; }; } #endif /* OSGGRAPHICSWINDOW_H */ openmw-openmw-0.38.0/components/sdlutil/sdlinputwrapper.cpp000066400000000000000000000502721264522266000242410ustar00rootroot00000000000000#include "sdlinputwrapper.hpp" #include #include #include namespace SDLUtil { InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr viewer, bool grab) : mSDLWindow(window), mViewer(viewer), mMouseListener(NULL), mKeyboardListener(NULL), mWindowListener(NULL), mConListener(NULL), mWarpX(0), mWarpY(0), mWarpCompensate(false), mWrapPointer(false), mAllowGrab(grab), mWantMouseVisible(false), mWantGrab(false), mWantRelative(false), mGrabPointer(false), mMouseRelative(false), mFirstMouseMove(true), mMouseZ(0), mMouseX(0), mMouseY(0), mWindowHasFocus(true), mMouseInWindow(true) { _setupOISKeys(); Uint32 flags = SDL_GetWindowFlags(mSDLWindow); mWindowHasFocus = (flags & SDL_WINDOW_INPUT_FOCUS); mMouseInWindow = (flags & SDL_WINDOW_MOUSE_FOCUS); } InputWrapper::~InputWrapper() { } void InputWrapper::capture(bool windowEventsOnly) { mViewer->getEventQueue()->frame(0.f); SDL_PumpEvents(); SDL_Event evt; if (windowEventsOnly) { // During loading, just handle window events, and keep others for later while (SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT)) handleWindowEvent(evt); return; } while(SDL_PollEvent(&evt)) { switch(evt.type) { case SDL_MOUSEMOTION: // Ignore this if it happened due to a warp if(!_handleWarpMotion(evt.motion)) { // If in relative mode, don't trigger events unless window has focus if (!mWantRelative || mWindowHasFocus) mMouseListener->mouseMoved(_packageMouseMotion(evt)); // Try to keep the mouse inside the window if (mWindowHasFocus) _wrapMousePointer(evt.motion); } break; case SDL_MOUSEWHEEL: mMouseListener->mouseMoved(_packageMouseMotion(evt)); break; case SDL_MOUSEBUTTONDOWN: mMouseListener->mousePressed(evt.button, evt.button.button); break; case SDL_MOUSEBUTTONUP: mMouseListener->mouseReleased(evt.button, evt.button.button); break; case SDL_KEYDOWN: if (!evt.key.repeat) mKeyboardListener->keyPressed(evt.key); // temporary for the stats viewer if (evt.key.keysym.sym == SDLK_F3) mViewer->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_F3); break; case SDL_KEYUP: if (!evt.key.repeat) mKeyboardListener->keyReleased(evt.key); // temporary for the stats viewer if (evt.key.keysym.sym == SDLK_F3) mViewer->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_F3); break; case SDL_TEXTINPUT: mKeyboardListener->textInput(evt.text); break; case SDL_JOYHATMOTION: //As we manage everything with GameController, don't even bother with these. case SDL_JOYAXISMOTION: case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: case SDL_JOYDEVICEADDED: case SDL_JOYDEVICEREMOVED: break; case SDL_CONTROLLERDEVICEADDED: if(mConListener) mConListener->controllerAdded(1, evt.cdevice); //We only support one joystick, so give everything a generic deviceID break; case SDL_CONTROLLERDEVICEREMOVED: if(mConListener) mConListener->controllerRemoved(evt.cdevice); break; case SDL_CONTROLLERBUTTONDOWN: if(mConListener) mConListener->buttonPressed(1, evt.cbutton); break; case SDL_CONTROLLERBUTTONUP: if(mConListener) mConListener->buttonReleased(1, evt.cbutton); break; case SDL_CONTROLLERAXISMOTION: if(mConListener) mConListener->axisMoved(1, evt.caxis); break; case SDL_WINDOWEVENT: handleWindowEvent(evt); break; case SDL_QUIT: if (mWindowListener) mWindowListener->windowClosed(); break; case SDL_CLIPBOARDUPDATE: break; // We don't need this event, clipboard is retrieved on demand case SDL_FINGERDOWN: case SDL_FINGERUP: case SDL_FINGERMOTION: case SDL_DOLLARGESTURE: case SDL_DOLLARRECORD: case SDL_MULTIGESTURE: // No use for touch & gesture events break; default: std::ios::fmtflags f(std::cerr.flags()); std::cerr << "Unhandled SDL event of type 0x" << std::hex << evt.type << std::endl; std::cerr.flags(f); break; } } } void InputWrapper::handleWindowEvent(const SDL_Event& evt) { switch (evt.window.event) { case SDL_WINDOWEVENT_ENTER: mMouseInWindow = true; updateMouseSettings(); break; case SDL_WINDOWEVENT_LEAVE: mMouseInWindow = false; updateMouseSettings(); break; case SDL_WINDOWEVENT_MOVED: // I'm not sure what OSG is using the window position for, but I don't think it's needed, // so we ignore window moved events (improves window movement performance) break; case SDL_WINDOWEVENT_SIZE_CHANGED: int w,h; SDL_GetWindowSize(mSDLWindow, &w, &h); int x,y; SDL_GetWindowPosition(mSDLWindow, &x,&y); mViewer->getCamera()->getGraphicsContext()->resized(x,y,w,h); mViewer->getEventQueue()->windowResize(x,y,w,h); if (mWindowListener) mWindowListener->windowResized(w, h); break; case SDL_WINDOWEVENT_RESIZED: // This should also fire SIZE_CHANGED, so no need to handle break; case SDL_WINDOWEVENT_FOCUS_GAINED: mWindowHasFocus = true; updateMouseSettings(); if (mWindowListener) mWindowListener->windowFocusChange(true); break; case SDL_WINDOWEVENT_FOCUS_LOST: mWindowHasFocus = false; updateMouseSettings(); if (mWindowListener) mWindowListener->windowFocusChange(false); break; case SDL_WINDOWEVENT_CLOSE: break; case SDL_WINDOWEVENT_SHOWN: if (mWindowListener) mWindowListener->windowVisibilityChange(true); break; case SDL_WINDOWEVENT_HIDDEN: if (mWindowListener) mWindowListener->windowVisibilityChange(false); break; } } bool InputWrapper::isModifierHeld(SDL_Keymod mod) { return (SDL_GetModState() & mod) != 0; } bool InputWrapper::isKeyDown(SDL_Scancode key) { return (SDL_GetKeyboardState(NULL)[key]) != 0; } /// \brief Moves the mouse to the specified point within the viewport void InputWrapper::warpMouse(int x, int y) { SDL_WarpMouseInWindow(mSDLWindow, x, y); mWarpCompensate = true; mWarpX = x; mWarpY = y; } /// \brief Locks the pointer to the window void InputWrapper::setGrabPointer(bool grab) { mWantGrab = grab; updateMouseSettings(); } /// \brief Set the mouse to relative positioning. Doesn't move the cursor /// and disables mouse acceleration. void InputWrapper::setMouseRelative(bool relative) { mWantRelative = relative; updateMouseSettings(); } void InputWrapper::setMouseVisible(bool visible) { mWantMouseVisible = visible; updateMouseSettings(); } void InputWrapper::updateMouseSettings() { mGrabPointer = mWantGrab && mMouseInWindow && mWindowHasFocus; SDL_SetWindowGrab(mSDLWindow, mGrabPointer && mAllowGrab ? SDL_TRUE : SDL_FALSE); SDL_ShowCursor(mWantMouseVisible || !mWindowHasFocus); bool relative = mWantRelative && mMouseInWindow && mWindowHasFocus; if(mMouseRelative == relative) return; mMouseRelative = relative; mWrapPointer = false; // eep, wrap the pointer manually if the input driver doesn't support // relative positioning natively // also use wrapping if no-grab was specified in options (SDL_SetRelativeMouseMode // appears to eat the mouse cursor when pausing in a debugger) bool success = mAllowGrab && SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE) == 0; if(relative && !success) mWrapPointer = true; //now remove all mouse events using the old setting from the queue SDL_PumpEvents(); SDL_FlushEvent(SDL_MOUSEMOTION); } /// \brief Internal method for ignoring relative motions as a side effect /// of warpMouse() bool InputWrapper::_handleWarpMotion(const SDL_MouseMotionEvent& evt) { if(!mWarpCompensate) return false; //this was a warp event, signal the caller to eat it. if(evt.x == mWarpX && evt.y == mWarpY) { mWarpCompensate = false; return true; } return false; } /// \brief Wrap the mouse to the viewport void InputWrapper::_wrapMousePointer(const SDL_MouseMotionEvent& evt) { //don't wrap if we don't want relative movements, support relative //movements natively, or aren't grabbing anyways if(!mMouseRelative || !mWrapPointer || !mGrabPointer) return; int width = 0; int height = 0; SDL_GetWindowSize(mSDLWindow, &width, &height); const int FUDGE_FACTOR_X = width; const int FUDGE_FACTOR_Y = height; //warp the mouse if it's about to go outside the window if(evt.x - FUDGE_FACTOR_X < 0 || evt.x + FUDGE_FACTOR_X > width || evt.y - FUDGE_FACTOR_Y < 0 || evt.y + FUDGE_FACTOR_Y > height) { warpMouse(width / 2, height / 2); } } /// \brief Package mouse and mousewheel motions into a single event MouseMotionEvent InputWrapper::_packageMouseMotion(const SDL_Event &evt) { MouseMotionEvent pack_evt; pack_evt.x = mMouseX; pack_evt.xrel = 0; pack_evt.y = mMouseY; pack_evt.yrel = 0; pack_evt.z = mMouseZ; pack_evt.zrel = 0; if(evt.type == SDL_MOUSEMOTION) { pack_evt.x = mMouseX = evt.motion.x; pack_evt.y = mMouseY = evt.motion.y; pack_evt.xrel = evt.motion.xrel; pack_evt.yrel = evt.motion.yrel; if (mFirstMouseMove) { // first event should be treated as non-relative, since there's no point of reference // SDL then (incorrectly) uses (0,0) as point of reference, on Linux at least... pack_evt.xrel = pack_evt.yrel = 0; mFirstMouseMove = false; } } else if(evt.type == SDL_MOUSEWHEEL) { mMouseZ += pack_evt.zrel = (evt.wheel.y * 120); pack_evt.z = mMouseZ; } else { throw new std::runtime_error("Tried to package non-motion event!"); } return pack_evt; } OIS::KeyCode InputWrapper::sdl2OISKeyCode(SDL_Keycode code) { OIS::KeyCode kc = OIS::KC_UNASSIGNED; KeyMap::const_iterator ois_equiv = mKeyMap.find(code); if(ois_equiv != mKeyMap.end()) kc = ois_equiv->second; return kc; } void InputWrapper::_setupOISKeys() { //lifted from OIS's SDLKeyboard.cpp mKeyMap.insert( KeyMap::value_type(SDLK_UNKNOWN, OIS::KC_UNASSIGNED)); mKeyMap.insert( KeyMap::value_type(SDLK_ESCAPE, OIS::KC_ESCAPE) ); mKeyMap.insert( KeyMap::value_type(SDLK_1, OIS::KC_1) ); mKeyMap.insert( KeyMap::value_type(SDLK_2, OIS::KC_2) ); mKeyMap.insert( KeyMap::value_type(SDLK_3, OIS::KC_3) ); mKeyMap.insert( KeyMap::value_type(SDLK_4, OIS::KC_4) ); mKeyMap.insert( KeyMap::value_type(SDLK_5, OIS::KC_5) ); mKeyMap.insert( KeyMap::value_type(SDLK_6, OIS::KC_6) ); mKeyMap.insert( KeyMap::value_type(SDLK_7, OIS::KC_7) ); mKeyMap.insert( KeyMap::value_type(SDLK_8, OIS::KC_8) ); mKeyMap.insert( KeyMap::value_type(SDLK_9, OIS::KC_9) ); mKeyMap.insert( KeyMap::value_type(SDLK_0, OIS::KC_0) ); mKeyMap.insert( KeyMap::value_type(SDLK_MINUS, OIS::KC_MINUS) ); mKeyMap.insert( KeyMap::value_type(SDLK_EQUALS, OIS::KC_EQUALS) ); mKeyMap.insert( KeyMap::value_type(SDLK_BACKSPACE, OIS::KC_BACK) ); mKeyMap.insert( KeyMap::value_type(SDLK_TAB, OIS::KC_TAB) ); mKeyMap.insert( KeyMap::value_type(SDLK_q, OIS::KC_Q) ); mKeyMap.insert( KeyMap::value_type(SDLK_w, OIS::KC_W) ); mKeyMap.insert( KeyMap::value_type(SDLK_e, OIS::KC_E) ); mKeyMap.insert( KeyMap::value_type(SDLK_r, OIS::KC_R) ); mKeyMap.insert( KeyMap::value_type(SDLK_t, OIS::KC_T) ); mKeyMap.insert( KeyMap::value_type(SDLK_y, OIS::KC_Y) ); mKeyMap.insert( KeyMap::value_type(SDLK_u, OIS::KC_U) ); mKeyMap.insert( KeyMap::value_type(SDLK_i, OIS::KC_I) ); mKeyMap.insert( KeyMap::value_type(SDLK_o, OIS::KC_O) ); mKeyMap.insert( KeyMap::value_type(SDLK_p, OIS::KC_P) ); mKeyMap.insert( KeyMap::value_type(SDLK_RETURN, OIS::KC_RETURN) ); mKeyMap.insert( KeyMap::value_type(SDLK_LCTRL, OIS::KC_LCONTROL)); mKeyMap.insert( KeyMap::value_type(SDLK_a, OIS::KC_A) ); mKeyMap.insert( KeyMap::value_type(SDLK_s, OIS::KC_S) ); mKeyMap.insert( KeyMap::value_type(SDLK_d, OIS::KC_D) ); mKeyMap.insert( KeyMap::value_type(SDLK_f, OIS::KC_F) ); mKeyMap.insert( KeyMap::value_type(SDLK_g, OIS::KC_G) ); mKeyMap.insert( KeyMap::value_type(SDLK_h, OIS::KC_H) ); mKeyMap.insert( KeyMap::value_type(SDLK_j, OIS::KC_J) ); mKeyMap.insert( KeyMap::value_type(SDLK_k, OIS::KC_K) ); mKeyMap.insert( KeyMap::value_type(SDLK_l, OIS::KC_L) ); mKeyMap.insert( KeyMap::value_type(SDLK_SEMICOLON, OIS::KC_SEMICOLON) ); mKeyMap.insert( KeyMap::value_type(SDLK_COLON, OIS::KC_COLON) ); mKeyMap.insert( KeyMap::value_type(SDLK_QUOTE, OIS::KC_APOSTROPHE) ); mKeyMap.insert( KeyMap::value_type(SDLK_BACKQUOTE, OIS::KC_GRAVE) ); mKeyMap.insert( KeyMap::value_type(SDLK_LSHIFT, OIS::KC_LSHIFT) ); mKeyMap.insert( KeyMap::value_type(SDLK_BACKSLASH, OIS::KC_BACKSLASH) ); mKeyMap.insert( KeyMap::value_type(SDLK_SLASH, OIS::KC_SLASH) ); mKeyMap.insert( KeyMap::value_type(SDLK_z, OIS::KC_Z) ); mKeyMap.insert( KeyMap::value_type(SDLK_x, OIS::KC_X) ); mKeyMap.insert( KeyMap::value_type(SDLK_c, OIS::KC_C) ); mKeyMap.insert( KeyMap::value_type(SDLK_v, OIS::KC_V) ); mKeyMap.insert( KeyMap::value_type(SDLK_b, OIS::KC_B) ); mKeyMap.insert( KeyMap::value_type(SDLK_n, OIS::KC_N) ); mKeyMap.insert( KeyMap::value_type(SDLK_m, OIS::KC_M) ); mKeyMap.insert( KeyMap::value_type(SDLK_COMMA, OIS::KC_COMMA) ); mKeyMap.insert( KeyMap::value_type(SDLK_PERIOD, OIS::KC_PERIOD)); mKeyMap.insert( KeyMap::value_type(SDLK_RSHIFT, OIS::KC_RSHIFT)); mKeyMap.insert( KeyMap::value_type(SDLK_KP_MULTIPLY, OIS::KC_MULTIPLY) ); mKeyMap.insert( KeyMap::value_type(SDLK_LALT, OIS::KC_LMENU) ); mKeyMap.insert( KeyMap::value_type(SDLK_SPACE, OIS::KC_SPACE)); mKeyMap.insert( KeyMap::value_type(SDLK_CAPSLOCK, OIS::KC_CAPITAL) ); mKeyMap.insert( KeyMap::value_type(SDLK_F1, OIS::KC_F1) ); mKeyMap.insert( KeyMap::value_type(SDLK_F2, OIS::KC_F2) ); mKeyMap.insert( KeyMap::value_type(SDLK_F3, OIS::KC_F3) ); mKeyMap.insert( KeyMap::value_type(SDLK_F4, OIS::KC_F4) ); mKeyMap.insert( KeyMap::value_type(SDLK_F5, OIS::KC_F5) ); mKeyMap.insert( KeyMap::value_type(SDLK_F6, OIS::KC_F6) ); mKeyMap.insert( KeyMap::value_type(SDLK_F7, OIS::KC_F7) ); mKeyMap.insert( KeyMap::value_type(SDLK_F8, OIS::KC_F8) ); mKeyMap.insert( KeyMap::value_type(SDLK_F9, OIS::KC_F9) ); mKeyMap.insert( KeyMap::value_type(SDLK_F10, OIS::KC_F10) ); mKeyMap.insert( KeyMap::value_type(SDLK_NUMLOCKCLEAR, OIS::KC_NUMLOCK) ); mKeyMap.insert( KeyMap::value_type(SDLK_SCROLLLOCK, OIS::KC_SCROLL)); mKeyMap.insert( KeyMap::value_type(SDLK_KP_7, OIS::KC_NUMPAD7) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_8, OIS::KC_NUMPAD8) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_9, OIS::KC_NUMPAD9) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_MINUS, OIS::KC_SUBTRACT) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_4, OIS::KC_NUMPAD4) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_5, OIS::KC_NUMPAD5) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_6, OIS::KC_NUMPAD6) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_PLUS, OIS::KC_ADD) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_1, OIS::KC_NUMPAD1) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_2, OIS::KC_NUMPAD2) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_3, OIS::KC_NUMPAD3) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_0, OIS::KC_NUMPAD0) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_PERIOD, OIS::KC_DECIMAL) ); mKeyMap.insert( KeyMap::value_type(SDLK_F11, OIS::KC_F11) ); mKeyMap.insert( KeyMap::value_type(SDLK_F12, OIS::KC_F12) ); mKeyMap.insert( KeyMap::value_type(SDLK_F13, OIS::KC_F13) ); mKeyMap.insert( KeyMap::value_type(SDLK_F14, OIS::KC_F14) ); mKeyMap.insert( KeyMap::value_type(SDLK_F15, OIS::KC_F15) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_EQUALS, OIS::KC_NUMPADEQUALS) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_DIVIDE, OIS::KC_DIVIDE) ); mKeyMap.insert( KeyMap::value_type(SDLK_SYSREQ, OIS::KC_SYSRQ) ); mKeyMap.insert( KeyMap::value_type(SDLK_RALT, OIS::KC_RMENU) ); mKeyMap.insert( KeyMap::value_type(SDLK_HOME, OIS::KC_HOME) ); mKeyMap.insert( KeyMap::value_type(SDLK_UP, OIS::KC_UP) ); mKeyMap.insert( KeyMap::value_type(SDLK_PAGEUP, OIS::KC_PGUP) ); mKeyMap.insert( KeyMap::value_type(SDLK_LEFT, OIS::KC_LEFT) ); mKeyMap.insert( KeyMap::value_type(SDLK_RIGHT, OIS::KC_RIGHT) ); mKeyMap.insert( KeyMap::value_type(SDLK_END, OIS::KC_END) ); mKeyMap.insert( KeyMap::value_type(SDLK_DOWN, OIS::KC_DOWN) ); mKeyMap.insert( KeyMap::value_type(SDLK_PAGEDOWN, OIS::KC_PGDOWN) ); mKeyMap.insert( KeyMap::value_type(SDLK_INSERT, OIS::KC_INSERT) ); mKeyMap.insert( KeyMap::value_type(SDLK_DELETE, OIS::KC_DELETE) ); mKeyMap.insert( KeyMap::value_type(SDLK_KP_ENTER, OIS::KC_NUMPADENTER) ); mKeyMap.insert( KeyMap::value_type(SDLK_RCTRL, OIS::KC_RCONTROL) ); mKeyMap.insert( KeyMap::value_type(SDLK_LGUI, OIS::KC_LWIN) ); mKeyMap.insert( KeyMap::value_type(SDLK_RGUI, OIS::KC_RWIN) ); mKeyMap.insert( KeyMap::value_type(SDLK_APPLICATION, OIS::KC_APPS) ); } } openmw-openmw-0.38.0/components/sdlutil/sdlinputwrapper.hpp000066400000000000000000000046061264522266000242460ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SDLUTIL_SDLINPUTWRAPPER_H #define OPENMW_COMPONENTS_SDLUTIL_SDLINPUTWRAPPER_H #include #include #include #include "OISCompat.hpp" #include "events.hpp" namespace osgViewer { class Viewer; } namespace SDLUtil { /// \brief A wrapper around SDL's event queue, mostly used for handling input-related events. class InputWrapper { public: InputWrapper(SDL_Window *window, osg::ref_ptr viewer, bool grab); ~InputWrapper(); void setMouseEventCallback(MouseListener* listen) { mMouseListener = listen; } void setKeyboardEventCallback(KeyListener* listen) { mKeyboardListener = listen; } void setWindowEventCallback(WindowListener* listen) { mWindowListener = listen; } void setControllerEventCallback(ControllerListener* listen) { mConListener = listen; } void capture(bool windowEventsOnly); bool isModifierHeld(SDL_Keymod mod); bool isKeyDown(SDL_Scancode key); void setMouseVisible (bool visible); void setMouseRelative(bool relative); bool getMouseRelative() { return mMouseRelative; } void setGrabPointer(bool grab); OIS::KeyCode sdl2OISKeyCode(SDL_Keycode code); void warpMouse(int x, int y); void updateMouseSettings(); private: void handleWindowEvent(const SDL_Event& evt); bool _handleWarpMotion(const SDL_MouseMotionEvent& evt); void _wrapMousePointer(const SDL_MouseMotionEvent &evt); MouseMotionEvent _packageMouseMotion(const SDL_Event& evt); void _setupOISKeys(); SDL_Window* mSDLWindow; osg::ref_ptr mViewer; MouseListener* mMouseListener; KeyListener* mKeyboardListener; WindowListener* mWindowListener; ControllerListener* mConListener; typedef std::map KeyMap; KeyMap mKeyMap; Uint16 mWarpX; Uint16 mWarpY; bool mWarpCompensate; bool mWrapPointer; bool mAllowGrab; bool mWantMouseVisible; bool mWantGrab; bool mWantRelative; bool mGrabPointer; bool mMouseRelative; bool mFirstMouseMove; Sint32 mMouseZ; Sint32 mMouseX; Sint32 mMouseY; bool mWindowHasFocus; bool mMouseInWindow; }; } #endif openmw-openmw-0.38.0/components/sdlutil/sdlvideowrapper.cpp000066400000000000000000000053371264522266000242120ustar00rootroot00000000000000#include "sdlvideowrapper.hpp" #include #include #include namespace SDLUtil { VideoWrapper::VideoWrapper(SDL_Window *window, osg::ref_ptr viewer) : mWindow(window) , mViewer(viewer) , mGamma(1.f) , mContrast(1.f) , mHasSetGammaContrast(false) { SDL_GetWindowGammaRamp(mWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]); } VideoWrapper::~VideoWrapper() { SDL_SetWindowFullscreen(mWindow, 0); // If user hasn't touched the defaults no need to restore if (mHasSetGammaContrast) SDL_SetWindowGammaRamp(mWindow, mOldSystemGammaRamp, &mOldSystemGammaRamp[256], &mOldSystemGammaRamp[512]); } void VideoWrapper::setSyncToVBlank(bool sync) { osgViewer::Viewer::Windows windows; mViewer->getWindows(windows); mViewer->stopThreading(); for (osgViewer::Viewer::Windows::iterator it = windows.begin(); it != windows.end(); ++it) { osgViewer::GraphicsWindow* win = *it; win->setSyncToVBlank(sync); } mViewer->startThreading(); } void VideoWrapper::setGammaContrast(float gamma, float contrast) { if (gamma == mGamma && contrast == mContrast) return; mGamma = gamma; mContrast = contrast; mHasSetGammaContrast = true; Uint16 red[256], green[256], blue[256]; for (int i = 0; i < 256; i++) { float k = i/256.0f; k = (k - 0.5f) * contrast + 0.5f; k = pow(k, 1.f/gamma); k *= 256; float value = k*256; if (value > 65535) value = 65535; else if (value < 0) value = 0; red[i] = green[i] = blue[i] = static_cast(value); } if (SDL_SetWindowGammaRamp(mWindow, red, green, blue) < 0) std::cout << "Couldn't set gamma: " << SDL_GetError() << std::endl; } void VideoWrapper::setVideoMode(int width, int height, bool fullscreen, bool windowBorder) { SDL_SetWindowFullscreen(mWindow, 0); if (SDL_GetWindowFlags(mWindow) & SDL_WINDOW_MAXIMIZED) SDL_RestoreWindow(mWindow); if (fullscreen) { SDL_DisplayMode mode; SDL_GetWindowDisplayMode(mWindow, &mode); mode.w = width; mode.h = height; SDL_SetWindowDisplayMode(mWindow, &mode); SDL_SetWindowFullscreen(mWindow, fullscreen); } else { SDL_SetWindowSize(mWindow, width, height); SDL_SetWindowBordered(mWindow, windowBorder ? SDL_TRUE : SDL_FALSE); } } } openmw-openmw-0.38.0/components/sdlutil/sdlvideowrapper.hpp000066400000000000000000000016051264522266000242110ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_SDLUTIL_SDLVIDEOWRAPPER_H #define OPENMW_COMPONENTS_SDLUTIL_SDLVIDEOWRAPPER_H #include #include struct SDL_Window; namespace osgViewer { class Viewer; } namespace SDLUtil { class VideoWrapper { public: VideoWrapper(SDL_Window* window, osg::ref_ptr viewer); ~VideoWrapper(); void setSyncToVBlank(bool sync); void setGammaContrast(float gamma, float contrast); void setVideoMode(int width, int height, bool fullscreen, bool windowBorder); private: SDL_Window* mWindow; osg::ref_ptr mViewer; float mGamma; float mContrast; bool mHasSetGammaContrast; // Store system gamma ramp on window creation. Restore system gamma ramp on exit Uint16 mOldSystemGammaRamp[256*3]; }; } #endif openmw-openmw-0.38.0/components/settings/000077500000000000000000000000001264522266000204445ustar00rootroot00000000000000openmw-openmw-0.38.0/components/settings/settings.cpp000066400000000000000000000405471264522266000230220ustar00rootroot00000000000000#include "settings.hpp" #include #include #include #include #include #include #include namespace { bool parseBool(const std::string& string) { return (Misc::StringUtils::ciEqual(string, "true")); } float parseFloat(const std::string& string) { std::stringstream stream; stream << string; float ret = 0.f; stream >> ret; return ret; } int parseInt(const std::string& string) { std::stringstream stream; stream << string; int ret = 0; stream >> ret; return ret; } template std::string toString(T val) { std::ostringstream stream; stream << val; return stream.str(); } template <> std::string toString(bool val) { return val ? "true" : "false"; } } namespace Settings { CategorySettingValueMap Manager::mDefaultSettings = CategorySettingValueMap(); CategorySettingValueMap Manager::mUserSettings = CategorySettingValueMap(); CategorySettingVector Manager::mChangedSettings = CategorySettingVector(); typedef std::map< CategorySetting, bool > CategorySettingStatusMap; class SettingsFileParser { public: SettingsFileParser() : mLine(0) {} void loadSettingsFile (const std::string& file, CategorySettingValueMap& settings) { mFile = file; boost::filesystem::ifstream stream; stream.open(boost::filesystem::path(file)); std::cout << "Loading settings file: " << file << std::endl; std::string currentCategory; mLine = 0; while (!stream.eof() && !stream.fail()) { ++mLine; std::string line; std::getline( stream, line ); size_t i = 0; if (!skipWhiteSpace(i, line)) continue; if (line[i] == '#') // skip comment continue; if (line[i] == '[') { size_t end = line.find(']', i); if (end == std::string::npos) fail("unterminated category"); currentCategory = line.substr(i+1, end - (i+1)); boost::algorithm::trim(currentCategory); i = end+1; } if (!skipWhiteSpace(i, line)) continue; if (currentCategory.empty()) fail("empty category name"); size_t settingEnd = line.find('=', i); if (settingEnd == std::string::npos) fail("unterminated setting name"); std::string setting = line.substr(i, (settingEnd-i)); boost::algorithm::trim(setting); size_t valueBegin = settingEnd+1; std::string value = line.substr(valueBegin); boost::algorithm::trim(value); if (settings.insert(std::make_pair(std::make_pair(currentCategory, setting), value)).second == false) fail(std::string("duplicate setting: [" + currentCategory + "] " + setting)); } } void saveSettingsFile (const std::string& file, CategorySettingValueMap& settings) { // No options have been written to the file yet. CategorySettingStatusMap written; for (CategorySettingValueMap::iterator it = settings.begin(); it != settings.end(); ++it) { written[it->first] = false; } // Have we substantively changed the settings file? bool changed = false; // Were there any lines at all in the file? bool existing = false; // The category/section we're currently in. std::string currentCategory; // Open the existing settings.cfg file to copy comments. This might not be the same file // as the output file if we're copying the setting from the default settings.cfg for the // first time. A minor change in API to pass the source file might be in order here. boost::filesystem::ifstream istream; boost::filesystem::path ipath(file); istream.open(ipath); // Create a new string stream to write the current settings to. It's likely that the // input file and the output file are the same, so this stream serves as a temporary file // of sorts. The setting files aren't very large so there's no performance issue. std::stringstream ostream; // For every line in the input file... while (!istream.eof() && !istream.fail()) { std::string line; std::getline(istream, line); // The current character position in the line. size_t i = 0; // Don't add additional newlines at the end of the file. if (istream.eof()) continue; // Copy entirely blank lines. if (!skipWhiteSpace(i, line)) { ostream << line << std::endl; continue; } // There were at least some comments in the input file. existing = true; // Copy comments. if (line[i] == '#') { ostream << line << std::endl; continue; } // Category heading. if (line[i] == '[') { size_t end = line.find(']', i); // This should never happen unless the player edited the file while playing. if (end == std::string::npos) { ostream << "# unterminated category: " << line << std::endl; changed = true; continue; } // Ensure that all options in the current category have been written. for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { if (mit->second == false && mit->first.first == currentCategory) { std::cout << "Added new setting: [" << currentCategory << "] " << mit->first.second << " = " << settings[mit->first] << std::endl; ostream << mit->first.second << " = " << settings[mit->first] << std::endl; mit->second = true; changed = true; } } // Update the current category. currentCategory = line.substr(i+1, end - (i+1)); boost::algorithm::trim(currentCategory); // Write the (new) current category to the file. ostream << "[" << currentCategory << "]" << std::endl; //std::cout << "Wrote category: " << currentCategory << std::endl; // A setting can apparently follow the category on an input line. That's rather // inconvenient, since it makes it more likely to have duplicative sections, // which our algorithm doesn't like. Do the best we can. i = end+1; } // Truncate trailing whitespace, since we're changing the file anayway. if (!skipWhiteSpace(i, line)) continue; // If we've found settings before the first category, something's wrong. This // should never happen unless the player edited the file while playing, since // the loadSettingsFile() logic rejects it. if (currentCategory.empty()) { ostream << "# empty category name: " << line << std::endl; changed = true; continue; } // Which setting was at this location in the input file? size_t settingEnd = line.find('=', i); // This should never happen unless the player edited the file while playing. if (settingEnd == std::string::npos) { ostream << "# unterminated setting name: " << line << std::endl; changed = true; continue; } std::string setting = line.substr(i, (settingEnd-i)); boost::algorithm::trim(setting); // Get the existing value so we can see if we've changed it. size_t valueBegin = settingEnd+1; std::string value = line.substr(valueBegin); boost::algorithm::trim(value); // Construct the setting map key to determine whether the setting has already been // written to the file. CategorySetting key = std::make_pair(currentCategory, setting); CategorySettingStatusMap::iterator finder = written.find(key); // Settings not in the written map are definitely invalid. Currently, this can only // happen if the player edited the file while playing, because loadSettingsFile() // will accept anything and pass it along in the map, but in the future, we might // want to handle invalid settings more gracefully here. if (finder == written.end()) { ostream << "# invalid setting: " << line << std::endl; changed = true; continue; } // Write the current value of the setting to the file. The key must exist in the // settings map because of how written was initialized and finder != end(). ostream << setting << " = " << settings[key] << std::endl; // Mark that setting as written. finder->second = true; // Did we really change it? if (value != settings[key]) { std::cout << "Changed setting: [" << currentCategory << "] " << setting << " = " << settings[key] << std::endl; changed = true; } // No need to write the current line, because we just emitted a replacement. // Curiously, it appears that comments at the ends of lines with settings are not // allowed, and the comment becomes part of the value. Was that intended? } // We're done with the input stream file. istream.close(); // Ensure that all options in the current category have been written. We must complete // the current category at the end of the file before moving on to any new categories. for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { if (mit->second == false && mit->first.first == currentCategory) { std::cout << "Added new setting: [" << mit->first.first << "] " << mit->first.second << " = " << settings[mit->first] << std::endl; ostream << mit->first.second << " = " << settings[mit->first] << std::endl; mit->second = true; changed = true; } } // If there was absolutely nothing in the file (or more likely the file didn't // exist), start the newly created file with a helpful comment. if (!existing) { ostream << "# This is the OpenMW user 'settings.cfg' file. This file only contains" << std::endl; ostream << "# explicitly changed settings. If you would like to revert a setting" << std::endl; ostream << "# to its default, simply remove it from this file. For available" << std::endl; ostream << "# settings, see the file 'settings-default.cfg' or the documentation at:" << std::endl; ostream << "#" << std::endl; ostream << "# https://wiki.openmw.org/index.php?title=Settings" << std::endl; } // We still have one more thing to do before we're completely done writing the file. // It's possible that there are entirely new categories, or that the input file had // disappeared completely, so we need ensure that all settings are written to the file // regardless of those issues. for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { // If the setting hasn't been written yet. if (mit->second == false) { // If the catgory has changed, write a new category header. if (mit->first.first != currentCategory) { currentCategory = mit->first.first; std::cout << "Created new setting section: " << mit->first.first << std::endl; ostream << std::endl; ostream << "[" << currentCategory << "]" << std::endl; } std::cout << "Added new setting: [" << mit->first.first << "] " << mit->first.second << " = " << settings[mit->first] << std::endl; // Then write the setting. No need to mark it as written because we're done. ostream << mit->first.second << " = " << settings[mit->first] << std::endl; changed = true; } } // Now install the newly written file in the requested place. if (changed) { std::cout << "Updating settings file: " << ipath << std::endl; boost::filesystem::ofstream ofstream; ofstream.open(ipath); ofstream << ostream.rdbuf(); ofstream.close(); } } private: /// Increment i until it longer points to a whitespace character /// in the string or has reached the end of the string. /// @return false if we have reached the end of the string bool skipWhiteSpace(size_t& i, std::string& str) { while (i < str.size() && std::isspace(str[i], std::locale::classic())) { ++i; } return i < str.size(); } void fail(const std::string& message) { std::stringstream error; error << "Error on line " << mLine << " in " << mFile << ":\n" << message; throw std::runtime_error(error.str()); } std::string mFile; int mLine; }; void Manager::clear() { mDefaultSettings.clear(); mUserSettings.clear(); mChangedSettings.clear(); } void Manager::loadDefault(const std::string &file) { SettingsFileParser parser; parser.loadSettingsFile(file, mDefaultSettings); } void Manager::loadUser(const std::string &file) { SettingsFileParser parser; parser.loadSettingsFile(file, mUserSettings); } void Manager::saveUser(const std::string &file) { SettingsFileParser parser; parser.saveSettingsFile(file, mUserSettings); } std::string Manager::getString(const std::string &setting, const std::string &category) { CategorySettingValueMap::key_type key = std::make_pair(category, setting); CategorySettingValueMap::iterator it = mUserSettings.find(key); if (it != mUserSettings.end()) return it->second; it = mDefaultSettings.find(key); if (it != mDefaultSettings.end()) return it->second; throw std::runtime_error(std::string("Trying to retrieve a non-existing setting: ") + setting + ".\nMake sure the settings-default.cfg file file was properly installed."); } float Manager::getFloat (const std::string& setting, const std::string& category) { return parseFloat( getString(setting, category) ); } int Manager::getInt (const std::string& setting, const std::string& category) { return parseInt( getString(setting, category) ); } bool Manager::getBool (const std::string& setting, const std::string& category) { return parseBool( getString(setting, category) ); } void Manager::setString(const std::string &setting, const std::string &category, const std::string &value) { CategorySettingValueMap::key_type key = std::make_pair(category, setting); CategorySettingValueMap::iterator found = mUserSettings.find(key); if (found != mUserSettings.end()) { if (found->second == value) return; } mUserSettings[key] = value; mChangedSettings.insert(key); } void Manager::setInt (const std::string& setting, const std::string& category, const int value) { setString(setting, category, toString(value)); } void Manager::setFloat (const std::string &setting, const std::string &category, const float value) { setString(setting, category, toString(value)); } void Manager::setBool(const std::string &setting, const std::string &category, const bool value) { setString(setting, category, toString(value)); } const CategorySettingVector Manager::apply() { CategorySettingVector vec = mChangedSettings; mChangedSettings.clear(); return vec; } } openmw-openmw-0.38.0/components/settings/settings.hpp000066400000000000000000000040351264522266000230170ustar00rootroot00000000000000#ifndef COMPONENTS_SETTINGS_H #define COMPONENTS_SETTINGS_H #include #include #include namespace Settings { typedef std::pair < std::string, std::string > CategorySetting; typedef std::set< std::pair > CategorySettingVector; typedef std::map < CategorySetting, std::string > CategorySettingValueMap; /// /// \brief Settings management (can change during runtime) /// class Manager { public: static CategorySettingValueMap mDefaultSettings; static CategorySettingValueMap mUserSettings; static CategorySettingVector mChangedSettings; ///< tracks all the settings that were changed since the last apply() call void clear(); ///< clears all settings and default settings void loadDefault (const std::string& file); ///< load file as the default settings (can be overridden by user settings) void loadUser (const std::string& file); ///< load file as user settings void saveUser (const std::string& file); ///< save user settings to file static const CategorySettingVector apply(); ///< returns the list of changed settings and then clears it static int getInt (const std::string& setting, const std::string& category); static float getFloat (const std::string& setting, const std::string& category); static std::string getString (const std::string& setting, const std::string& category); static bool getBool (const std::string& setting, const std::string& category); static void setInt (const std::string& setting, const std::string& category, const int value); static void setFloat (const std::string& setting, const std::string& category, const float value); static void setString (const std::string& setting, const std::string& category, const std::string& value); static void setBool (const std::string& setting, const std::string& category, const bool value); }; } #endif // _COMPONENTS_SETTINGS_H openmw-openmw-0.38.0/components/terrain/000077500000000000000000000000001264522266000202505ustar00rootroot00000000000000openmw-openmw-0.38.0/components/terrain/buffercache.cpp000066400000000000000000000176721264522266000232260ustar00rootroot00000000000000#include "buffercache.hpp" #include #include #include "defs.hpp" namespace { template osg::ref_ptr createIndexBuffer(unsigned int flags, unsigned int verts) { // LOD level n means every 2^n-th vertex is kept size_t lodLevel = (flags >> (4*4)); size_t lodDeltas[4]; for (int i=0; i<4; ++i) lodDeltas[i] = (flags >> (4*i)) & (0xf); bool anyDeltas = (lodDeltas[Terrain::North] || lodDeltas[Terrain::South] || lodDeltas[Terrain::West] || lodDeltas[Terrain::East]); size_t increment = 1 << lodLevel; assert(increment < verts); osg::ref_ptr indices (new IndexArrayType(osg::PrimitiveSet::TRIANGLES)); indices->reserve((verts-1)*(verts-1)*2*3 / increment); size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; // If any edge needs stitching we'll skip all edges at this point, // mainly because stitching one edge would have an effect on corners and on the adjacent edges if (anyDeltas) { colStart += increment; colEnd -= increment; rowEnd -= increment; rowStart += increment; } for (size_t row = rowStart; row < rowEnd; row += increment) { for (size_t col = colStart; col < colEnd; col += increment) { // diamond pattern if ((row + col%2) % 2 == 1) { indices->push_back(verts*(col+increment)+row); indices->push_back(verts*(col+increment)+row+increment); indices->push_back(verts*col+row+increment); indices->push_back(verts*col+row); indices->push_back(verts*(col+increment)+row); indices->push_back(verts*(col)+row+increment); } else { indices->push_back(verts*col+row); indices->push_back(verts*(col+increment)+row+increment); indices->push_back(verts*col+row+increment); indices->push_back(verts*col+row); indices->push_back(verts*(col+increment)+row); indices->push_back(verts*(col+increment)+row+increment); } } } size_t innerStep = increment; if (anyDeltas) { // Now configure LOD transitions at the edges - this is pretty tedious, // and some very long and boring code, but it works great // South size_t row = 0; size_t outerStep = 1 << (lodDeltas[Terrain::South] + lodLevel); for (size_t col = 0; col < verts-1; col += outerStep) { indices->push_back(verts*col+row); indices->push_back(verts*(col+outerStep)+row); // Make sure not to touch the right edge if (col+outerStep == verts-1) indices->push_back(verts*(col+outerStep-innerStep)+row+innerStep); else indices->push_back(verts*(col+outerStep)+row+innerStep); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the left or right edges if (col+i == 0 || col+i == verts-1-innerStep) continue; indices->push_back(verts*(col)+row); indices->push_back(verts*(col+i+innerStep)+row+innerStep); indices->push_back(verts*(col+i)+row+innerStep); } } // North row = verts-1; outerStep = size_t(1) << (lodDeltas[Terrain::North] + lodLevel); for (size_t col = 0; col < verts-1; col += outerStep) { indices->push_back(verts*(col+outerStep)+row); indices->push_back(verts*col+row); // Make sure not to touch the left edge if (col == 0) indices->push_back(verts*(col+innerStep)+row-innerStep); else indices->push_back(verts*col+row-innerStep); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the left or right edges if (col+i == 0 || col+i == verts-1-innerStep) continue; indices->push_back(verts*(col+i)+row-innerStep); indices->push_back(verts*(col+i+innerStep)+row-innerStep); indices->push_back(verts*(col+outerStep)+row); } } // West size_t col = 0; outerStep = size_t(1) << (lodDeltas[Terrain::West] + lodLevel); for (size_t row = 0; row < verts-1; row += outerStep) { indices->push_back(verts*col+row+outerStep); indices->push_back(verts*col+row); // Make sure not to touch the top edge if (row+outerStep == verts-1) indices->push_back(verts*(col+innerStep)+row+outerStep-innerStep); else indices->push_back(verts*(col+innerStep)+row+outerStep); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the top or bottom edges if (row+i == 0 || row+i == verts-1-innerStep) continue; indices->push_back(verts*col+row); indices->push_back(verts*(col+innerStep)+row+i); indices->push_back(verts*(col+innerStep)+row+i+innerStep); } } // East col = verts-1; outerStep = size_t(1) << (lodDeltas[Terrain::East] + lodLevel); for (size_t row = 0; row < verts-1; row += outerStep) { indices->push_back(verts*col+row); indices->push_back(verts*col+row+outerStep); // Make sure not to touch the bottom edge if (row == 0) indices->push_back(verts*(col-innerStep)+row+innerStep); else indices->push_back(verts*(col-innerStep)+row); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the top or bottom edges if (row+i == 0 || row+i == verts-1-innerStep) continue; indices->push_back(verts*col+row+outerStep); indices->push_back(verts*(col-innerStep)+row+i+innerStep); indices->push_back(verts*(col-innerStep)+row+i); } } } return indices; } } namespace Terrain { osg::ref_ptr BufferCache::getUVBuffer() { if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end()) { return mUvBufferMap[mNumVerts]; } int vertexCount = mNumVerts * mNumVerts; osg::ref_ptr uvs (new osg::Vec2Array); uvs->reserve(vertexCount); for (unsigned int col = 0; col < mNumVerts; ++col) { for (unsigned int row = 0; row < mNumVerts; ++row) { uvs->push_back(osg::Vec2f(col / static_cast(mNumVerts-1), row / static_cast(mNumVerts-1))); } } // Assign a VBO here to enable state sharing between different Geometries. uvs->setVertexBufferObject(new osg::VertexBufferObject); mUvBufferMap[mNumVerts] = uvs; return uvs; } osg::ref_ptr BufferCache::getIndexBuffer(unsigned int flags) { unsigned int verts = mNumVerts; if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) { return mIndexBufferMap[flags]; } osg::ref_ptr buffer; if (verts*verts > (0xffffu)) buffer = createIndexBuffer(flags, verts); else buffer = createIndexBuffer(flags, verts); // Assign a EBO here to enable state sharing between different Geometries. buffer->setElementBufferObject(new osg::ElementBufferObject); mIndexBufferMap[flags] = buffer; return buffer; } } openmw-openmw-0.38.0/components/terrain/buffercache.hpp000066400000000000000000000022041264522266000232140ustar00rootroot00000000000000#ifndef COMPONENTS_TERRAIN_BUFFERCACHE_H #define COMPONENTS_TERRAIN_BUFFERCACHE_H #include #include #include namespace Terrain { /// @brief Implements creation and caching of vertex buffers for terrain chunks. class BufferCache { public: BufferCache(unsigned int numVerts) : mNumVerts(numVerts) {} /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each) /// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices) osg::ref_ptr getIndexBuffer (unsigned int flags); osg::ref_ptr getUVBuffer(); // TODO: add releaseGLObjects() for our vertex/element buffer objects private: // Index buffers are shared across terrain batches where possible. There is one index buffer for each // combination of LOD deltas and index buffer LOD we may need. std::map > mIndexBufferMap; std::map > mUvBufferMap; unsigned int mNumVerts; }; } #endif openmw-openmw-0.38.0/components/terrain/defs.hpp000066400000000000000000000007241264522266000217050ustar00rootroot00000000000000#ifndef COMPONENTS_TERRAIN_DEFS_HPP #define COMPONENTS_TERRAIN_DEFS_HPP #include namespace Terrain { enum Direction { North = 0, East = 1, South = 2, West = 3 }; struct LayerInfo { std::string mDiffuseMap; std::string mNormalMap; bool mParallax; // Height info in normal map alpha channel? bool mSpecular; // Specular info in diffuse map alpha channel? }; } #endif openmw-openmw-0.38.0/components/terrain/material.cpp000066400000000000000000000066361264522266000225650ustar00rootroot00000000000000#include "material.hpp" #include #include #include #include #include #include namespace Terrain { FixedFunctionTechnique::FixedFunctionTechnique(const std::vector >& layers, const std::vector >& blendmaps, int blendmapScale, float layerTileSize) { bool firstLayer = true; int i=0; for (std::vector >::const_iterator it = layers.begin(); it != layers.end(); ++it) { osg::ref_ptr stateset (new osg::StateSet); if (!firstLayer) { stateset->setMode(GL_BLEND, osg::StateAttribute::ON); osg::ref_ptr depth (new osg::Depth); depth->setFunction(osg::Depth::EQUAL); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); } int texunit = 0; if(!firstLayer) { osg::ref_ptr blendmap = blendmaps.at(i++); stateset->setTextureAttributeAndModes(texunit, blendmap.get()); // This is to map corner vertices directly to the center of a blendmap texel. osg::Matrixf texMat; float scale = (blendmapScale/(static_cast(blendmapScale)+1.f)); texMat.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f)); texMat.preMultScale(osg::Vec3f(scale, scale, 1.f)); texMat.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f)); stateset->setTextureAttributeAndModes(texunit, new osg::TexMat(texMat)); osg::ref_ptr texEnvCombine (new osg::TexEnvCombine); texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE); texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); stateset->setTextureAttributeAndModes(texunit, texEnvCombine, osg::StateAttribute::ON); ++texunit; } // Add the actual layer texture multiplied by the alpha map. osg::ref_ptr tex = *it; stateset->setTextureAttributeAndModes(texunit, tex.get()); osg::ref_ptr texMat (new osg::TexMat); texMat->setMatrix(osg::Matrix::scale(osg::Vec3f(layerTileSize,layerTileSize,1.f))); stateset->setTextureAttributeAndModes(texunit, texMat, osg::StateAttribute::ON); firstLayer = false; addPass(stateset); } } Effect::Effect(const std::vector > &layers, const std::vector > &blendmaps, int blendmapScale, float layerTileSize) : mLayers(layers) , mBlendmaps(blendmaps) , mBlendmapScale(blendmapScale) , mLayerTileSize(layerTileSize) { osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); selectTechnique(0); } bool Effect::define_techniques() { addTechnique(new FixedFunctionTechnique(mLayers, mBlendmaps, mBlendmapScale, mLayerTileSize)); return true; } } openmw-openmw-0.38.0/components/terrain/material.hpp000066400000000000000000000025501264522266000225610ustar00rootroot00000000000000#ifndef COMPONENTS_TERRAIN_MATERIAL_H #define COMPONENTS_TERRAIN_MATERIAL_H #include #include #include "defs.hpp" namespace osg { class Texture2D; } namespace Terrain { class FixedFunctionTechnique : public osgFX::Technique { public: FixedFunctionTechnique( const std::vector >& layers, const std::vector >& blendmaps, int blendmapScale, float layerTileSize); protected: virtual void define_passes() {} }; class Effect : public osgFX::Effect { public: Effect( const std::vector >& layers, const std::vector >& blendmaps, int blendmapScale, float layerTileSize); virtual bool define_techniques(); virtual const char *effectName() const { return NULL; } virtual const char *effectDescription() const { return NULL; } virtual const char *effectAuthor() const { return NULL; } private: std::vector > mLayers; std::vector > mBlendmaps; int mBlendmapScale; float mLayerTileSize; }; } #endif openmw-openmw-0.38.0/components/terrain/storage.cpp000066400000000000000000000000271264522266000224170ustar00rootroot00000000000000#include "storage.hpp" openmw-openmw-0.38.0/components/terrain/storage.hpp000066400000000000000000000077351264522266000224410ustar00rootroot00000000000000#ifndef COMPONENTS_TERRAIN_STORAGE_H #define COMPONENTS_TERRAIN_STORAGE_H #include #include #include #include #include #include "defs.hpp" namespace osg { class Image; } namespace Terrain { /// We keep storage of terrain data abstract here since we need different implementations for game and editor class Storage { public: virtual ~Storage() {} public: /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; /// Get the minimum and maximum heights of a terrain region. /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree. /// Larger chunks can simply merge AABB of children. /// @param size size of the chunk in cell units /// @param center center of the chunk in cell units /// @param min min height will be stored here /// @param max max height will be stored here /// @return true if there was data available for this terrain chunk virtual bool getMinMaxHeights (float size, const osg::Vec2f& center, float& min, float& max) = 0; /// Fill vertex buffers for a terrain chunk. /// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue. /// @note Vertices should be written in row-major order (a row is defined as parallel to the x-axis). /// The specified positions should be in local space, i.e. relative to the center of the terrain chunk. /// @param lodLevel LOD level, 0 = most detailed /// @param size size of the terrain chunk in cell units /// @param center center of the chunk in cell units /// @param positions buffer to write vertices /// @param normals buffer to write vertex normals /// @param colours buffer to write vertex colours virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, osg::ref_ptr positions, osg::ref_ptr normals, osg::ref_ptr colours) = 0; typedef std::vector > ImageVector; /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. /// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @param chunkSize size of the terrain chunk in cell units /// @param chunkCenter center of the chunk in cell units /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - /// otherwise, each texture contains blend values for one layer only. Shader-based rendering /// can utilize packing, FFP can't. /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, bool pack, ImageVector& blendmaps, std::vector& layerList) = 0; virtual float getHeightAt (const osg::Vec3f& worldPos) = 0; virtual LayerInfo getDefaultLayer() = 0; /// Get the transformation factor for mapping cell units to world units. virtual float getCellWorldSize() = 0; /// Get the number of vertices on one side for each cell. Should be (power of two)+1 virtual int getCellVertices() = 0; }; } #endif openmw-openmw-0.38.0/components/terrain/terraingrid.cpp000066400000000000000000000176751264522266000233060ustar00rootroot00000000000000#include "terraingrid.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "material.hpp" #include "storage.hpp" namespace { class StaticBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback { public: StaticBoundingBoxCallback(const osg::BoundingBox& bounds) : mBoundingBox(bounds) { } virtual osg::BoundingBox computeBound(const osg::Drawable&) const { return mBoundingBox; } private: osg::BoundingBox mBoundingBox; }; } namespace Terrain { TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask) : Terrain::World(parent, resourceSystem, ico, storage, nodeMask) , mNumSplits(4) , mKdTreeBuilder(new osg::KdTreeBuilder) { mCache = BufferCache((storage->getCellVertices()-1)/static_cast(mNumSplits) + 1); } TerrainGrid::~TerrainGrid() { while (!mGrid.empty()) { unloadCell(mGrid.begin()->first.first, mGrid.begin()->first.second); } } class GridElement { public: osg::ref_ptr mNode; }; osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter) { if (chunkSize * mNumSplits > 1.f) { // keep splitting osg::ref_ptr group (new osg::Group); if (parent) parent->addChild(group); float newChunkSize = chunkSize/2.f; buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize/2.f, newChunkSize/2.f)); buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize/2.f, -newChunkSize/2.f)); buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize/2.f, newChunkSize/2.f)); buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize/2.f, -newChunkSize/2.f)); return group; } else { float minH, maxH; if (!mStorage->getMinMaxHeights(chunkSize, chunkCenter, minH, maxH)) return NULL; // no terrain defined osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); if (parent) parent->addChild(transform); osg::ref_ptr positions (new osg::Vec3Array); osg::ref_ptr normals (new osg::Vec3Array); osg::ref_ptr colors (new osg::Vec4Array); osg::ref_ptr vbo (new osg::VertexBufferObject); positions->setVertexBufferObject(vbo); normals->setVertexBufferObject(vbo); colors->setVertexBufferObject(vbo); mStorage->fillVertexBuffers(0, chunkSize, chunkCenter, positions, normals, colors); osg::ref_ptr geometry (new osg::Geometry); geometry->setVertexArray(positions); geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX); geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); geometry->setUseDisplayList(false); geometry->setUseVertexBufferObjects(true); geometry->addPrimitiveSet(mCache.getIndexBuffer(0)); // we already know the bounding box, so no need to let OSG compute it. osg::Vec3f min(-0.5f*mStorage->getCellWorldSize()*chunkSize, -0.5f*mStorage->getCellWorldSize()*chunkSize, minH); osg::Vec3f max (0.5f*mStorage->getCellWorldSize()*chunkSize, 0.5f*mStorage->getCellWorldSize()*chunkSize, maxH); osg::BoundingBox bounds(min, max); geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds)); std::vector layerList; std::vector > blendmaps; mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList); // For compiling textures, I don't think the osgFX::Effect does it correctly osg::ref_ptr textureCompileDummy (new osg::Node); std::vector > layerTextures; for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) { layerTextures.push_back(mResourceSystem->getTextureManager()->getTexture2D(it->mDiffuseMap, osg::Texture::REPEAT, osg::Texture::REPEAT)); textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(0, layerTextures.back()); } std::vector > blendmapTextures; for (std::vector >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it) { osg::ref_ptr texture (new osg::Texture2D); texture->setImage(*it); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture->setResizeNonPowerOfTwoHint(false); blendmapTextures.push_back(texture); textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(0, layerTextures.back()); } // use texture coordinates for both texture units, the layer texture and blend texture for (unsigned int i=0; i<2; ++i) geometry->setTexCoordArray(i, mCache.getUVBuffer()); float blendmapScale = ESM::Land::LAND_TEXTURE_SIZE*chunkSize; osg::ref_ptr effect (new Terrain::Effect(layerTextures, blendmapTextures, blendmapScale, blendmapScale)); effect->addCullCallback(new SceneUtil::LightListCallback); transform->addChild(effect); #if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) osg::Node* toAttach = geometry.get(); #else osg::ref_ptr geode (new osg::Geode); geode->addDrawable(geometry); osg::Node* toAttach = geode.get(); #endif effect->addChild(toAttach); if (mIncrementalCompileOperation) { mIncrementalCompileOperation->add(toAttach); mIncrementalCompileOperation->add(textureCompileDummy); } return transform; } } void TerrainGrid::loadCell(int x, int y) { if (mGrid.find(std::make_pair(x, y)) != mGrid.end()) return; // already loaded osg::Vec2f center(x+0.5f, y+0.5f); osg::ref_ptr terrainNode = buildTerrain(NULL, 1.f, center); if (!terrainNode) return; // no terrain defined std::auto_ptr element (new GridElement); element->mNode = terrainNode; mTerrainRoot->addChild(element->mNode); // kdtree probably not needed with mNumSplits=4 /* // build a kdtree to speed up intersection tests with the terrain // Note, the build could be optimized using a custom kdtree builder, since we know that the terrain can be represented by a quadtree geode->accept(*mKdTreeBuilder); */ mGrid[std::make_pair(x,y)] = element.release(); } void TerrainGrid::unloadCell(int x, int y) { Grid::iterator it = mGrid.find(std::make_pair(x,y)); if (it == mGrid.end()) return; GridElement* element = it->second; mTerrainRoot->removeChild(element->mNode); delete element; mGrid.erase(it); } } openmw-openmw-0.38.0/components/terrain/terraingrid.hpp000066400000000000000000000020661264522266000232770ustar00rootroot00000000000000#ifndef COMPONENTS_TERRAIN_TERRAINGRID_H #define COMPONENTS_TERRAIN_TERRAINGRID_H #include #include "world.hpp" #include "material.hpp" namespace osg { class KdTreeBuilder; } namespace Terrain { class GridElement; /// @brief Simple terrain implementation that loads cells in a grid, with no LOD class TerrainGrid : public Terrain::World { public: TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask); ~TerrainGrid(); virtual void loadCell(int x, int y); virtual void unloadCell(int x, int y); private: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); // split each ESM::Cell into mNumSplits*mNumSplits terrain chunks unsigned int mNumSplits; typedef std::map, GridElement*> Grid; Grid mGrid; osg::ref_ptr mKdTreeBuilder; }; } #endif openmw-openmw-0.38.0/components/terrain/world.cpp000066400000000000000000000015301264522266000221020ustar00rootroot00000000000000#include "world.hpp" #include #include #include "storage.hpp" namespace Terrain { World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask) : mStorage(storage) , mCache(storage->getCellVertices()) , mParent(parent) , mResourceSystem(resourceSystem) , mIncrementalCompileOperation(ico) { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); mParent->addChild(mTerrainRoot); } World::~World() { mParent->removeChild(mTerrainRoot); delete mStorage; } float World::getHeightAt(const osg::Vec3f &worldPos) { return mStorage->getHeightAt(worldPos); } } openmw-openmw-0.38.0/components/terrain/world.hpp000066400000000000000000000027631264522266000221200ustar00rootroot00000000000000#ifndef COMPONENTS_TERRAIN_WORLD_H #define COMPONENTS_TERRAIN_WORLD_H #include #include "defs.hpp" #include "buffercache.hpp" namespace osg { class Group; } namespace osgUtil { class IncrementalCompileOperation; } namespace Resource { class ResourceSystem; } namespace Terrain { class Storage; /** * @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed * is up to the implementation. */ class World { public: /// @note takes ownership of \a storage /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param nodeMask mask for the terrain root World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask); virtual ~World(); float getHeightAt (const osg::Vec3f& worldPos); // This is only a hint and may be ignored by the implementation. virtual void loadCell(int x, int y) {} virtual void unloadCell(int x, int y) {} Storage* getStorage() { return mStorage; } protected: Storage* mStorage; BufferCache mCache; osg::ref_ptr mParent; osg::ref_ptr mTerrainRoot; Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mIncrementalCompileOperation; }; } #endif openmw-openmw-0.38.0/components/to_utf8/000077500000000000000000000000001264522266000201745ustar00rootroot00000000000000openmw-openmw-0.38.0/components/to_utf8/.gitignore000066400000000000000000000000121264522266000221550ustar00rootroot00000000000000gen_iconv openmw-openmw-0.38.0/components/to_utf8/Makefile000066400000000000000000000002001264522266000216240ustar00rootroot00000000000000tables_gen.hpp: gen_iconv ./gen_iconv > tables_gen.hpp gen_iconv: gen_iconv.cpp g++ -Wall $^ -o $@ clean: rm -f ./gen_iconvopenmw-openmw-0.38.0/components/to_utf8/gen_iconv.cpp000066400000000000000000000060121264522266000226460ustar00rootroot00000000000000// This program generates the file tables_gen.hpp #include using namespace std; #include #include void tab() { cout << " "; } // write one number with a space in front of it and a comma after it void num(char i, bool last) { // Convert i to its integer value, i.e. -128 to 127. Printing it directly // would result in non-printable characters in the source code, which is bad. cout << " " << static_cast(i); if(!last) cout << ","; } // Write one table entry (UTF8 value), 1-5 bytes void writeChar(char *value, int length, bool last, const std::string &comment="") { assert(length >= 1 && length <= 5); tab(); num(length, false); for(int i=0;i<5;i++) num(value[i], last && i==4); if(comment != "") cout << " // " << comment; cout << endl; } // What to write on missing characters void writeMissing(bool last) { // Just write a space character char value[5]; value[0] = ' '; for(int i=1; i<5; i++) value[i] = 0; writeChar(value, 1, last, "not part of this charset"); } int write_table(const std::string &charset, const std::string &tableName) { // Write table header cout << "static signed char " << tableName << "[] =\n{\n"; // Open conversion system iconv_t cd = iconv_open ("UTF-8", charset.c_str()); // Convert each character from 0 to 255 for(int i=0; i<256; i++) { bool last = (i==255); char input = i; char *iptr = &input; size_t ileft = 1; char output[5]; for(int k=0; k<5; k++) output[k] = 0; char *optr = output; size_t oleft = 5; size_t res = iconv(cd, &iptr, &ileft, &optr, &oleft); if(res) writeMissing(last); else writeChar(output, 5-oleft, last); } iconv_close (cd); // Finish table cout << "};\n"; return 0; } int main() { // Write header guard cout << "#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H\n#define COMPONENTS_TOUTF8_TABLE_GEN_H\n\n"; // Write namespace cout << "namespace ToUTF8\n{\n\n"; // Central European and Eastern European languages that use Latin script, such as // Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian. cout << "\n/// Central European and Eastern European languages that use Latin script," "\n/// such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian," "\n/// Serbian (Latin script), Romanian and Albanian." "\n"; write_table("WINDOWS-1250", "windows_1250"); // Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages cout << "\n/// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic" "\n/// and other languages" "\n"; write_table("WINDOWS-1251", "windows_1251"); // English cout << "\n/// Latin alphabet used by English and some other Western languages" "\n"; write_table("WINDOWS-1252", "windows_1252"); write_table("CP437", "cp437"); // Close namespace cout << "\n}\n\n"; // Close header guard cout << "#endif\n\n"; return 0; } openmw-openmw-0.38.0/components/to_utf8/tables_gen.hpp000066400000000000000000000637561264522266000230310ustar00rootroot00000000000000#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H #define COMPONENTS_TOUTF8_TABLE_GEN_H namespace ToUTF8 { /// Central European and Eastern European languages that use Latin script, /// such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, /// Serbian (Latin script), Romanian and Albanian. static signed char windows_1250[] = { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 1, 7, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 1, 9, 0, 0, 0, 0, 1, 10, 0, 0, 0, 0, 1, 11, 0, 0, 0, 0, 1, 12, 0, 0, 0, 0, 1, 13, 0, 0, 0, 0, 1, 14, 0, 0, 0, 0, 1, 15, 0, 0, 0, 0, 1, 16, 0, 0, 0, 0, 1, 17, 0, 0, 0, 0, 1, 18, 0, 0, 0, 0, 1, 19, 0, 0, 0, 0, 1, 20, 0, 0, 0, 0, 1, 21, 0, 0, 0, 0, 1, 22, 0, 0, 0, 0, 1, 23, 0, 0, 0, 0, 1, 24, 0, 0, 0, 0, 1, 25, 0, 0, 0, 0, 1, 26, 0, 0, 0, 0, 1, 27, 0, 0, 0, 0, 1, 28, 0, 0, 0, 0, 1, 29, 0, 0, 0, 0, 1, 30, 0, 0, 0, 0, 1, 31, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 1, 33, 0, 0, 0, 0, 1, 34, 0, 0, 0, 0, 1, 35, 0, 0, 0, 0, 1, 36, 0, 0, 0, 0, 1, 37, 0, 0, 0, 0, 1, 38, 0, 0, 0, 0, 1, 39, 0, 0, 0, 0, 1, 40, 0, 0, 0, 0, 1, 41, 0, 0, 0, 0, 1, 42, 0, 0, 0, 0, 1, 43, 0, 0, 0, 0, 1, 44, 0, 0, 0, 0, 1, 45, 0, 0, 0, 0, 1, 46, 0, 0, 0, 0, 1, 47, 0, 0, 0, 0, 1, 48, 0, 0, 0, 0, 1, 49, 0, 0, 0, 0, 1, 50, 0, 0, 0, 0, 1, 51, 0, 0, 0, 0, 1, 52, 0, 0, 0, 0, 1, 53, 0, 0, 0, 0, 1, 54, 0, 0, 0, 0, 1, 55, 0, 0, 0, 0, 1, 56, 0, 0, 0, 0, 1, 57, 0, 0, 0, 0, 1, 58, 0, 0, 0, 0, 1, 59, 0, 0, 0, 0, 1, 60, 0, 0, 0, 0, 1, 61, 0, 0, 0, 0, 1, 62, 0, 0, 0, 0, 1, 63, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 1, 65, 0, 0, 0, 0, 1, 66, 0, 0, 0, 0, 1, 67, 0, 0, 0, 0, 1, 68, 0, 0, 0, 0, 1, 69, 0, 0, 0, 0, 1, 70, 0, 0, 0, 0, 1, 71, 0, 0, 0, 0, 1, 72, 0, 0, 0, 0, 1, 73, 0, 0, 0, 0, 1, 74, 0, 0, 0, 0, 1, 75, 0, 0, 0, 0, 1, 76, 0, 0, 0, 0, 1, 77, 0, 0, 0, 0, 1, 78, 0, 0, 0, 0, 1, 79, 0, 0, 0, 0, 1, 80, 0, 0, 0, 0, 1, 81, 0, 0, 0, 0, 1, 82, 0, 0, 0, 0, 1, 83, 0, 0, 0, 0, 1, 84, 0, 0, 0, 0, 1, 85, 0, 0, 0, 0, 1, 86, 0, 0, 0, 0, 1, 87, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 1, 89, 0, 0, 0, 0, 1, 90, 0, 0, 0, 0, 1, 91, 0, 0, 0, 0, 1, 92, 0, 0, 0, 0, 1, 93, 0, 0, 0, 0, 1, 94, 0, 0, 0, 0, 1, 95, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 1, 97, 0, 0, 0, 0, 1, 98, 0, 0, 0, 0, 1, 99, 0, 0, 0, 0, 1, 100, 0, 0, 0, 0, 1, 101, 0, 0, 0, 0, 1, 102, 0, 0, 0, 0, 1, 103, 0, 0, 0, 0, 1, 104, 0, 0, 0, 0, 1, 105, 0, 0, 0, 0, 1, 106, 0, 0, 0, 0, 1, 107, 0, 0, 0, 0, 1, 108, 0, 0, 0, 0, 1, 109, 0, 0, 0, 0, 1, 110, 0, 0, 0, 0, 1, 111, 0, 0, 0, 0, 1, 112, 0, 0, 0, 0, 1, 113, 0, 0, 0, 0, 1, 114, 0, 0, 0, 0, 1, 115, 0, 0, 0, 0, 1, 116, 0, 0, 0, 0, 1, 117, 0, 0, 0, 0, 1, 118, 0, 0, 0, 0, 1, 119, 0, 0, 0, 0, 1, 120, 0, 0, 0, 0, 1, 121, 0, 0, 0, 0, 1, 122, 0, 0, 0, 0, 1, 123, 0, 0, 0, 0, 1, 124, 0, 0, 0, 0, 1, 125, 0, 0, 0, 0, 1, 126, 0, 0, 0, 0, 1, 127, 0, 0, 0, 0, 3, -30, -126, -84, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 3, -30, -128, -102, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 3, -30, -128, -98, 0, 0, 3, -30, -128, -90, 0, 0, 3, -30, -128, -96, 0, 0, 3, -30, -128, -95, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 3, -30, -128, -80, 0, 0, 2, -59, -96, 0, 0, 0, 3, -30, -128, -71, 0, 0, 2, -59, -102, 0, 0, 0, 2, -59, -92, 0, 0, 0, 2, -59, -67, 0, 0, 0, 2, -59, -71, 0, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 3, -30, -128, -104, 0, 0, 3, -30, -128, -103, 0, 0, 3, -30, -128, -100, 0, 0, 3, -30, -128, -99, 0, 0, 3, -30, -128, -94, 0, 0, 3, -30, -128, -109, 0, 0, 3, -30, -128, -108, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 3, -30, -124, -94, 0, 0, 2, -59, -95, 0, 0, 0, 3, -30, -128, -70, 0, 0, 2, -59, -101, 0, 0, 0, 2, -59, -91, 0, 0, 0, 2, -59, -66, 0, 0, 0, 2, -59, -70, 0, 0, 0, 2, -62, -96, 0, 0, 0, 2, -53, -121, 0, 0, 0, 2, -53, -104, 0, 0, 0, 2, -59, -127, 0, 0, 0, 2, -62, -92, 0, 0, 0, 2, -60, -124, 0, 0, 0, 2, -62, -90, 0, 0, 0, 2, -62, -89, 0, 0, 0, 2, -62, -88, 0, 0, 0, 2, -62, -87, 0, 0, 0, 2, -59, -98, 0, 0, 0, 2, -62, -85, 0, 0, 0, 2, -62, -84, 0, 0, 0, 2, -62, -83, 0, 0, 0, 2, -62, -82, 0, 0, 0, 2, -59, -69, 0, 0, 0, 2, -62, -80, 0, 0, 0, 2, -62, -79, 0, 0, 0, 2, -53, -101, 0, 0, 0, 2, -59, -126, 0, 0, 0, 2, -62, -76, 0, 0, 0, 2, -62, -75, 0, 0, 0, 2, -62, -74, 0, 0, 0, 2, -62, -73, 0, 0, 0, 2, -62, -72, 0, 0, 0, 2, -60, -123, 0, 0, 0, 2, -59, -97, 0, 0, 0, 2, -62, -69, 0, 0, 0, 2, -60, -67, 0, 0, 0, 2, -53, -99, 0, 0, 0, 2, -60, -66, 0, 0, 0, 2, -59, -68, 0, 0, 0, 2, -59, -108, 0, 0, 0, 2, -61, -127, 0, 0, 0, 2, -61, -126, 0, 0, 0, 2, -60, -126, 0, 0, 0, 2, -61, -124, 0, 0, 0, 2, -60, -71, 0, 0, 0, 2, -60, -122, 0, 0, 0, 2, -61, -121, 0, 0, 0, 2, -60, -116, 0, 0, 0, 2, -61, -119, 0, 0, 0, 2, -60, -104, 0, 0, 0, 2, -61, -117, 0, 0, 0, 2, -60, -102, 0, 0, 0, 2, -61, -115, 0, 0, 0, 2, -61, -114, 0, 0, 0, 2, -60, -114, 0, 0, 0, 2, -60, -112, 0, 0, 0, 2, -59, -125, 0, 0, 0, 2, -59, -121, 0, 0, 0, 2, -61, -109, 0, 0, 0, 2, -61, -108, 0, 0, 0, 2, -59, -112, 0, 0, 0, 2, -61, -106, 0, 0, 0, 2, -61, -105, 0, 0, 0, 2, -59, -104, 0, 0, 0, 2, -59, -82, 0, 0, 0, 2, -61, -102, 0, 0, 0, 2, -59, -80, 0, 0, 0, 2, -61, -100, 0, 0, 0, 2, -61, -99, 0, 0, 0, 2, -59, -94, 0, 0, 0, 2, -61, -97, 0, 0, 0, 2, -59, -107, 0, 0, 0, 2, -61, -95, 0, 0, 0, 2, -61, -94, 0, 0, 0, 2, -60, -125, 0, 0, 0, 2, -61, -92, 0, 0, 0, 2, -60, -70, 0, 0, 0, 2, -60, -121, 0, 0, 0, 2, -61, -89, 0, 0, 0, 2, -60, -115, 0, 0, 0, 2, -61, -87, 0, 0, 0, 2, -60, -103, 0, 0, 0, 2, -61, -85, 0, 0, 0, 2, -60, -101, 0, 0, 0, 2, -61, -83, 0, 0, 0, 2, -61, -82, 0, 0, 0, 2, -60, -113, 0, 0, 0, 2, -60, -111, 0, 0, 0, 2, -59, -124, 0, 0, 0, 2, -59, -120, 0, 0, 0, 2, -61, -77, 0, 0, 0, 2, -61, -76, 0, 0, 0, 2, -59, -111, 0, 0, 0, 2, -61, -74, 0, 0, 0, 2, -61, -73, 0, 0, 0, 2, -59, -103, 0, 0, 0, 2, -59, -81, 0, 0, 0, 2, -61, -70, 0, 0, 0, 2, -59, -79, 0, 0, 0, 2, -61, -68, 0, 0, 0, 2, -61, -67, 0, 0, 0, 2, -59, -93, 0, 0, 0, 2, -53, -103, 0, 0, 0 }; /// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic /// and other languages static signed char windows_1251[] = { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 1, 7, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 1, 9, 0, 0, 0, 0, 1, 10, 0, 0, 0, 0, 1, 11, 0, 0, 0, 0, 1, 12, 0, 0, 0, 0, 1, 13, 0, 0, 0, 0, 1, 14, 0, 0, 0, 0, 1, 15, 0, 0, 0, 0, 1, 16, 0, 0, 0, 0, 1, 17, 0, 0, 0, 0, 1, 18, 0, 0, 0, 0, 1, 19, 0, 0, 0, 0, 1, 20, 0, 0, 0, 0, 1, 21, 0, 0, 0, 0, 1, 22, 0, 0, 0, 0, 1, 23, 0, 0, 0, 0, 1, 24, 0, 0, 0, 0, 1, 25, 0, 0, 0, 0, 1, 26, 0, 0, 0, 0, 1, 27, 0, 0, 0, 0, 1, 28, 0, 0, 0, 0, 1, 29, 0, 0, 0, 0, 1, 30, 0, 0, 0, 0, 1, 31, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 1, 33, 0, 0, 0, 0, 1, 34, 0, 0, 0, 0, 1, 35, 0, 0, 0, 0, 1, 36, 0, 0, 0, 0, 1, 37, 0, 0, 0, 0, 1, 38, 0, 0, 0, 0, 1, 39, 0, 0, 0, 0, 1, 40, 0, 0, 0, 0, 1, 41, 0, 0, 0, 0, 1, 42, 0, 0, 0, 0, 1, 43, 0, 0, 0, 0, 1, 44, 0, 0, 0, 0, 1, 45, 0, 0, 0, 0, 1, 46, 0, 0, 0, 0, 1, 47, 0, 0, 0, 0, 1, 48, 0, 0, 0, 0, 1, 49, 0, 0, 0, 0, 1, 50, 0, 0, 0, 0, 1, 51, 0, 0, 0, 0, 1, 52, 0, 0, 0, 0, 1, 53, 0, 0, 0, 0, 1, 54, 0, 0, 0, 0, 1, 55, 0, 0, 0, 0, 1, 56, 0, 0, 0, 0, 1, 57, 0, 0, 0, 0, 1, 58, 0, 0, 0, 0, 1, 59, 0, 0, 0, 0, 1, 60, 0, 0, 0, 0, 1, 61, 0, 0, 0, 0, 1, 62, 0, 0, 0, 0, 1, 63, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 1, 65, 0, 0, 0, 0, 1, 66, 0, 0, 0, 0, 1, 67, 0, 0, 0, 0, 1, 68, 0, 0, 0, 0, 1, 69, 0, 0, 0, 0, 1, 70, 0, 0, 0, 0, 1, 71, 0, 0, 0, 0, 1, 72, 0, 0, 0, 0, 1, 73, 0, 0, 0, 0, 1, 74, 0, 0, 0, 0, 1, 75, 0, 0, 0, 0, 1, 76, 0, 0, 0, 0, 1, 77, 0, 0, 0, 0, 1, 78, 0, 0, 0, 0, 1, 79, 0, 0, 0, 0, 1, 80, 0, 0, 0, 0, 1, 81, 0, 0, 0, 0, 1, 82, 0, 0, 0, 0, 1, 83, 0, 0, 0, 0, 1, 84, 0, 0, 0, 0, 1, 85, 0, 0, 0, 0, 1, 86, 0, 0, 0, 0, 1, 87, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 1, 89, 0, 0, 0, 0, 1, 90, 0, 0, 0, 0, 1, 91, 0, 0, 0, 0, 1, 92, 0, 0, 0, 0, 1, 93, 0, 0, 0, 0, 1, 94, 0, 0, 0, 0, 1, 95, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 1, 97, 0, 0, 0, 0, 1, 98, 0, 0, 0, 0, 1, 99, 0, 0, 0, 0, 1, 100, 0, 0, 0, 0, 1, 101, 0, 0, 0, 0, 1, 102, 0, 0, 0, 0, 1, 103, 0, 0, 0, 0, 1, 104, 0, 0, 0, 0, 1, 105, 0, 0, 0, 0, 1, 106, 0, 0, 0, 0, 1, 107, 0, 0, 0, 0, 1, 108, 0, 0, 0, 0, 1, 109, 0, 0, 0, 0, 1, 110, 0, 0, 0, 0, 1, 111, 0, 0, 0, 0, 1, 112, 0, 0, 0, 0, 1, 113, 0, 0, 0, 0, 1, 114, 0, 0, 0, 0, 1, 115, 0, 0, 0, 0, 1, 116, 0, 0, 0, 0, 1, 117, 0, 0, 0, 0, 1, 118, 0, 0, 0, 0, 1, 119, 0, 0, 0, 0, 1, 120, 0, 0, 0, 0, 1, 121, 0, 0, 0, 0, 1, 122, 0, 0, 0, 0, 1, 123, 0, 0, 0, 0, 1, 124, 0, 0, 0, 0, 1, 125, 0, 0, 0, 0, 1, 126, 0, 0, 0, 0, 1, 127, 0, 0, 0, 0, 2, -48, -126, 0, 0, 0, 2, -48, -125, 0, 0, 0, 3, -30, -128, -102, 0, 0, 2, -47, -109, 0, 0, 0, 3, -30, -128, -98, 0, 0, 3, -30, -128, -90, 0, 0, 3, -30, -128, -96, 0, 0, 3, -30, -128, -95, 0, 0, 3, -30, -126, -84, 0, 0, 3, -30, -128, -80, 0, 0, 2, -48, -119, 0, 0, 0, 3, -30, -128, -71, 0, 0, 2, -48, -118, 0, 0, 0, 2, -48, -116, 0, 0, 0, 2, -48, -117, 0, 0, 0, 2, -48, -113, 0, 0, 0, 2, -47, -110, 0, 0, 0, 3, -30, -128, -104, 0, 0, 3, -30, -128, -103, 0, 0, 3, -30, -128, -100, 0, 0, 3, -30, -128, -99, 0, 0, 3, -30, -128, -94, 0, 0, 3, -30, -128, -109, 0, 0, 3, -30, -128, -108, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 3, -30, -124, -94, 0, 0, 2, -47, -103, 0, 0, 0, 3, -30, -128, -70, 0, 0, 2, -47, -102, 0, 0, 0, 2, -47, -100, 0, 0, 0, 2, -47, -101, 0, 0, 0, 2, -47, -97, 0, 0, 0, 2, -62, -96, 0, 0, 0, 2, -48, -114, 0, 0, 0, 2, -47, -98, 0, 0, 0, 2, -48, -120, 0, 0, 0, 2, -62, -92, 0, 0, 0, 2, -46, -112, 0, 0, 0, 2, -62, -90, 0, 0, 0, 2, -62, -89, 0, 0, 0, 2, -48, -127, 0, 0, 0, 2, -62, -87, 0, 0, 0, 2, -48, -124, 0, 0, 0, 2, -62, -85, 0, 0, 0, 2, -62, -84, 0, 0, 0, 2, -62, -83, 0, 0, 0, 2, -62, -82, 0, 0, 0, 2, -48, -121, 0, 0, 0, 2, -62, -80, 0, 0, 0, 2, -62, -79, 0, 0, 0, 2, -48, -122, 0, 0, 0, 2, -47, -106, 0, 0, 0, 2, -46, -111, 0, 0, 0, 2, -62, -75, 0, 0, 0, 2, -62, -74, 0, 0, 0, 2, -62, -73, 0, 0, 0, 2, -47, -111, 0, 0, 0, 3, -30, -124, -106, 0, 0, 2, -47, -108, 0, 0, 0, 2, -62, -69, 0, 0, 0, 2, -47, -104, 0, 0, 0, 2, -48, -123, 0, 0, 0, 2, -47, -107, 0, 0, 0, 2, -47, -105, 0, 0, 0, 2, -48, -112, 0, 0, 0, 2, -48, -111, 0, 0, 0, 2, -48, -110, 0, 0, 0, 2, -48, -109, 0, 0, 0, 2, -48, -108, 0, 0, 0, 2, -48, -107, 0, 0, 0, 2, -48, -106, 0, 0, 0, 2, -48, -105, 0, 0, 0, 2, -48, -104, 0, 0, 0, 2, -48, -103, 0, 0, 0, 2, -48, -102, 0, 0, 0, 2, -48, -101, 0, 0, 0, 2, -48, -100, 0, 0, 0, 2, -48, -99, 0, 0, 0, 2, -48, -98, 0, 0, 0, 2, -48, -97, 0, 0, 0, 2, -48, -96, 0, 0, 0, 2, -48, -95, 0, 0, 0, 2, -48, -94, 0, 0, 0, 2, -48, -93, 0, 0, 0, 2, -48, -92, 0, 0, 0, 2, -48, -91, 0, 0, 0, 2, -48, -90, 0, 0, 0, 2, -48, -89, 0, 0, 0, 2, -48, -88, 0, 0, 0, 2, -48, -87, 0, 0, 0, 2, -48, -86, 0, 0, 0, 2, -48, -85, 0, 0, 0, 2, -48, -84, 0, 0, 0, 2, -48, -83, 0, 0, 0, 2, -48, -82, 0, 0, 0, 2, -48, -81, 0, 0, 0, 2, -48, -80, 0, 0, 0, 2, -48, -79, 0, 0, 0, 2, -48, -78, 0, 0, 0, 2, -48, -77, 0, 0, 0, 2, -48, -76, 0, 0, 0, 2, -48, -75, 0, 0, 0, 2, -48, -74, 0, 0, 0, 2, -48, -73, 0, 0, 0, 2, -48, -72, 0, 0, 0, 2, -48, -71, 0, 0, 0, 2, -48, -70, 0, 0, 0, 2, -48, -69, 0, 0, 0, 2, -48, -68, 0, 0, 0, 2, -48, -67, 0, 0, 0, 2, -48, -66, 0, 0, 0, 2, -48, -65, 0, 0, 0, 2, -47, -128, 0, 0, 0, 2, -47, -127, 0, 0, 0, 2, -47, -126, 0, 0, 0, 2, -47, -125, 0, 0, 0, 2, -47, -124, 0, 0, 0, 2, -47, -123, 0, 0, 0, 2, -47, -122, 0, 0, 0, 2, -47, -121, 0, 0, 0, 2, -47, -120, 0, 0, 0, 2, -47, -119, 0, 0, 0, 2, -47, -118, 0, 0, 0, 2, -47, -117, 0, 0, 0, 2, -47, -116, 0, 0, 0, 2, -47, -115, 0, 0, 0, 2, -47, -114, 0, 0, 0, 2, -47, -113, 0, 0, 0 }; /// Latin alphabet used by English and some other Western languages static signed char windows_1252[] = { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 1, 7, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 1, 9, 0, 0, 0, 0, 1, 10, 0, 0, 0, 0, 1, 11, 0, 0, 0, 0, 1, 12, 0, 0, 0, 0, 1, 13, 0, 0, 0, 0, 1, 14, 0, 0, 0, 0, 1, 15, 0, 0, 0, 0, 1, 16, 0, 0, 0, 0, 1, 17, 0, 0, 0, 0, 1, 18, 0, 0, 0, 0, 1, 19, 0, 0, 0, 0, 1, 20, 0, 0, 0, 0, 1, 21, 0, 0, 0, 0, 1, 22, 0, 0, 0, 0, 1, 23, 0, 0, 0, 0, 1, 24, 0, 0, 0, 0, 1, 25, 0, 0, 0, 0, 1, 26, 0, 0, 0, 0, 1, 27, 0, 0, 0, 0, 1, 28, 0, 0, 0, 0, 1, 29, 0, 0, 0, 0, 1, 30, 0, 0, 0, 0, 1, 31, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 1, 33, 0, 0, 0, 0, 1, 34, 0, 0, 0, 0, 1, 35, 0, 0, 0, 0, 1, 36, 0, 0, 0, 0, 1, 37, 0, 0, 0, 0, 1, 38, 0, 0, 0, 0, 1, 39, 0, 0, 0, 0, 1, 40, 0, 0, 0, 0, 1, 41, 0, 0, 0, 0, 1, 42, 0, 0, 0, 0, 1, 43, 0, 0, 0, 0, 1, 44, 0, 0, 0, 0, 1, 45, 0, 0, 0, 0, 1, 46, 0, 0, 0, 0, 1, 47, 0, 0, 0, 0, 1, 48, 0, 0, 0, 0, 1, 49, 0, 0, 0, 0, 1, 50, 0, 0, 0, 0, 1, 51, 0, 0, 0, 0, 1, 52, 0, 0, 0, 0, 1, 53, 0, 0, 0, 0, 1, 54, 0, 0, 0, 0, 1, 55, 0, 0, 0, 0, 1, 56, 0, 0, 0, 0, 1, 57, 0, 0, 0, 0, 1, 58, 0, 0, 0, 0, 1, 59, 0, 0, 0, 0, 1, 60, 0, 0, 0, 0, 1, 61, 0, 0, 0, 0, 1, 62, 0, 0, 0, 0, 1, 63, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 1, 65, 0, 0, 0, 0, 1, 66, 0, 0, 0, 0, 1, 67, 0, 0, 0, 0, 1, 68, 0, 0, 0, 0, 1, 69, 0, 0, 0, 0, 1, 70, 0, 0, 0, 0, 1, 71, 0, 0, 0, 0, 1, 72, 0, 0, 0, 0, 1, 73, 0, 0, 0, 0, 1, 74, 0, 0, 0, 0, 1, 75, 0, 0, 0, 0, 1, 76, 0, 0, 0, 0, 1, 77, 0, 0, 0, 0, 1, 78, 0, 0, 0, 0, 1, 79, 0, 0, 0, 0, 1, 80, 0, 0, 0, 0, 1, 81, 0, 0, 0, 0, 1, 82, 0, 0, 0, 0, 1, 83, 0, 0, 0, 0, 1, 84, 0, 0, 0, 0, 1, 85, 0, 0, 0, 0, 1, 86, 0, 0, 0, 0, 1, 87, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 1, 89, 0, 0, 0, 0, 1, 90, 0, 0, 0, 0, 1, 91, 0, 0, 0, 0, 1, 92, 0, 0, 0, 0, 1, 93, 0, 0, 0, 0, 1, 94, 0, 0, 0, 0, 1, 95, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 1, 97, 0, 0, 0, 0, 1, 98, 0, 0, 0, 0, 1, 99, 0, 0, 0, 0, 1, 100, 0, 0, 0, 0, 1, 101, 0, 0, 0, 0, 1, 102, 0, 0, 0, 0, 1, 103, 0, 0, 0, 0, 1, 104, 0, 0, 0, 0, 1, 105, 0, 0, 0, 0, 1, 106, 0, 0, 0, 0, 1, 107, 0, 0, 0, 0, 1, 108, 0, 0, 0, 0, 1, 109, 0, 0, 0, 0, 1, 110, 0, 0, 0, 0, 1, 111, 0, 0, 0, 0, 1, 112, 0, 0, 0, 0, 1, 113, 0, 0, 0, 0, 1, 114, 0, 0, 0, 0, 1, 115, 0, 0, 0, 0, 1, 116, 0, 0, 0, 0, 1, 117, 0, 0, 0, 0, 1, 118, 0, 0, 0, 0, 1, 119, 0, 0, 0, 0, 1, 120, 0, 0, 0, 0, 1, 121, 0, 0, 0, 0, 1, 122, 0, 0, 0, 0, 1, 123, 0, 0, 0, 0, 1, 124, 0, 0, 0, 0, 1, 125, 0, 0, 0, 0, 1, 126, 0, 0, 0, 0, 1, 127, 0, 0, 0, 0, 3, -30, -126, -84, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 3, -30, -128, -102, 0, 0, 2, -58, -110, 0, 0, 0, 3, -30, -128, -98, 0, 0, 3, -30, -128, -90, 0, 0, 3, -30, -128, -96, 0, 0, 3, -30, -128, -95, 0, 0, 2, -53, -122, 0, 0, 0, 3, -30, -128, -80, 0, 0, 2, -59, -96, 0, 0, 0, 3, -30, -128, -71, 0, 0, 2, -59, -110, 0, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 2, -59, -67, 0, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 1, 32, 0, 0, 0, 0, // not part of this charset 3, -30, -128, -104, 0, 0, 3, -30, -128, -103, 0, 0, 3, -30, -128, -100, 0, 0, 3, -30, -128, -99, 0, 0, 3, -30, -128, -94, 0, 0, 3, -30, -128, -109, 0, 0, 3, -30, -128, -108, 0, 0, 2, -53, -100, 0, 0, 0, 3, -30, -124, -94, 0, 0, 2, -59, -95, 0, 0, 0, 3, -30, -128, -70, 0, 0, 2, -59, -109, 0, 0, 0, 1, 32, 0, 0, 0, 0, // not part of this charset 2, -59, -66, 0, 0, 0, 2, -59, -72, 0, 0, 0, 2, -62, -96, 0, 0, 0, 2, -62, -95, 0, 0, 0, 2, -62, -94, 0, 0, 0, 2, -62, -93, 0, 0, 0, 2, -62, -92, 0, 0, 0, 2, -62, -91, 0, 0, 0, 2, -62, -90, 0, 0, 0, 2, -62, -89, 0, 0, 0, 2, -62, -88, 0, 0, 0, 2, -62, -87, 0, 0, 0, 2, -62, -86, 0, 0, 0, 2, -62, -85, 0, 0, 0, 2, -62, -84, 0, 0, 0, 2, -62, -83, 0, 0, 0, 2, -62, -82, 0, 0, 0, 2, -62, -81, 0, 0, 0, 2, -62, -80, 0, 0, 0, 2, -62, -79, 0, 0, 0, 2, -62, -78, 0, 0, 0, 2, -62, -77, 0, 0, 0, 2, -62, -76, 0, 0, 0, 2, -62, -75, 0, 0, 0, 2, -62, -74, 0, 0, 0, 2, -62, -73, 0, 0, 0, 2, -62, -72, 0, 0, 0, 2, -62, -71, 0, 0, 0, 2, -62, -70, 0, 0, 0, 2, -62, -69, 0, 0, 0, 2, -62, -68, 0, 0, 0, 2, -62, -67, 0, 0, 0, 2, -62, -66, 0, 0, 0, 2, -62, -65, 0, 0, 0, 2, -61, -128, 0, 0, 0, 2, -61, -127, 0, 0, 0, 2, -61, -126, 0, 0, 0, 2, -61, -125, 0, 0, 0, 2, -61, -124, 0, 0, 0, 2, -61, -123, 0, 0, 0, 2, -61, -122, 0, 0, 0, 2, -61, -121, 0, 0, 0, 2, -61, -120, 0, 0, 0, 2, -61, -119, 0, 0, 0, 2, -61, -118, 0, 0, 0, 2, -61, -117, 0, 0, 0, 2, -61, -116, 0, 0, 0, 2, -61, -115, 0, 0, 0, 2, -61, -114, 0, 0, 0, 2, -61, -113, 0, 0, 0, 2, -61, -112, 0, 0, 0, 2, -61, -111, 0, 0, 0, 2, -61, -110, 0, 0, 0, 2, -61, -109, 0, 0, 0, 2, -61, -108, 0, 0, 0, 2, -61, -107, 0, 0, 0, 2, -61, -106, 0, 0, 0, 2, -61, -105, 0, 0, 0, 2, -61, -104, 0, 0, 0, 2, -61, -103, 0, 0, 0, 2, -61, -102, 0, 0, 0, 2, -61, -101, 0, 0, 0, 2, -61, -100, 0, 0, 0, 2, -61, -99, 0, 0, 0, 2, -61, -98, 0, 0, 0, 2, -61, -97, 0, 0, 0, 2, -61, -96, 0, 0, 0, 2, -61, -95, 0, 0, 0, 2, -61, -94, 0, 0, 0, 2, -61, -93, 0, 0, 0, 2, -61, -92, 0, 0, 0, 2, -61, -91, 0, 0, 0, 2, -61, -90, 0, 0, 0, 2, -61, -89, 0, 0, 0, 2, -61, -88, 0, 0, 0, 2, -61, -87, 0, 0, 0, 2, -61, -86, 0, 0, 0, 2, -61, -85, 0, 0, 0, 2, -61, -84, 0, 0, 0, 2, -61, -83, 0, 0, 0, 2, -61, -82, 0, 0, 0, 2, -61, -81, 0, 0, 0, 2, -61, -80, 0, 0, 0, 2, -61, -79, 0, 0, 0, 2, -61, -78, 0, 0, 0, 2, -61, -77, 0, 0, 0, 2, -61, -76, 0, 0, 0, 2, -61, -75, 0, 0, 0, 2, -61, -74, 0, 0, 0, 2, -61, -73, 0, 0, 0, 2, -61, -72, 0, 0, 0, 2, -61, -71, 0, 0, 0, 2, -61, -70, 0, 0, 0, 2, -61, -69, 0, 0, 0, 2, -61, -68, 0, 0, 0, 2, -61, -67, 0, 0, 0, 2, -61, -66, 0, 0, 0, 2, -61, -65, 0, 0, 0 }; static signed char cp437[] = { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 1, 7, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 1, 9, 0, 0, 0, 0, 1, 10, 0, 0, 0, 0, 1, 11, 0, 0, 0, 0, 1, 12, 0, 0, 0, 0, 1, 13, 0, 0, 0, 0, 1, 14, 0, 0, 0, 0, 1, 15, 0, 0, 0, 0, 1, 16, 0, 0, 0, 0, 1, 17, 0, 0, 0, 0, 1, 18, 0, 0, 0, 0, 1, 19, 0, 0, 0, 0, 1, 20, 0, 0, 0, 0, 1, 21, 0, 0, 0, 0, 1, 22, 0, 0, 0, 0, 1, 23, 0, 0, 0, 0, 1, 24, 0, 0, 0, 0, 1, 25, 0, 0, 0, 0, 1, 26, 0, 0, 0, 0, 1, 27, 0, 0, 0, 0, 1, 28, 0, 0, 0, 0, 1, 29, 0, 0, 0, 0, 1, 30, 0, 0, 0, 0, 1, 31, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 1, 33, 0, 0, 0, 0, 1, 34, 0, 0, 0, 0, 1, 35, 0, 0, 0, 0, 1, 36, 0, 0, 0, 0, 1, 37, 0, 0, 0, 0, 1, 38, 0, 0, 0, 0, 1, 39, 0, 0, 0, 0, 1, 40, 0, 0, 0, 0, 1, 41, 0, 0, 0, 0, 1, 42, 0, 0, 0, 0, 1, 43, 0, 0, 0, 0, 1, 44, 0, 0, 0, 0, 1, 45, 0, 0, 0, 0, 1, 46, 0, 0, 0, 0, 1, 47, 0, 0, 0, 0, 1, 48, 0, 0, 0, 0, 1, 49, 0, 0, 0, 0, 1, 50, 0, 0, 0, 0, 1, 51, 0, 0, 0, 0, 1, 52, 0, 0, 0, 0, 1, 53, 0, 0, 0, 0, 1, 54, 0, 0, 0, 0, 1, 55, 0, 0, 0, 0, 1, 56, 0, 0, 0, 0, 1, 57, 0, 0, 0, 0, 1, 58, 0, 0, 0, 0, 1, 59, 0, 0, 0, 0, 1, 60, 0, 0, 0, 0, 1, 61, 0, 0, 0, 0, 1, 62, 0, 0, 0, 0, 1, 63, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 1, 65, 0, 0, 0, 0, 1, 66, 0, 0, 0, 0, 1, 67, 0, 0, 0, 0, 1, 68, 0, 0, 0, 0, 1, 69, 0, 0, 0, 0, 1, 70, 0, 0, 0, 0, 1, 71, 0, 0, 0, 0, 1, 72, 0, 0, 0, 0, 1, 73, 0, 0, 0, 0, 1, 74, 0, 0, 0, 0, 1, 75, 0, 0, 0, 0, 1, 76, 0, 0, 0, 0, 1, 77, 0, 0, 0, 0, 1, 78, 0, 0, 0, 0, 1, 79, 0, 0, 0, 0, 1, 80, 0, 0, 0, 0, 1, 81, 0, 0, 0, 0, 1, 82, 0, 0, 0, 0, 1, 83, 0, 0, 0, 0, 1, 84, 0, 0, 0, 0, 1, 85, 0, 0, 0, 0, 1, 86, 0, 0, 0, 0, 1, 87, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 1, 89, 0, 0, 0, 0, 1, 90, 0, 0, 0, 0, 1, 91, 0, 0, 0, 0, 1, 92, 0, 0, 0, 0, 1, 93, 0, 0, 0, 0, 1, 94, 0, 0, 0, 0, 1, 95, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 1, 97, 0, 0, 0, 0, 1, 98, 0, 0, 0, 0, 1, 99, 0, 0, 0, 0, 1, 100, 0, 0, 0, 0, 1, 101, 0, 0, 0, 0, 1, 102, 0, 0, 0, 0, 1, 103, 0, 0, 0, 0, 1, 104, 0, 0, 0, 0, 1, 105, 0, 0, 0, 0, 1, 106, 0, 0, 0, 0, 1, 107, 0, 0, 0, 0, 1, 108, 0, 0, 0, 0, 1, 109, 0, 0, 0, 0, 1, 110, 0, 0, 0, 0, 1, 111, 0, 0, 0, 0, 1, 112, 0, 0, 0, 0, 1, 113, 0, 0, 0, 0, 1, 114, 0, 0, 0, 0, 1, 115, 0, 0, 0, 0, 1, 116, 0, 0, 0, 0, 1, 117, 0, 0, 0, 0, 1, 118, 0, 0, 0, 0, 1, 119, 0, 0, 0, 0, 1, 120, 0, 0, 0, 0, 1, 121, 0, 0, 0, 0, 1, 122, 0, 0, 0, 0, 1, 123, 0, 0, 0, 0, 1, 124, 0, 0, 0, 0, 1, 125, 0, 0, 0, 0, 1, 126, 0, 0, 0, 0, 1, 127, 0, 0, 0, 0, 2, -61, -121, 0, 0, 0, 2, -61, -68, 0, 0, 0, 2, -61, -87, 0, 0, 0, 2, -61, -94, 0, 0, 0, 2, -61, -92, 0, 0, 0, 2, -61, -96, 0, 0, 0, 2, -61, -91, 0, 0, 0, 2, -61, -89, 0, 0, 0, 2, -61, -86, 0, 0, 0, 2, -61, -85, 0, 0, 0, 2, -61, -88, 0, 0, 0, 2, -61, -81, 0, 0, 0, 2, -61, -82, 0, 0, 0, 2, -61, -84, 0, 0, 0, 2, -61, -124, 0, 0, 0, 2, -61, -123, 0, 0, 0, 2, -61, -119, 0, 0, 0, 2, -61, -90, 0, 0, 0, 2, -61, -122, 0, 0, 0, 2, -61, -76, 0, 0, 0, 2, -61, -74, 0, 0, 0, 2, -61, -78, 0, 0, 0, 2, -61, -69, 0, 0, 0, 2, -61, -71, 0, 0, 0, 2, -61, -65, 0, 0, 0, 2, -61, -106, 0, 0, 0, 2, -61, -100, 0, 0, 0, 2, -62, -94, 0, 0, 0, 2, -62, -93, 0, 0, 0, 2, -62, -91, 0, 0, 0, 3, -30, -126, -89, 0, 0, 2, -58, -110, 0, 0, 0, 2, -61, -95, 0, 0, 0, 2, -61, -83, 0, 0, 0, 2, -61, -77, 0, 0, 0, 2, -61, -70, 0, 0, 0, 2, -61, -79, 0, 0, 0, 2, -61, -111, 0, 0, 0, 2, -62, -86, 0, 0, 0, 2, -62, -70, 0, 0, 0, 2, -62, -65, 0, 0, 0, 3, -30, -116, -112, 0, 0, 2, -62, -84, 0, 0, 0, 2, -62, -67, 0, 0, 0, 2, -62, -68, 0, 0, 0, 2, -62, -95, 0, 0, 0, 2, -62, -85, 0, 0, 0, 2, -62, -69, 0, 0, 0, 3, -30, -106, -111, 0, 0, 3, -30, -106, -110, 0, 0, 3, -30, -106, -109, 0, 0, 3, -30, -108, -126, 0, 0, 3, -30, -108, -92, 0, 0, 3, -30, -107, -95, 0, 0, 3, -30, -107, -94, 0, 0, 3, -30, -107, -106, 0, 0, 3, -30, -107, -107, 0, 0, 3, -30, -107, -93, 0, 0, 3, -30, -107, -111, 0, 0, 3, -30, -107, -105, 0, 0, 3, -30, -107, -99, 0, 0, 3, -30, -107, -100, 0, 0, 3, -30, -107, -101, 0, 0, 3, -30, -108, -112, 0, 0, 3, -30, -108, -108, 0, 0, 3, -30, -108, -76, 0, 0, 3, -30, -108, -84, 0, 0, 3, -30, -108, -100, 0, 0, 3, -30, -108, -128, 0, 0, 3, -30, -108, -68, 0, 0, 3, -30, -107, -98, 0, 0, 3, -30, -107, -97, 0, 0, 3, -30, -107, -102, 0, 0, 3, -30, -107, -108, 0, 0, 3, -30, -107, -87, 0, 0, 3, -30, -107, -90, 0, 0, 3, -30, -107, -96, 0, 0, 3, -30, -107, -112, 0, 0, 3, -30, -107, -84, 0, 0, 3, -30, -107, -89, 0, 0, 3, -30, -107, -88, 0, 0, 3, -30, -107, -92, 0, 0, 3, -30, -107, -91, 0, 0, 3, -30, -107, -103, 0, 0, 3, -30, -107, -104, 0, 0, 3, -30, -107, -110, 0, 0, 3, -30, -107, -109, 0, 0, 3, -30, -107, -85, 0, 0, 3, -30, -107, -86, 0, 0, 3, -30, -108, -104, 0, 0, 3, -30, -108, -116, 0, 0, 3, -30, -106, -120, 0, 0, 3, -30, -106, -124, 0, 0, 3, -30, -106, -116, 0, 0, 3, -30, -106, -112, 0, 0, 3, -30, -106, -128, 0, 0, 2, -50, -79, 0, 0, 0, 2, -61, -97, 0, 0, 0, 2, -50, -109, 0, 0, 0, 2, -49, -128, 0, 0, 0, 2, -50, -93, 0, 0, 0, 2, -49, -125, 0, 0, 0, 2, -62, -75, 0, 0, 0, 2, -49, -124, 0, 0, 0, 2, -50, -90, 0, 0, 0, 2, -50, -104, 0, 0, 0, 2, -50, -87, 0, 0, 0, 2, -50, -76, 0, 0, 0, 3, -30, -120, -98, 0, 0, 2, -49, -122, 0, 0, 0, 2, -50, -75, 0, 0, 0, 3, -30, -120, -87, 0, 0, 3, -30, -119, -95, 0, 0, 2, -62, -79, 0, 0, 0, 3, -30, -119, -91, 0, 0, 3, -30, -119, -92, 0, 0, 3, -30, -116, -96, 0, 0, 3, -30, -116, -95, 0, 0, 2, -61, -73, 0, 0, 0, 3, -30, -119, -120, 0, 0, 2, -62, -80, 0, 0, 0, 3, -30, -120, -103, 0, 0, 2, -62, -73, 0, 0, 0, 3, -30, -120, -102, 0, 0, 3, -30, -127, -65, 0, 0, 2, -62, -78, 0, 0, 0, 3, -30, -106, -96, 0, 0, 2, -62, -96, 0, 0, 0 }; } #endif openmw-openmw-0.38.0/components/to_utf8/tests/000077500000000000000000000000001264522266000213365ustar00rootroot00000000000000openmw-openmw-0.38.0/components/to_utf8/tests/.gitignore000066400000000000000000000000071264522266000233230ustar00rootroot00000000000000*_test openmw-openmw-0.38.0/components/to_utf8/tests/output/000077500000000000000000000000001264522266000226765ustar00rootroot00000000000000openmw-openmw-0.38.0/components/to_utf8/tests/output/to_utf8_test.out000066400000000000000000000012701264522266000260560ustar00rootroot00000000000000original: Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам? converted: Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам? original: Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger. converted: Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger. openmw-openmw-0.38.0/components/to_utf8/tests/test.sh000077500000000000000000000004441264522266000226560ustar00rootroot00000000000000#!/bin/bash make || exit mkdir -p output PROGS=*_test for a in $PROGS; do if [ -f "output/$a.out" ]; then echo "Running $a:" ./$a | diff output/$a.out - else echo "Creating $a.out" ./$a > "output/$a.out" git add "output/$a.out" fi done openmw-openmw-0.38.0/components/to_utf8/tests/test_data/000077500000000000000000000000001264522266000233065ustar00rootroot00000000000000openmw-openmw-0.38.0/components/to_utf8/tests/test_data/french-utf8.txt000066400000000000000000000001531264522266000261770ustar00rootroot00000000000000Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger.openmw-openmw-0.38.0/components/to_utf8/tests/test_data/french-win1252.txt000066400000000000000000000001501264522266000264150ustar00rootroot00000000000000Vous lui donnez le gteau sans protester avant daller chercher tous vos amis et de revenir vous venger.openmw-openmw-0.38.0/components/to_utf8/tests/test_data/russian-utf8.txt000066400000000000000000000003311264522266000264140ustar00rootroot00000000000000Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам?openmw-openmw-0.38.0/components/to_utf8/tests/test_data/russian-win1251.txt000066400000000000000000000001701264522266000266350ustar00rootroot00000000000000 , , ?openmw-openmw-0.38.0/components/to_utf8/tests/to_utf8_test.cpp000066400000000000000000000033151264522266000244730ustar00rootroot00000000000000#include #include #include #include #include "../to_utf8.hpp" std::string getFirstLine(const std::string &filename); void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile, const std::string &utf8File); /// Test character encoding conversion to and from UTF-8 void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile, const std::string &utf8File) { // get some test data std::string legacyEncLine = getFirstLine(legacyEncFile); std::string utf8Line = getFirstLine(utf8File); // create an encoder for specified character encoding ToUTF8::Utf8Encoder encoder (encoding); // convert text to UTF-8 std::string convertedUtf8Line = encoder.getUtf8(legacyEncLine); std::cout << "original: " << utf8Line << std::endl; std::cout << "converted: " << convertedUtf8Line << std::endl; // check correctness assert(convertedUtf8Line == utf8Line); // convert UTF-8 text to legacy encoding std::string convertedLegacyEncLine = encoder.getLegacyEnc(utf8Line); // check correctness assert(convertedLegacyEncLine == legacyEncLine); } std::string getFirstLine(const std::string &filename) { std::string line; std::ifstream text (filename.c_str()); if (!text.is_open()) { throw std::runtime_error("Unable to open file " + filename); } std::getline(text, line); text.close(); return line; } int main() { testEncoder(ToUTF8::WINDOWS_1251, "test_data/russian-win1251.txt", "test_data/russian-utf8.txt"); testEncoder(ToUTF8::WINDOWS_1252, "test_data/french-win1252.txt", "test_data/french-utf8.txt"); return 0; } openmw-openmw-0.38.0/components/to_utf8/to_utf8.cpp000066400000000000000000000244031264522266000222730ustar00rootroot00000000000000#include "to_utf8.hpp" #include #include #include #include #include /* This file contains the code to translate from WINDOWS-1252 (native charset used in English version of Morrowind) to UTF-8. The library is designed to be extened to support more source encodings later, which means that we may add support for Russian, Polish and Chinese files and so on. The code does not depend on any external library at runtime. Instead, it uses a pregenerated table made with iconv (see gen_iconv.cpp and the Makefile) which is located in tables_gen.hpp. This is both faster and uses less dependencies. The tables would only need to be regenerated if we are adding support more input encodings. As such, there is no need to make the generator code platform independent. The library is optimized for the case of pure ASCII input strings, which is the vast majority of cases at least for the English version. A test of my version of Morrowind.esm got 130 non-ASCII vs 236195 ASCII strings, or less than 0.06% of strings containing non-ASCII characters. To optmize for this, ff the first pass of the string does not find any non-ASCII characters, the entire string is passed along without any modification. Most of the non-ASCII strings are books, and are quite large. (The non-ASCII characters are typically starting and ending quotation marks.) Within these, almost all the characters are ASCII. For this purpose, the library is also optimized for mostly-ASCII contents even in the cases where some conversion is necessary. */ // Generated tables #include "tables_gen.hpp" using namespace ToUTF8; Utf8Encoder::Utf8Encoder(const FromType sourceEncoding): mOutput(50*1024) { switch (sourceEncoding) { case ToUTF8::WINDOWS_1252: { translationArray = ToUTF8::windows_1252; break; } case ToUTF8::WINDOWS_1250: { translationArray = ToUTF8::windows_1250; break; } case ToUTF8::WINDOWS_1251: { translationArray = ToUTF8::windows_1251; break; } case ToUTF8::CP437: { translationArray = ToUTF8::cp437; break; } default: { assert(0); } } } std::string Utf8Encoder::getUtf8(const char* input, size_t size) { // Double check that the input string stops at some point (it might // contain zero terminators before this, inside its own data, which // is also ok.) assert(input[size] == 0); // Note: The rest of this function is designed for single-character // input encodings only. It also assumes that the input encoding // shares its first 128 values (0-127) with ASCII. There are no plans // to add more encodings to this module (we are using utf8 for new // content files), so that shouldn't be an issue. // Compute output length, and check for pure ascii input at the same // time. bool ascii; size_t outlen = getLength(input, ascii); // If we're pure ascii, then don't bother converting anything. if(ascii) return std::string(input, outlen); // Make sure the output is large enough resize(outlen); char *out = &mOutput[0]; // Translate while (*input) copyFromArray(*(input++), out); // Make sure that we wrote the correct number of bytes assert((out-&mOutput[0]) == (int)outlen); // And make extra sure the output is null terminated assert(mOutput.size() > outlen); assert(mOutput[outlen] == 0); // Return a string return std::string(&mOutput[0], outlen); } std::string Utf8Encoder::getLegacyEnc(const char *input, size_t size) { // Double check that the input string stops at some point (it might // contain zero terminators before this, inside its own data, which // is also ok.) assert(input[size] == 0); // TODO: The rest of this function is designed for single-character // input encodings only. It also assumes that the input the input // encoding shares its first 128 values (0-127) with ASCII. These // conditions must be checked again if you add more input encodings // later. // Compute output length, and check for pure ascii input at the same // time. bool ascii; size_t outlen = getLength2(input, ascii); // If we're pure ascii, then don't bother converting anything. if(ascii) return std::string(input, outlen); // Make sure the output is large enough resize(outlen); char *out = &mOutput[0]; // Translate while(*input) copyFromArray2(input, out); // Make sure that we wrote the correct number of bytes assert((out-&mOutput[0]) == (int)outlen); // And make extra sure the output is null terminated assert(mOutput.size() > outlen); assert(mOutput[outlen] == 0); // Return a string return std::string(&mOutput[0], outlen); } // Make sure the output vector is large enough for 'size' bytes, // including a terminating zero after it. void Utf8Encoder::resize(size_t size) { if (mOutput.size() <= size) // Add some extra padding to reduce the chance of having to resize // again later. mOutput.resize(3*size); // And make sure the string is zero terminated mOutput[size] = 0; } /** Get the total length length needed to decode the given string with the given translation array. The arrays are encoded with 6 bytes per character, with the first giving the length and the next 5 the actual data. The function serves a dual purpose for optimization reasons: it checks if the input is pure ascii (all values are <= 127). If this is the case, then the ascii parameter is set to true, and the caller can optimize for this case. */ size_t Utf8Encoder::getLength(const char* input, bool &ascii) { ascii = true; size_t len = 0; const char* ptr = input; unsigned char inp = *ptr; // Do away with the ascii part of the string first (this is almost // always the entire string.) while (inp && inp < 128) inp = *(++ptr); len += (ptr-input); // If we're not at the null terminator at this point, then there // were some non-ascii characters to deal with. Go to slow-mode for // the rest of the string. if (inp) { ascii = false; while (inp) { // Find the translated length of this character in the // lookup table. len += translationArray[inp*6]; inp = *(++ptr); } } return len; } // Translate one character 'ch' using the translation array 'arr', and // advance the output pointer accordingly. void Utf8Encoder::copyFromArray(unsigned char ch, char* &out) { // Optimize for ASCII values if (ch < 128) { *(out++) = ch; return; } const signed char *in = translationArray + ch*6; int len = *(in++); for (int i=0; i #include #include namespace ToUTF8 { // These are all the currently supported code pages enum FromType { WINDOWS_1250, // Central ane Eastern European languages WINDOWS_1251, // Cyrillic languages WINDOWS_1252, // Used by English version of Morrowind (and // probably others) CP437 // Used for fonts (*.fnt) if data files encoding is 1252. Otherwise, uses the same encoding as the data files. }; FromType calculateEncoding(const std::string& encodingName); std::string encodingUsingMessage(const std::string& encodingName); // class class Utf8Encoder { public: Utf8Encoder(FromType sourceEncoding); // Convert to UTF8 from the previously given code page. std::string getUtf8(const char *input, size_t size); inline std::string getUtf8(const std::string &str) { return getUtf8(str.c_str(), str.size()); } std::string getLegacyEnc(const char *input, size_t size); inline std::string getLegacyEnc(const std::string &str) { return getLegacyEnc(str.c_str(), str.size()); } private: void resize(size_t size); size_t getLength(const char* input, bool &ascii); void copyFromArray(unsigned char chp, char* &out); size_t getLength2(const char* input, bool &ascii); void copyFromArray2(const char*& chp, char* &out); std::vector mOutput; signed char* translationArray; }; } #endif openmw-openmw-0.38.0/components/translation/000077500000000000000000000000001264522266000211425ustar00rootroot00000000000000openmw-openmw-0.38.0/components/translation/translation.cpp000066400000000000000000000074131264522266000242110ustar00rootroot00000000000000#include "translation.hpp" #include #include namespace Translation { Storage::Storage() : mEncoder(NULL) { } void Storage::loadTranslationData(const Files::Collections& dataFileCollections, const std::string& esmFileName) { std::string esmNameNoExtension(Misc::StringUtils::lowerCase(esmFileName)); //changing the extension size_t dotPos = esmNameNoExtension.rfind('.'); if (dotPos != std::string::npos) esmNameNoExtension.resize(dotPos); loadData(mCellNamesTranslations, esmNameNoExtension, ".cel", dataFileCollections); loadData(mPhraseForms, esmNameNoExtension, ".top", dataFileCollections); loadData(mTopicIDs, esmNameNoExtension, ".mrk", dataFileCollections); } void Storage::loadData(ContainerType& container, const std::string& fileNameNoExtension, const std::string& extension, const Files::Collections& dataFileCollections) { std::string fileName = fileNameNoExtension + extension; if (dataFileCollections.getCollection (extension).doesExist (fileName)) { boost::filesystem::ifstream stream ( dataFileCollections.getCollection (extension).getPath (fileName)); if (!stream.is_open()) throw std::runtime_error ("failed to open translation file: " + fileName); loadDataFromStream(container, stream); } } void Storage::loadDataFromStream(ContainerType& container, std::istream& stream) { std::string line; while (!stream.eof() && !stream.fail()) { std::getline( stream, line ); if (!line.empty() && *line.rbegin() == '\r') line.resize(line.size() - 1); if (!line.empty()) { line = mEncoder->getUtf8(line); size_t tab_pos = line.find('\t'); if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1) { std::string key = line.substr(0, tab_pos); std::string value = line.substr(tab_pos + 1); if (!key.empty() && !value.empty()) container.insert(std::make_pair(key, value)); } } } } std::string Storage::translateCellName(const std::string& cellName) const { std::map::const_iterator entry = mCellNamesTranslations.find(cellName); if (entry == mCellNamesTranslations.end()) return cellName; return entry->second; } std::string Storage::topicID(const std::string& phrase) const { std::string result = topicStandardForm(phrase); //seeking for the topic ID std::map::const_iterator topicIDIterator = mTopicIDs.find(result); if (topicIDIterator != mTopicIDs.end()) result = topicIDIterator->second; return result; } std::string Storage::topicStandardForm(const std::string& phrase) const { std::map::const_iterator phraseFormsIterator = mPhraseForms.find(phrase); if (phraseFormsIterator != mPhraseForms.end()) return phraseFormsIterator->second; else return phrase; } void Storage::setEncoder(ToUTF8::Utf8Encoder* encoder) { mEncoder = encoder; } bool Storage::hasTranslation() const { return !mCellNamesTranslations.empty() || !mTopicIDs.empty() || !mPhraseForms.empty(); } } openmw-openmw-0.38.0/components/translation/translation.hpp000066400000000000000000000023711264522266000242140ustar00rootroot00000000000000#ifndef COMPONENTS_TRANSLATION_DATA_H #define COMPONENTS_TRANSLATION_DATA_H #include #include namespace Translation { class Storage { public: Storage(); void loadTranslationData(const Files::Collections& dataFileCollections, const std::string& esmFileName); std::string translateCellName(const std::string& cellName) const; std::string topicID(const std::string& phrase) const; // Standard form usually means nominative case std::string topicStandardForm(const std::string& phrase) const; void setEncoder(ToUTF8::Utf8Encoder* encoder); bool hasTranslation() const; private: typedef std::map ContainerType; void loadData(ContainerType& container, const std::string& fileNameNoExtension, const std::string& extension, const Files::Collections& dataFileCollections); void loadDataFromStream(ContainerType& container, std::istream& stream); ToUTF8::Utf8Encoder* mEncoder; ContainerType mCellNamesTranslations, mTopicIDs, mPhraseForms; }; } #endif openmw-openmw-0.38.0/components/version/000077500000000000000000000000001264522266000202715ustar00rootroot00000000000000openmw-openmw-0.38.0/components/version/version.cpp000066400000000000000000000015701264522266000224650ustar00rootroot00000000000000#include "version.hpp" #include #include namespace Version { Version getOpenmwVersion(const std::string &resourcePath) { boost::filesystem::path path (resourcePath + "/version"); boost::filesystem::ifstream stream (path); Version v; std::getline(stream, v.mVersion); std::getline(stream, v.mCommitHash); std::getline(stream, v.mTagHash); return v; } std::string Version::describe() { std::string str = "OpenMW version " + mVersion; std::string rev = mCommitHash; std::string tag = mTagHash; if (!rev.empty() && !tag.empty()) { rev = rev.substr(0, 10); str += "\nRevision: " + rev; } return str; } std::string getOpenmwVersionDescription(const std::string &resourcePath) { Version v = getOpenmwVersion(resourcePath); return v.describe(); } } openmw-openmw-0.38.0/components/version/version.hpp000066400000000000000000000010431264522266000224650ustar00rootroot00000000000000#ifndef VERSION_HPP #define VERSION_HPP #include namespace Version { struct Version { std::string mVersion; std::string mCommitHash; std::string mTagHash; std::string describe(); }; /// Read OpenMW version from the version file located in resourcePath. Version getOpenmwVersion(const std::string& resourcePath); /// Helper function to getOpenmwVersion and describe() it std::string getOpenmwVersionDescription(const std::string& resourcePath); } #endif // VERSION_HPP openmw-openmw-0.38.0/components/vfs/000077500000000000000000000000001264522266000174025ustar00rootroot00000000000000openmw-openmw-0.38.0/components/vfs/archive.hpp000066400000000000000000000011441264522266000215340ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_RESOURCE_ARCHIVE_H #define OPENMW_COMPONENTS_RESOURCE_ARCHIVE_H #include #include namespace VFS { class File { public: virtual ~File() {} virtual Files::IStreamPtr open() = 0; }; class Archive { public: virtual ~Archive() {} /// List all resources contained in this archive, and run the resource names through the given normalize function. virtual void listResources(std::map& out, char (*normalize_function) (char)) = 0; }; } #endif openmw-openmw-0.38.0/components/vfs/bsaarchive.cpp000066400000000000000000000017771264522266000222310ustar00rootroot00000000000000#include "bsaarchive.hpp" namespace VFS { BsaArchive::BsaArchive(const std::string &filename) { mFile.open(filename); const Bsa::BSAFile::FileList &filelist = mFile.getList(); for(Bsa::BSAFile::FileList::const_iterator it = filelist.begin();it != filelist.end();++it) { mResources.push_back(BsaArchiveFile(&*it, &mFile)); } } void BsaArchive::listResources(std::map &out, char (*normalize_function)(char)) { for (std::vector::iterator it = mResources.begin(); it != mResources.end(); ++it) { std::string ent = it->mInfo->name; std::transform(ent.begin(), ent.end(), ent.begin(), normalize_function); out[ent] = &*it; } } // ------------------------------------------------------------------------------ BsaArchiveFile::BsaArchiveFile(const Bsa::BSAFile::FileStruct *info, Bsa::BSAFile* bsa) : mInfo(info) , mFile(bsa) { } Files::IStreamPtr BsaArchiveFile::open() { return mFile->getFile(mInfo); } } openmw-openmw-0.38.0/components/vfs/bsaarchive.hpp000066400000000000000000000013241264522266000222220ustar00rootroot00000000000000#ifndef VFS_BSAARCHIVE_HPP_ #define VFS_BSAARCHIVE_HPP_ #include "archive.hpp" #include namespace VFS { class BsaArchiveFile : public File { public: BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, Bsa::BSAFile* bsa); virtual Files::IStreamPtr open(); const Bsa::BSAFile::FileStruct* mInfo; Bsa::BSAFile* mFile; }; class BsaArchive : public Archive { public: BsaArchive(const std::string& filename); virtual void listResources(std::map& out, char (*normalize_function) (char)); private: Bsa::BSAFile mFile; std::vector mResources; }; } #endifopenmw-openmw-0.38.0/components/vfs/filesystemarchive.cpp000066400000000000000000000032471264522266000236420ustar00rootroot00000000000000#include "filesystemarchive.hpp" #include namespace VFS { FileSystemArchive::FileSystemArchive(const std::string &path) : mBuiltIndex(false) , mPath(path) { } void FileSystemArchive::listResources(std::map &out, char (*normalize_function)(char)) { if (!mBuiltIndex) { typedef boost::filesystem::recursive_directory_iterator directory_iterator; directory_iterator end; size_t prefix = mPath.size (); if (mPath.size () > 0 && mPath [prefix - 1] != '\\' && mPath [prefix - 1] != '/') ++prefix; for (directory_iterator i (mPath); i != end; ++i) { if(boost::filesystem::is_directory (*i)) continue; std::string proper = i->path ().string (); FileSystemArchiveFile file(proper); std::string searchable; std::transform(proper.begin() + prefix, proper.end(), std::back_inserter(searchable), normalize_function); mIndex.insert (std::make_pair (searchable, file)); } mBuiltIndex = true; } for (index::iterator it = mIndex.begin(); it != mIndex.end(); ++it) { out[it->first] = &it->second; } } // ---------------------------------------------------------------------------------- FileSystemArchiveFile::FileSystemArchiveFile(const std::string &path) : mPath(path) { } Files::IStreamPtr FileSystemArchiveFile::open() { return Files::openConstrainedFileStream(mPath.c_str()); } } openmw-openmw-0.38.0/components/vfs/filesystemarchive.hpp000066400000000000000000000013761264522266000236500ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_RESOURCE_FILESYSTEMARCHIVE_H #define OPENMW_COMPONENTS_RESOURCE_FILESYSTEMARCHIVE_H #include "archive.hpp" namespace VFS { class FileSystemArchiveFile : public File { public: FileSystemArchiveFile(const std::string& path); virtual Files::IStreamPtr open(); private: std::string mPath; }; class FileSystemArchive : public Archive { public: FileSystemArchive(const std::string& path); virtual void listResources(std::map& out, char (*normalize_function) (char)); private: typedef std::map index; index mIndex; bool mBuiltIndex; std::string mPath; }; } #endif openmw-openmw-0.38.0/components/vfs/manager.cpp000066400000000000000000000043001264522266000215150ustar00rootroot00000000000000#include "manager.hpp" #include #include #include #include "archive.hpp" namespace { char strict_normalize_char(char ch) { return ch == '\\' ? '/' : ch; } char nonstrict_normalize_char(char ch) { return ch == '\\' ? '/' : Misc::StringUtils::toLower(ch); } void normalize_path(std::string& path, bool strict) { char (*normalize_char)(char) = strict ? &strict_normalize_char : &nonstrict_normalize_char; std::transform(path.begin(), path.end(), path.begin(), normalize_char); } } namespace VFS { Manager::Manager(bool strict) : mStrict(strict) { } Manager::~Manager() { for (std::vector::iterator it = mArchives.begin(); it != mArchives.end(); ++it) delete *it; mArchives.clear(); } void Manager::addArchive(Archive *archive) { mArchives.push_back(archive); } void Manager::buildIndex() { mIndex.clear(); for (std::vector::const_iterator it = mArchives.begin(); it != mArchives.end(); ++it) (*it)->listResources(mIndex, mStrict ? &strict_normalize_char : &nonstrict_normalize_char); } Files::IStreamPtr Manager::get(const std::string &name) const { std::string normalized = name; normalize_path(normalized, mStrict); return getNormalized(normalized); } Files::IStreamPtr Manager::getNormalized(const std::string &normalizedName) const { std::map::const_iterator found = mIndex.find(normalizedName); if (found == mIndex.end()) throw std::runtime_error("Resource '" + normalizedName + "' not found"); return found->second->open(); } bool Manager::exists(const std::string &name) const { std::string normalized = name; normalize_path(normalized, mStrict); return mIndex.find(normalized) != mIndex.end(); } const std::map& Manager::getIndex() const { return mIndex; } void Manager::normalizeFilename(std::string &name) const { normalize_path(name, mStrict); } } openmw-openmw-0.38.0/components/vfs/manager.hpp000066400000000000000000000040671264522266000215340ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_RESOURCEMANAGER_H #define OPENMW_COMPONENTS_RESOURCEMANAGER_H #include #include #include namespace VFS { class Archive; class File; /// @brief The main class responsible for loading files from a virtual file system. /// @par Various archive types (e.g. directories on the filesystem, or compressed archives) /// can be registered, and will be merged into a single file tree. If the same filename is /// contained in multiple archives, the last added archive will have priority. class Manager { public: /// @param strict Use strict path handling? If enabled, no case folding will /// be done, but slash/backslash conversions are always done. Manager(bool strict); ~Manager(); /// Register the given archive. All files contained in it will be added to the index on the next buildIndex() call. /// @note Takes ownership of the given pointer. void addArchive(Archive* archive); /// Build the file index. Should be called when all archives have been registered. void buildIndex(); /// Does a file with this name exist? bool exists(const std::string& name) const; /// Get a complete list of files from all archives const std::map& getIndex() const; /// Normalize the given filename, making slashes/backslashes consistent, and lower-casing if mStrict is false. void normalizeFilename(std::string& name) const; /// Retrieve a file by name. /// @note Throws an exception if the file can not be found. Files::IStreamPtr get(const std::string& name) const; /// Retrieve a file by name (name is already normalized). /// @note Throws an exception if the file can not be found. Files::IStreamPtr getNormalized(const std::string& normalizedName) const; private: bool mStrict; std::vector mArchives; std::map mIndex; }; } #endif openmw-openmw-0.38.0/components/vfs/registerarchives.cpp000066400000000000000000000030511264522266000234560ustar00rootroot00000000000000#include "registerarchives.hpp" #include #include #include #include #include namespace VFS { void registerArchives(VFS::Manager *vfs, const Files::Collections &collections, const std::vector &archives, bool useLooseFiles) { const Files::PathContainer& dataDirs = collections.getPaths(); for (std::vector::const_iterator archive = archives.begin(); archive != archives.end(); ++archive) { if (collections.doesExist(*archive)) { // Last BSA has the highest priority const std::string archivePath = collections.getPath(*archive).string(); std::cout << "Adding BSA archive " << archivePath << std::endl; vfs->addArchive(new BsaArchive(archivePath)); } else { std::stringstream message; message << "Archive '" << *archive << "' not found"; throw std::runtime_error(message.str()); } } if (useLooseFiles) for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { std::cout << "Adding data directory " << iter->string() << std::endl; // Last data dir has the highest priority vfs->addArchive(new FileSystemArchive(iter->string())); } vfs->buildIndex(); } } openmw-openmw-0.38.0/components/vfs/registerarchives.hpp000066400000000000000000000006721264522266000234710ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_VFS_REGISTER_ARCHIVES_H #define OPENMW_COMPONENTS_VFS_REGISTER_ARCHIVES_H #include namespace VFS { class Manager; /// @brief Register BSA and file system archives based on the given OpenMW configuration. void registerArchives (VFS::Manager* vfs, const Files::Collections& collections, const std::vector& archives, bool useLooseFiles); } #endif openmw-openmw-0.38.0/components/widgets/000077500000000000000000000000001264522266000202525ustar00rootroot00000000000000openmw-openmw-0.38.0/components/widgets/box.cpp000066400000000000000000000307131264522266000215520ustar00rootroot00000000000000#include "box.hpp" namespace Gui { void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) { MyGUI::Widget * parent = w->getParent(); if (parent != 0) { if (mExpandDirection.isLeft()) { int hdiff = getRequestedSize ().width - w->getSize().width; w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0)); } w->setSize(getRequestedSize ()); while (parent != 0) { Box * b = dynamic_cast(parent); if (b) b->notifyChildrenSizeChanged(); else break; parent = parent->getParent(); } } } MyGUI::IntSize AutoSizedTextBox::getRequestedSize() { return getTextSize(); } void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) { TextBox::setCaption(_value); notifySizeChange (this); } void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) { if (_key == "ExpandDirection") { mExpandDirection = MyGUI::Align::parse (_value); } else { TextBox::setPropertyOverride (_key, _value); } } MyGUI::IntSize AutoSizedEditBox::getRequestedSize() { if (getAlign().isHStretch()) throw std::runtime_error("AutoSizedEditBox can't have HStretch align (" + getName() + ")"); return MyGUI::IntSize(getSize().width, getTextSize().height); } void AutoSizedEditBox::setCaption(const MyGUI::UString& _value) { EditBox::setCaption(_value); notifySizeChange (this); } void AutoSizedEditBox::setPropertyOverride(const std::string& _key, const std::string& _value) { if (_key == "ExpandDirection") { mExpandDirection = MyGUI::Align::parse (_value); } else { EditBox::setPropertyOverride (_key, _value); } } MyGUI::IntSize AutoSizedButton::getRequestedSize() { MyGUI::IntSize padding(24, 8); if (isUserString("TextPadding")) padding = MyGUI::IntSize::parse(getUserString("TextPadding")); MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(padding.width,padding.height); return size; } void AutoSizedButton::setCaption(const MyGUI::UString& _value) { Button::setCaption(_value); notifySizeChange (this); } void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value) { if (_key == "ExpandDirection") { mExpandDirection = MyGUI::Align::parse (_value); } else { Button::setPropertyOverride (_key, _value); } } Box::Box() : mSpacing(4) , mPadding(0) , mAutoResize(false) { } void Box::notifyChildrenSizeChanged () { align(); } bool Box::_setPropertyImpl(const std::string& _key, const std::string& _value) { if (_key == "Spacing") mSpacing = MyGUI::utility::parseValue(_value); else if (_key == "Padding") mPadding = MyGUI::utility::parseValue(_value); else if (_key == "AutoResize") mAutoResize = MyGUI::utility::parseValue(_value); else return false; return true; } void HBox::align () { unsigned int count = getChildCount (); size_t h_stretched_count = 0; int total_width = 0; int total_height = 0; std::vector< std::pair > sizes; sizes.resize(count); for (unsigned int i = 0; i < count; ++i) { MyGUI::Widget* w = getChildAt(i); bool hstretch = w->getUserString ("HStretch") == "true"; bool hidden = w->getUserString("Hidden") == "true"; if (hidden) continue; h_stretched_count += hstretch; AutoSizedWidget* aw = dynamic_cast(w); if (aw) { sizes[i] = std::make_pair(aw->getRequestedSize (), hstretch); total_width += aw->getRequestedSize ().width; total_height = std::max(total_height, aw->getRequestedSize ().height); } else { sizes[i] = std::make_pair(w->getSize(), hstretch); total_width += w->getSize().width; if (!(w->getUserString("VStretch") == "true")) total_height = std::max(total_height, w->getSize().height); } if (i != count-1) total_width += mSpacing; } if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) { setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); return; } int curX = 0; for (unsigned int i = 0; i < count; ++i) { if (i == 0) curX += mPadding; MyGUI::Widget* w = getChildAt(i); bool hidden = w->getUserString("Hidden") == "true"; if (hidden) continue; bool vstretch = w->getUserString ("VStretch") == "true"; int max_height = getSize().height - mPadding*2; int height = vstretch ? max_height : sizes[i].first.height; MyGUI::IntCoord widgetCoord; widgetCoord.left = curX; widgetCoord.top = mPadding + (getSize().height-mPadding*2 - height) / 2; int width = 0; if (sizes[i].second) { if (h_stretched_count == 0) throw std::logic_error("unexpected"); width = sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count; } else width = sizes[i].first.width; widgetCoord.width = width; widgetCoord.height = height; w->setCoord(widgetCoord); curX += width; if (i != count-1) curX += mSpacing; } } void HBox::setPropertyOverride(const std::string& _key, const std::string& _value) { if (!Box::_setPropertyImpl (_key, _value)) MyGUI::Widget::setPropertyOverride(_key, _value); } void HBox::setSize (const MyGUI::IntSize& _value) { MyGUI::Widget::setSize (_value); align(); } void HBox::setCoord (const MyGUI::IntCoord& _value) { MyGUI::Widget::setCoord (_value); align(); } void HBox::onWidgetCreated(MyGUI::Widget* _widget) { align(); } MyGUI::IntSize HBox::getRequestedSize () { MyGUI::IntSize size(0,0); for (unsigned int i = 0; i < getChildCount (); ++i) { bool hidden = getChildAt(i)->getUserString("Hidden") == "true"; if (hidden) continue; AutoSizedWidget* w = dynamic_cast(getChildAt(i)); if (w) { MyGUI::IntSize requested = w->getRequestedSize (); size.height = std::max(size.height, requested.height); size.width = size.width + requested.width; if (i != getChildCount()-1) size.width += mSpacing; } else { MyGUI::IntSize requested = getChildAt(i)->getSize (); size.height = std::max(size.height, requested.height); if (getChildAt(i)->getUserString("HStretch") != "true") size.width = size.width + requested.width; if (i != getChildCount()-1) size.width += mSpacing; } size.height += mPadding*2; size.width += mPadding*2; } return size; } void VBox::align () { unsigned int count = getChildCount (); size_t v_stretched_count = 0; int total_height = 0; int total_width = 0; std::vector< std::pair > sizes; sizes.resize(count); for (unsigned int i = 0; i < count; ++i) { MyGUI::Widget* w = getChildAt(i); bool hidden = w->getUserString("Hidden") == "true"; if (hidden) continue; bool vstretch = w->getUserString ("VStretch") == "true"; v_stretched_count += vstretch; AutoSizedWidget* aw = dynamic_cast(w); if (aw) { sizes[i] = std::make_pair(aw->getRequestedSize (), vstretch); total_height += aw->getRequestedSize ().height; total_width = std::max(total_width, aw->getRequestedSize ().width); } else { sizes[i] = std::make_pair(w->getSize(), vstretch); total_height += w->getSize().height; if (!(w->getUserString("HStretch") == "true")) total_width = std::max(total_width, w->getSize().width); } if (i != count-1) total_height += mSpacing; } if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) { setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); return; } int curY = 0; for (unsigned int i = 0; i < count; ++i) { if (i==0) curY += mPadding; MyGUI::Widget* w = getChildAt(i); bool hidden = w->getUserString("Hidden") == "true"; if (hidden) continue; bool hstretch = w->getUserString ("HStretch") == "true"; int maxWidth = getSize().width - mPadding*2; int width = hstretch ? maxWidth : sizes[i].first.width; MyGUI::IntCoord widgetCoord; widgetCoord.top = curY; widgetCoord.left = mPadding + (getSize().width-mPadding*2 - width) / 2; int height = 0; if (sizes[i].second) { if (v_stretched_count == 0) throw std::logic_error("unexpected"); height = sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count; } else height = sizes[i].first.height; widgetCoord.height = height; widgetCoord.width = width; w->setCoord(widgetCoord); curY += height; if (i != count-1) curY += mSpacing; } } void VBox::setPropertyOverride(const std::string& _key, const std::string& _value) { if (!Box::_setPropertyImpl (_key, _value)) MyGUI::Widget::setPropertyOverride(_key, _value); } void VBox::setSize (const MyGUI::IntSize& _value) { MyGUI::Widget::setSize (_value); align(); } void VBox::setCoord (const MyGUI::IntCoord& _value) { MyGUI::Widget::setCoord (_value); align(); } MyGUI::IntSize VBox::getRequestedSize () { MyGUI::IntSize size(0,0); for (unsigned int i = 0; i < getChildCount (); ++i) { bool hidden = getChildAt(i)->getUserString("Hidden") == "true"; if (hidden) continue; AutoSizedWidget* w = dynamic_cast(getChildAt(i)); if (w) { MyGUI::IntSize requested = w->getRequestedSize (); size.width = std::max(size.width, requested.width); size.height = size.height + requested.height; if (i != getChildCount()-1) size.height += mSpacing; } else { MyGUI::IntSize requested = getChildAt(i)->getSize (); size.width = std::max(size.width, requested.width); if (getChildAt(i)->getUserString("VStretch") != "true") size.height = size.height + requested.height; if (i != getChildCount()-1) size.height += mSpacing; } size.height += mPadding*2; size.width += mPadding*2; } return size; } void VBox::onWidgetCreated(MyGUI::Widget* _widget) { align(); } } openmw-openmw-0.38.0/components/widgets/box.hpp000066400000000000000000000062031264522266000215540ustar00rootroot00000000000000#ifndef OPENMW_WIDGETS_BOX_H #define OPENMW_WIDGETS_BOX_H #include #include #include #include namespace Gui { class AutoSizedWidget { public: AutoSizedWidget() : mExpandDirection(MyGUI::Align::Right) {} virtual MyGUI::IntSize getRequestedSize() = 0; protected: void notifySizeChange(MyGUI::Widget* w); MyGUI::Align mExpandDirection; }; class AutoSizedTextBox : public AutoSizedWidget, public MyGUI::TextBox { MYGUI_RTTI_DERIVED( AutoSizedTextBox ) public: virtual MyGUI::IntSize getRequestedSize(); virtual void setCaption(const MyGUI::UString& _value); protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); }; class AutoSizedEditBox : public AutoSizedWidget, public MyGUI::EditBox { MYGUI_RTTI_DERIVED( AutoSizedEditBox ) public: virtual MyGUI::IntSize getRequestedSize(); virtual void setCaption(const MyGUI::UString& _value); protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); }; class AutoSizedButton : public AutoSizedWidget, public MyGUI::Button { MYGUI_RTTI_DERIVED( AutoSizedButton ) public: virtual MyGUI::IntSize getRequestedSize(); virtual void setCaption(const MyGUI::UString& _value); protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); }; /** * @brief A container widget that automatically sizes its children * @note the box being an AutoSizedWidget as well allows to put boxes inside a box */ class Box : public AutoSizedWidget { public: Box(); void notifyChildrenSizeChanged(); protected: virtual void align() = 0; virtual bool _setPropertyImpl(const std::string& _key, const std::string& _value); int mSpacing; // how much space to put between elements int mPadding; // outer padding bool mAutoResize; // auto resize the box so that it exactly fits all elements }; class HBox : public Box, public MyGUI::Widget { MYGUI_RTTI_DERIVED( HBox ) public: virtual void setSize (const MyGUI::IntSize &_value); virtual void setCoord (const MyGUI::IntCoord &_value); protected: virtual void align(); virtual MyGUI::IntSize getRequestedSize(); virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void onWidgetCreated(MyGUI::Widget* _widget); }; class VBox : public Box, public MyGUI::Widget { MYGUI_RTTI_DERIVED( VBox) public: virtual void setSize (const MyGUI::IntSize &_value); virtual void setCoord (const MyGUI::IntCoord &_value); protected: virtual void align(); virtual MyGUI::IntSize getRequestedSize(); virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void onWidgetCreated(MyGUI::Widget* _widget); }; } #endif openmw-openmw-0.38.0/components/widgets/imagebutton.cpp000066400000000000000000000042771264522266000233060ustar00rootroot00000000000000#include "imagebutton.hpp" #include namespace Gui { void ImageButton::setPropertyOverride(const std::string &_key, const std::string &_value) { if (_key == "ImageHighlighted") mImageHighlighted = _value; else if (_key == "ImagePushed") mImagePushed = _value; else if (_key == "ImageNormal") { if (mImageNormal == "") { setImageTexture(_value); } mImageNormal = _value; } else ImageBox::setPropertyOverride(_key, _value); } void ImageButton::onMouseSetFocus(Widget* _old) { setImageTexture(mImageHighlighted); ImageBox::onMouseSetFocus(_old); } void ImageButton::onMouseLostFocus(Widget* _new) { setImageTexture(mImageNormal); ImageBox::onMouseLostFocus(_new); } void ImageButton::onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) { if (_id == MyGUI::MouseButton::Left) setImageTexture(mImagePushed); ImageBox::onMouseButtonPressed(_left, _top, _id); } MyGUI::IntSize ImageButton::getRequestedSize() { MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(mImageNormal); if (!texture) { std::cerr << "ImageButton: can't find " << mImageNormal << std::endl; return MyGUI::IntSize(0,0); } return MyGUI::IntSize (texture->getWidth(), texture->getHeight()); } void ImageButton::setImage(const std::string &image) { size_t extpos = image.find_last_of("."); std::string imageNoExt = image.substr(0, extpos); std::string ext = image.substr(extpos); mImageNormal = imageNoExt + "_idle" + ext; mImageHighlighted = imageNoExt + "_over" + ext; mImagePushed = imageNoExt + "_pressed" + ext; setImageTexture(mImageNormal); } void ImageButton::onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) { if (_id == MyGUI::MouseButton::Left) setImageTexture(mImageHighlighted); ImageBox::onMouseButtonReleased(_left, _top, _id); } } openmw-openmw-0.38.0/components/widgets/imagebutton.hpp000066400000000000000000000021351264522266000233020ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_WIDGETS_IMAGEBUTTON_H #define OPENMW_COMPONENTS_WIDGETS_IMAGEBUTTON_H #include namespace Gui { /** * @brief allows using different image textures depending on the button state */ class ImageButton : public MyGUI::ImageBox { MYGUI_RTTI_DERIVED(ImageButton) public: MyGUI::IntSize getRequestedSize(); /// Set mImageNormal, mImageHighlighted and mImagePushed based on file convention (image_idle.ext, image_over.ext and image_pressed.ext) void setImage(const std::string& image); protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void onMouseLostFocus(MyGUI::Widget* _new); virtual void onMouseSetFocus(MyGUI::Widget* _old); virtual void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id); virtual void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id); std::string mImageHighlighted; std::string mImageNormal; std::string mImagePushed; }; } #endif openmw-openmw-0.38.0/components/widgets/list.cpp000066400000000000000000000123331264522266000217330ustar00rootroot00000000000000#include "list.hpp" #include #include #include #include namespace Gui { MWList::MWList() : mScrollView(0) ,mClient(0) , mItemHeight(0) { } void MWList::initialiseOverride() { Base::initialiseOverride(); assignWidget(mClient, "Client"); if (mClient == 0) mClient = this; mScrollView = mClient->createWidgetReal( "MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0), MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); } void MWList::addItem(const std::string& name) { mItems.push_back(name); } void MWList::addSeparator() { mItems.push_back(""); } void MWList::adjustSize() { redraw(); } void MWList::redraw(bool scrollbarShown) { const int _scrollBarWidth = 20; // fetch this from skin? const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; const int spacing = 3; int viewPosition = -mScrollView->getViewOffset().top; while (mScrollView->getChildCount()) { MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); } mItemHeight = 0; int i=0; for (std::vector::const_iterator it=mItems.begin(); it!=mItems.end(); ++it) { if (*it != "") { if (mListItemSkin.empty()) return; MyGUI::Button* button = mScrollView->createWidget( mListItemSkin, MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24), MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it)); button->setCaption((*it)); button->getSubWidgetText()->setWordWrap(true); button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheelMoved); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); int height = button->getTextSize().height; button->setSize(MyGUI::IntSize(button->getSize().width, height)); button->setUserData(i); mItemHeight += height + spacing; } else { MyGUI::ImageBox* separator = mScrollView->createWidget("MW_HLine", MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth() - scrollBarWidth - 4, 18), MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); separator->setNeedMouseFocus(false); mItemHeight += 18 + spacing; } ++i; } // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mScrollView->setVisibleVScroll(false); mScrollView->setCanvasSize(mClient->getSize().width, std::max(mItemHeight, mClient->getSize().height)); mScrollView->setVisibleVScroll(true); if (!scrollbarShown && mItemHeight > mClient->getSize().height) redraw(true); int viewRange = mScrollView->getCanvasSize().height; if(viewPosition > viewRange) viewPosition = viewRange; mScrollView->setViewOffset(MyGUI::IntPoint(0, -viewPosition)); } void MWList::setPropertyOverride(const std::string &_key, const std::string &_value) { if (_key == "ListItemSkin") mListItemSkin = _value; else Base::setPropertyOverride(_key, _value); } unsigned int MWList::getItemCount() { return mItems.size(); } std::string MWList::getItemNameAt(unsigned int at) { assert(at < mItems.size() && "List item out of bounds"); return mItems[at]; } void MWList::removeItem(const std::string& name) { assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() ); mItems.erase( std::find(mItems.begin(), mItems.end(), name) ); } void MWList::clear() { mItems.clear(); } void MWList::onMouseWheelMoved(MyGUI::Widget* _sender, int _rel) { //NB view offset is negative if (mScrollView->getViewOffset().top + _rel*0.3f > 0) mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); else mScrollView->setViewOffset(MyGUI::IntPoint(0, static_cast(mScrollView->getViewOffset().top + _rel*0.3))); } void MWList::onItemSelected(MyGUI::Widget* _sender) { std::string name = _sender->castType()->getCaption(); int id = *_sender->getUserData(); eventItemSelected(name, id); eventWidgetSelected(_sender); } MyGUI::Button *MWList::getItemWidget(const std::string& name) { return mScrollView->findWidget (getName() + "_item_" + name)->castType(); } void MWList::scrollToTop() { mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); } } openmw-openmw-0.38.0/components/widgets/list.hpp000066400000000000000000000042601264522266000217400ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_WIDGETS_LIST_HPP #define OPENMW_COMPONENTS_WIDGETS_LIST_HPP #include namespace Gui { /** * \brief a very simple list widget that supports word-wrapping entries * \note if the width or height of the list changes, you must call adjustSize() method */ class MWList : public MyGUI::Widget { MYGUI_RTTI_DERIVED(MWList) public: MWList(); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_StringInt; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Widget; /** * Event: Item selected with the mouse. * signature: void method(std::string itemName, int index) */ EventHandle_StringInt eventItemSelected; /** * Event: Item selected with the mouse. * signature: void method(MyGUI::Widget* sender) */ EventHandle_Widget eventWidgetSelected; /** * Call after the size of the list changed, or items were inserted/removed */ void adjustSize(); void addItem(const std::string& name); void addSeparator(); ///< add a seperator between the current and the next item. void removeItem(const std::string& name); unsigned int getItemCount(); std::string getItemNameAt(unsigned int at); ///< \attention if there are separators, this method will return "" at the place where the separator is void clear(); MyGUI::Button* getItemWidget(const std::string& name); ///< get widget for an item name, useful to set up tooltip void scrollToTop(); virtual void setPropertyOverride(const std::string& _key, const std::string& _value); protected: void initialiseOverride(); void redraw(bool scrollbarShown = false); void onMouseWheelMoved(MyGUI::Widget* _sender, int _rel); void onItemSelected(MyGUI::Widget* _sender); private: MyGUI::ScrollView* mScrollView; MyGUI::Widget* mClient; std::string mListItemSkin; std::vector mItems; int mItemHeight; // height of all items }; } #endif openmw-openmw-0.38.0/components/widgets/numericeditbox.cpp000066400000000000000000000033021264522266000237750ustar00rootroot00000000000000#include "numericeditbox.hpp" #include namespace Gui { void NumericEditBox::initialiseOverride() { Base::initialiseOverride(); eventEditTextChange += MyGUI::newDelegate(this, &NumericEditBox::onEditTextChange); mValue = 0; setCaption("0"); } void NumericEditBox::shutdownOverride() { Base::shutdownOverride(); eventEditTextChange -= MyGUI::newDelegate(this, &NumericEditBox::onEditTextChange); } void NumericEditBox::onEditTextChange(MyGUI::EditBox *sender) { std::string newCaption = sender->getCaption(); if (newCaption.empty()) { return; } try { mValue = boost::lexical_cast(newCaption); int capped = std::min(mMaxValue, std::max(mValue, mMinValue)); if (capped != mValue) { mValue = capped; setCaption(MyGUI::utility::toString(mValue)); } } catch (boost::bad_lexical_cast&) { setCaption(MyGUI::utility::toString(mValue)); } eventValueChanged(mValue); } void NumericEditBox::setValue(int value) { if (value != mValue) { setCaption(MyGUI::utility::toString(value)); mValue = value; } } void NumericEditBox::setMinValue(int minValue) { mMinValue = minValue; } void NumericEditBox::setMaxValue(int maxValue) { mMaxValue = maxValue; } void NumericEditBox::onKeyLostFocus(MyGUI::Widget* _new) { Base::onKeyLostFocus(_new); setCaption(MyGUI::utility::toString(mValue)); } } openmw-openmw-0.38.0/components/widgets/numericeditbox.hpp000066400000000000000000000020311264522266000240000ustar00rootroot00000000000000#ifndef OPENMW_NUMERIC_EDIT_BOX_H #define OPENMW_NUMERIC_EDIT_BOX_H #include namespace Gui { /** * @brief A variant of the EditBox that only allows integer inputs */ class NumericEditBox : public MyGUI::EditBox { MYGUI_RTTI_DERIVED(NumericEditBox) public: NumericEditBox() : mValue(0), mMinValue(std::numeric_limits::min()), mMaxValue(std::numeric_limits::max()) {} void initialiseOverride(); void shutdownOverride(); typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ValueChanged; EventHandle_ValueChanged eventValueChanged; /// @note Does not trigger eventValueChanged void setValue (int value); void setMinValue(int minValue); void setMaxValue(int maxValue); private: void onEditTextChange(MyGUI::EditBox* sender); void onKeyLostFocus(MyGUI::Widget* _new); int mValue; int mMinValue; int mMaxValue; }; } #endif openmw-openmw-0.38.0/components/widgets/sharedstatebutton.cpp000066400000000000000000000064731264522266000245330ustar00rootroot00000000000000#include "sharedstatebutton.hpp" namespace Gui { SharedStateButton::SharedStateButton() : mIsMousePressed(false) , mIsMouseFocus(false) { } void SharedStateButton::shutdownOverride() { ButtonGroup group = mSharedWith; // make a copy so that we don't nuke the vector during iteration for (ButtonGroup::iterator it = group.begin(); it != group.end(); ++it) { (*it)->shareStateWith(ButtonGroup()); } } void SharedStateButton::shareStateWith(ButtonGroup shared) { mSharedWith = shared; } void SharedStateButton::onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) { mIsMousePressed = true; Base::onMouseButtonPressed(_left, _top, _id); updateButtonState(); } void SharedStateButton::onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) { mIsMousePressed = false; Base::onMouseButtonReleased(_left, _top, _id); updateButtonState(); } void SharedStateButton::onMouseSetFocus(MyGUI::Widget *_old) { mIsMouseFocus = true; Base::onMouseSetFocus(_old); updateButtonState(); } void SharedStateButton::onMouseLostFocus(MyGUI::Widget *_new) { mIsMouseFocus = false; Base::onMouseLostFocus(_new); updateButtonState(); } void SharedStateButton::baseUpdateEnable() { Base::baseUpdateEnable(); updateButtonState(); } void SharedStateButton::setStateSelected(bool _value) { Base::setStateSelected(_value); updateButtonState(); for (ButtonGroup::iterator it = mSharedWith.begin(); it != mSharedWith.end(); ++it) { (*it)->MyGUI::Button::setStateSelected(getStateSelected()); } } bool SharedStateButton::_setState(const std::string &_value) { bool ret = _setWidgetState(_value); if (ret) { for (ButtonGroup::iterator it = mSharedWith.begin(); it != mSharedWith.end(); ++it) { (*it)->_setWidgetState(_value); } } return ret; } void SharedStateButton::updateButtonState() { if (getStateSelected()) { if (!getInheritedEnabled()) { if (!_setState("disabled_checked")) _setState("disabled"); } else if (mIsMousePressed) { if (!_setState("pushed_checked")) _setState("pushed"); } else if (mIsMouseFocus) { if (!_setState("highlighted_checked")) _setState("pushed"); } else _setState("normal_checked"); } else { if (!getInheritedEnabled()) _setState("disabled"); else if (mIsMousePressed) _setState("pushed"); else if (mIsMouseFocus) _setState("highlighted"); else _setState("normal"); } } void SharedStateButton::createButtonGroup(ButtonGroup group) { for (ButtonGroup::iterator it = group.begin(); it != group.end(); ++it) { (*it)->shareStateWith(group); } } } openmw-openmw-0.38.0/components/widgets/sharedstatebutton.hpp000066400000000000000000000026011264522266000245250ustar00rootroot00000000000000#ifndef OPENMW_WIDGETS_SHAREDSTATEBUTTON_HPP #define OPENMW_WIDGETS_SHAREDSTATEBUTTON_HPP #include namespace Gui { class SharedStateButton; typedef std::vector ButtonGroup; /// @brief A button that applies its own state changes to other widgets, to do this you define it as part of a ButtonGroup. class SharedStateButton : public MyGUI::Button { MYGUI_RTTI_DERIVED(SharedStateButton) public: SharedStateButton(); protected: void updateButtonState(); virtual void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id); virtual void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id); virtual void onMouseSetFocus(MyGUI::Widget* _old); virtual void onMouseLostFocus(MyGUI::Widget* _new); virtual void baseUpdateEnable(); virtual void shutdownOverride(); bool _setState(const std::string &_value); public: void shareStateWith(ButtonGroup shared); /// @note The ButtonGroup connection will be destroyed when any widget in the group gets destroyed. static void createButtonGroup(ButtonGroup group); //! Set button selected state void setStateSelected(bool _value); private: ButtonGroup mSharedWith; bool mIsMousePressed; bool mIsMouseFocus; }; } #endif openmw-openmw-0.38.0/components/widgets/tags.cpp000066400000000000000000000040511264522266000217140ustar00rootroot00000000000000#include "tags.hpp" #include namespace Gui { bool replaceTag(const MyGUI::UString& tag, MyGUI::UString& out, const std::map& fallbackSettings) { std::string fontcolour = "fontcolour="; size_t fontcolourLength = fontcolour.length(); std::string fontcolourhtml = "fontcolourhtml="; size_t fontcolourhtmlLength = fontcolourhtml.length(); if (tag.compare(0, fontcolourLength, fontcolour) == 0) { std::string fallbackName = "FontColor_color_" + tag.substr(fontcolourLength); std::map::const_iterator it = fallbackSettings.find(fallbackName); if (it == fallbackSettings.end()) throw std::runtime_error("Unknown fallback name: " + fallbackName); std::string str = it->second; std::string ret[3]; unsigned int j=0; for(unsigned int i=0;i::const_iterator it = fallbackSettings.find(fallbackName); if (it == fallbackSettings.end()) throw std::runtime_error("Unknown fallback name: " + fallbackName); std::string str = it->second; std::string ret[3]; unsigned int j=0; for(unsigned int i=0;i #include #include namespace Gui { /// Try to replace a tag. Returns true on success and writes the result to \a out. bool replaceTag (const MyGUI::UString& tag, MyGUI::UString& out, const std::map& fallbackSettings); } #endif openmw-openmw-0.38.0/components/widgets/widgets.cpp000066400000000000000000000022421264522266000224240ustar00rootroot00000000000000#include "widgets.hpp" #include #include "list.hpp" #include "numericeditbox.hpp" #include "box.hpp" #include "imagebutton.hpp" #include "sharedstatebutton.hpp" #include "windowcaption.hpp" namespace Gui { void registerAllWidgets() { MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } } openmw-openmw-0.38.0/components/widgets/widgets.hpp000066400000000000000000000003231264522266000224270ustar00rootroot00000000000000#ifndef OPENMW_COMPONENTS_WIDGETS_H #define OPENMW_COMPONENTS_WIDGETS_H namespace Gui { /// Register all widgets from this component with MyGUI's factory manager. void registerAllWidgets(); } #endif openmw-openmw-0.38.0/components/widgets/windowcaption.cpp000066400000000000000000000026271264522266000236520ustar00rootroot00000000000000#include "windowcaption.hpp" #include namespace Gui { WindowCaption::WindowCaption() : mLeft(NULL) , mRight(NULL) { } void WindowCaption::initialiseOverride() { Base::initialiseOverride(); assignWidget(mLeft, "Left"); assignWidget(mRight, "Right"); assignWidget(mClient, "Client"); if (!mClient) throw std::runtime_error("WindowCaption needs an EditBox Client widget in its skin"); } void WindowCaption::setCaption(const MyGUI::UString &_value) { EditBox::setCaption(_value); align(); } void WindowCaption::setSize(const MyGUI::IntSize& _value) { Base::setSize(_value); align(); } void WindowCaption::setCoord(const MyGUI::IntCoord& _value) { Base::setCoord(_value); align(); } void WindowCaption::align() { MyGUI::IntSize textSize = getTextSize(); MyGUI::Widget* caption = mClient; caption->setSize(textSize.width + 24, caption->getHeight()); int barwidth = (getWidth()-caption->getWidth())/2; caption->setPosition(barwidth, caption->getTop()); if (mLeft) mLeft->setCoord(0, mLeft->getTop(), barwidth, mLeft->getHeight()); if (mRight) mRight->setCoord(barwidth + caption->getWidth(), mRight->getTop(), barwidth, mRight->getHeight()); } } openmw-openmw-0.38.0/components/widgets/windowcaption.hpp000066400000000000000000000013601264522266000236500ustar00rootroot00000000000000#ifndef OPENMW_WIDGETS_WINDOWCAPTION_H #define OPENMW_WIDGETS_WINDOWCAPTION_H #include namespace Gui { /// Window caption that automatically adjusts "Left" and "Right" widgets in its skin /// based on the text size of the caption in the middle class WindowCaption : public MyGUI::EditBox { MYGUI_RTTI_DERIVED(WindowCaption) public: WindowCaption(); virtual void setCaption(const MyGUI::UString &_value); virtual void initialiseOverride(); virtual void setSize(const MyGUI::IntSize& _value); virtual void setCoord(const MyGUI::IntCoord& _value); private: MyGUI::Widget* mLeft; MyGUI::Widget* mRight; void align(); }; } #endif openmw-openmw-0.38.0/docs/000077500000000000000000000000001264522266000153475ustar00rootroot00000000000000openmw-openmw-0.38.0/docs/Doxyfile.cmake000066400000000000000000003077101264522266000201440ustar00rootroot00000000000000# Doxyfile 1.8.7 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single 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 #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = OpenMW # 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 = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. 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 = @OpenMW_BINARY_DIR@/docs/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 causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = 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. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES 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. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES 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. # The default value is: YES. 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 and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # 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. # The default value is: NO. 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. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES 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 # The default value is: YES. FULL_PATH_NAMES = YES # 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. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. 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 list of 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. # The default value is: NO. 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-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # 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 behavior. 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 behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # 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. # The default value is: NO. 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. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act 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 = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # 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. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = 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. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES 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. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # 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 respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # 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. Does not have any effect # for Java sources. # The default value is: YES. 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 only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = YES # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO 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. # The default value is: NO. 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 these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. 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 these declarations will be # included in the documentation. # The default value is: NO. 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 these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. 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 then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. 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. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES 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. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = 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 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. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = 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. # The default value is: YES. 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. # The default value is: YES. 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. # The default value is: YES. 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. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have 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 value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. 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. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = 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 program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. Do not use file names with spaces, bibtex cannot handle them. See # also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag 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. # The default value is: YES. WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag 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. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. 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) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is 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. # Note: If this tag is empty the current directory is searched. INPUT = @OpenMW_SOURCE_DIR@/apps \ @OpenMW_SOURCE_DIR@/components \ @OpenMW_SOURCE_DIR@/libs \ @OpenMW_BINARY_DIR@/docs/mainpage.hpp # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (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, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.vhd \ *.vhdl # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # 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. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. 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. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # 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. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be 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: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. 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 information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # 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 that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = 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. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES 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. # See also: Section \class. # The default value is: YES. 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. # The default value is: YES. ALPHABETICAL_INDEX = NO # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. 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 a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. 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. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. 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). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. 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 left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "OpenMW Documentation" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.openmw # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.openmw # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = OpenMW # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # 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. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # 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. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # 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). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # 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. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.openmw # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.openmw # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value 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 # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # 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. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = YES # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /